File: FileProviderRazorProjectFileSystem.cs
Web Access
Project: src\src\Mvc\Mvc.Razor.RuntimeCompilation\src\Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.csproj (Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.FileProviders;
 
namespace Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
 
internal sealed class FileProviderRazorProjectFileSystem : RazorProjectFileSystem
{
    private const string RazorFileExtension = ".cshtml";
    private readonly RuntimeCompilationFileProvider _fileProvider;
    private readonly IWebHostEnvironment _hostingEnvironment;
 
    public FileProviderRazorProjectFileSystem(RuntimeCompilationFileProvider fileProvider, IWebHostEnvironment hostingEnvironment)
    {
        ArgumentNullException.ThrowIfNull(fileProvider);
        ArgumentNullException.ThrowIfNull(hostingEnvironment);
 
        _fileProvider = fileProvider;
        _hostingEnvironment = hostingEnvironment;
    }
 
    public IFileProvider FileProvider => _fileProvider.FileProvider;
 
    public override RazorProjectItem GetItem(string path)
    {
        return GetItem(path, fileKind: null);
    }
 
    public override RazorProjectItem GetItem(string path, string? fileKind)
    {
        path = NormalizeAndEnsureValidPath(path);
        var fileInfo = FileProvider.GetFileInfo(path);
 
        return new FileProviderRazorProjectItem(fileInfo, basePath: string.Empty, filePath: path, root: _hostingEnvironment.ContentRootPath, fileKind);
    }
 
    public override IEnumerable<RazorProjectItem> EnumerateItems(string path)
    {
        path = NormalizeAndEnsureValidPath(path);
        return EnumerateFiles(FileProvider.GetDirectoryContents(path), path, prefix: string.Empty);
    }
 
    private IEnumerable<RazorProjectItem> EnumerateFiles(IDirectoryContents directory, string basePath, string prefix)
    {
        if (directory.Exists)
        {
            foreach (var fileInfo in directory)
            {
                if (fileInfo.IsDirectory)
                {
                    var relativePath = prefix + "/" + fileInfo.Name;
                    var subDirectory = FileProvider.GetDirectoryContents(JoinPath(basePath, relativePath));
                    var children = EnumerateFiles(subDirectory, basePath, relativePath);
                    foreach (var child in children)
                    {
                        yield return child;
                    }
                }
                else if (string.Equals(RazorFileExtension, Path.GetExtension(fileInfo.Name), StringComparison.OrdinalIgnoreCase))
                {
                    var filePath = prefix + "/" + fileInfo.Name;
 
                    yield return new FileProviderRazorProjectItem(fileInfo, basePath, filePath: filePath, root: _hostingEnvironment.ContentRootPath);
                }
            }
        }
    }
 
    private static string JoinPath(string path1, string path2)
    {
        var hasTrailingSlash = path1.EndsWith('/');
        var hasLeadingSlash = path2.StartsWith('/');
        if (hasLeadingSlash && hasTrailingSlash)
        {
            return string.Concat(path1, path2.AsSpan(1));
        }
        else if (hasLeadingSlash || hasTrailingSlash)
        {
            return path1 + path2;
        }
 
        return path1 + "/" + path2;
    }
}