|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
internal sealed class PageActionDescriptorChangeProvider : IActionDescriptorChangeProvider
{
private readonly RuntimeCompilationFileProvider _fileProvider;
private readonly string[] _searchPatterns;
private readonly string[] _additionalFilesToTrack;
public PageActionDescriptorChangeProvider(
RazorProjectEngine projectEngine,
RuntimeCompilationFileProvider fileProvider,
IOptions<RazorPagesOptions> razorPagesOptions)
{
ArgumentNullException.ThrowIfNull(projectEngine);
ArgumentNullException.ThrowIfNull(fileProvider);
ArgumentNullException.ThrowIfNull(razorPagesOptions);
_fileProvider = fileProvider;
var rootDirectory = razorPagesOptions.Value.RootDirectory;
Debug.Assert(!string.IsNullOrEmpty(rootDirectory));
rootDirectory = rootDirectory.TrimEnd('/');
// Search pattern that matches all cshtml files under the Pages RootDirectory
var pagesRootSearchPattern = rootDirectory + "/**/*.cshtml";
// Search pattern that matches all cshtml files under the Pages AreaRootDirectory
var areaRootSearchPattern = "/Areas/**/*.cshtml";
_searchPatterns = new[]
{
pagesRootSearchPattern,
areaRootSearchPattern
};
// pagesRootSearchPattern will miss _ViewImports outside the RootDirectory despite these influencing
// compilation. e.g. when RootDirectory = /Dir1/Dir2, the search pattern will ignore changes to
// [/_ViewImports.cshtml, /Dir1/_ViewImports.cshtml]. We need to additionally account for these.
var importFeatures = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().ToArray();
var fileAtPagesRoot = projectEngine.FileSystem.GetItem(rootDirectory + "/Index.cshtml", fileKind: null);
_additionalFilesToTrack = GetImports(importFeatures, fileAtPagesRoot);
}
public IChangeToken GetChangeToken()
{
var fileProvider = _fileProvider.FileProvider;
var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + _searchPatterns.Length];
for (var i = 0; i < _additionalFilesToTrack.Length; i++)
{
changeTokens[i] = fileProvider.Watch(_additionalFilesToTrack[i]);
}
for (var i = 0; i < _searchPatterns.Length; i++)
{
var wildcardChangeToken = fileProvider.Watch(_searchPatterns[i]);
changeTokens[_additionalFilesToTrack.Length + i] = wildcardChangeToken;
}
return new CompositeChangeToken(changeTokens);
}
private static string[] GetImports(
IImportProjectFeature[] importFeatures,
RazorProjectItem file)
{
return importFeatures
.SelectMany(f => f.GetImports(file))
.Where(f => f.FilePath != null)
.Select(f => f.FilePath)
.ToArray();
}
}
|