Expose locations of `_ViewImports.cshtml` that affect a given Razor file.

- Added `ChunkTreeResult` to associate inherited chunks with a specific source file.
- Updated existing tests to validate file path.

#2256
This commit is contained in:
N. Taylor Mullen 2015-08-18 19:39:28 -07:00
parent 91c0081939
commit b871172dd0
6 changed files with 90 additions and 32 deletions

View File

@ -40,17 +40,18 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
}
/// <summary>
/// Gets an ordered <see cref="IReadOnlyList{T}"/> of parsed <see cref="ChunkTree"/> for each
/// <c>_ViewImports</c> that is applicable to the page located at <paramref name="pagePath"/>. The list is
/// ordered so that the <see cref="ChunkTree"/> for the <c>_ViewImports</c> closest to the
/// Gets an ordered <see cref="IReadOnlyList{ChunkTreeResult}"/> of parsed <see cref="ChunkTree"/>s and
/// file paths for each <c>_ViewImports</c> that is applicable to the page located at
/// <paramref name="pagePath"/>. The list is ordered so that the <see cref="ChunkTreeResult"/>'s
/// <see cref="ChunkTreeResult.ChunkTree"/> for the <c>_ViewImports</c> closest to the
/// <paramref name="pagePath"/> in the file system appears first.
/// </summary>
/// <param name="pagePath">The path of the page to locate inherited chunks for.</param>
/// <returns>A <see cref="IReadOnlyList{ChunkTree}"/> of parsed <c>_ViewImports</c>
/// <see cref="ChunkTree"/>s.</returns>
public virtual IReadOnlyList<ChunkTree> GetInheritedChunkTrees([NotNull] string pagePath)
/// <returns>A <see cref="IReadOnlyList{ChunkTreeResult}"/> of parsed <c>_ViewImports</c>
/// <see cref="ChunkTree"/>s and their file paths.</returns>
public virtual IReadOnlyList<ChunkTreeResult> GetInheritedChunkTreeResults([NotNull] string pagePath)
{
var inheritedChunkTrees = new List<ChunkTree>();
var inheritedChunkTreeResults = new List<ChunkTreeResult>();
var templateEngine = new RazorTemplateEngine(_razorHost);
foreach (var viewImportsPath in ViewHierarchyUtility.GetViewImportsLocations(pagePath))
{
@ -67,11 +68,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
if (chunkTree != null)
{
inheritedChunkTrees.Add(chunkTree);
var result = new ChunkTreeResult(chunkTree, viewImportsPath);
inheritedChunkTreeResults.Add(result);
}
}
return inheritedChunkTrees;
return inheritedChunkTreeResults;
}
/// <summary>

View File

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Chunks;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor.Directives
{
/// <summary>
/// Contains <see cref="AspNet.Razor.Chunks.ChunkTree"/> information.
/// </summary>
public class ChunkTreeResult
{
/// <summary>
/// Initializes a new instance of <see cref="ChunkTreeResult"/>.
/// </summary>
/// <param name="chunkTree">The <see cref="AspNet.Razor.Chunks.ChunkTree"/> generated from the file at the
/// given <paramref name="filePath"/>.</param>
/// <param name="filePath">The path to the file that generated the given <paramref name="chunkTree"/>.</param>
public ChunkTreeResult([NotNull] ChunkTree chunkTree, [NotNull] string filePath)
{
ChunkTree = chunkTree;
FilePath = filePath;
}
/// <summary>
/// The <see cref="AspNet.Razor.Chunks.ChunkTree"/> generated from the file at <see cref="FilePath"/>.
/// </summary>
public ChunkTree ChunkTree { get; }
/// <summary>
/// The path to the file that generated the <see cref="ChunkTree"/>.
/// </summary>
public string FilePath { get; }
}
}

View File

@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
ChunkTree chunkTree;
if (!_chunkTreeCache.TryGetValue(pagePath, out chunkTree))
{
// GetOrAdd is invoked for each _GlobalImport that might potentially exist in the path.
// GetOrAdd is invoked for each _ViewImport that might potentially exist in the path.
// We can avoid performing file system lookups for files that do not exist by caching
// negative results and adding a Watch for that file.
@ -58,7 +58,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
var file = _fileProvider.GetFileInfo(pagePath);
chunkTree = file.Exists ? getChunkTree(file) : null;
_chunkTreeCache.Set(pagePath, chunkTree, options);
}

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET45
using Microsoft.AspNet.FileProviders;
#endif
@ -224,6 +225,21 @@ namespace Microsoft.AspNet.Mvc.Razor
}
}
/// <summary>
/// Locates and parses _ViewImports.cshtml files applying to the given <paramref name="sourceFileName"/> to
/// create <see cref="ChunkTreeResult"/>s.
/// </summary>
/// <param name="sourceFileName">The path to a Razor file to locate _ViewImports.cshtml for.</param>
/// <returns>Inherited <see cref="ChunkTreeResult"/>s.</returns>
public IReadOnlyList<ChunkTreeResult> GetInheritedChunkTreeResults([NotNull] string sourceFileName)
{
// Need the normalized path to resolve inherited chunks only. Full paths are needed for generated Razor
// files checksum and line pragmas to enable DesignTime debugging.
var normalizedPath = _pathNormalizer.NormalizePath(sourceFileName);
return ChunkInheritanceUtility.GetInheritedChunkTreeResults(normalizedPath);
}
/// <inheritdoc />
public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream)
{
@ -236,9 +252,8 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <inheritdoc />
public override RazorParser DecorateRazorParser([NotNull] RazorParser razorParser, string sourceFileName)
{
sourceFileName = _pathNormalizer.NormalizePath(sourceFileName);
var inheritedChunkTrees = GetInheritedChunkTrees(sourceFileName);
var inheritedChunkTrees = ChunkInheritanceUtility.GetInheritedChunkTrees(sourceFileName);
return new MvcRazorParser(razorParser, inheritedChunkTrees, DefaultInheritedChunks, ModelExpressionType);
}
@ -253,14 +268,11 @@ namespace Microsoft.AspNet.Mvc.Razor
[NotNull] CodeGenerator incomingGenerator,
[NotNull] CodeGeneratorContext context)
{
// Need the normalized path to resolve inherited chunks only. Full paths are needed for generated Razor
// files checksum and line pragmas to enable DesignTime debugging.
var normalizedPath = _pathNormalizer.NormalizePath(context.SourceFile);
var inheritedChunks = ChunkInheritanceUtility.GetInheritedChunkTrees(normalizedPath);
var inheritedChunkTrees = GetInheritedChunkTrees(context.SourceFile);
ChunkInheritanceUtility.MergeInheritedChunkTrees(
context.ChunkTreeBuilder.ChunkTree,
inheritedChunks,
inheritedChunkTrees,
DefaultModel);
return new MvcCSharpCodeGenerator(
@ -273,5 +285,14 @@ namespace Microsoft.AspNet.Mvc.Razor
CreateModelExpressionMethodName = CreateModelExpressionMethod
});
}
private IReadOnlyList<ChunkTree> GetInheritedChunkTrees(string sourceFileName)
{
var inheritedChunkTrees = GetInheritedChunkTreeResults(sourceFileName)
.Select(result => result.ChunkTree)
.ToList();
return inheritedChunkTrees;
}
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Chunks;
using Microsoft.AspNet.Testing;
using Xunit;
namespace Microsoft.AspNet.Mvc.Razor.Directives
@ -36,14 +35,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks);
// Act
var chunkTrees = utility.GetInheritedChunkTrees(PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml"));
var chunkTreeResults = utility.GetInheritedChunkTreeResults(
PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml"));
// Assert
Assert.Collection(chunkTrees,
chunkTree =>
Assert.Collection(chunkTreeResults,
chunkTreeResult =>
{
var viewImportsPath = PlatformNormalizer.NormalizePath(@"Views\home\_ViewImports.cshtml");
Assert.Collection(chunkTree.Chunks,
Assert.Collection(chunkTreeResult.ChunkTree.Chunks,
chunk =>
{
Assert.IsType<LiteralChunk>(chunk);
@ -60,11 +60,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath);
});
Assert.Equal(viewImportsPath, chunkTreeResult.FilePath);
},
chunkTree =>
chunkTreeResult =>
{
var viewImportsPath = PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml");
Assert.Collection(chunkTree.Chunks,
Assert.Collection(chunkTreeResult.ChunkTree.Chunks,
chunk =>
{
Assert.IsType<LiteralChunk>(chunk);
@ -104,6 +105,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath);
});
Assert.Equal(viewImportsPath, chunkTreeResult.FilePath);
});
}
@ -125,7 +127,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks);
// Act
var chunkTrees = utility.GetInheritedChunkTrees(PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml"));
var chunkTrees = utility.GetInheritedChunkTreeResults(PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml"));
// Assert
Assert.Empty(chunkTrees);
@ -168,9 +170,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
var chunkTree = new ChunkTree();
// Act
utility.MergeInheritedChunkTrees(chunkTree,
inheritedChunkTrees,
"dynamic");
utility.MergeInheritedChunkTrees(chunkTree, inheritedChunkTrees, "dynamic");
// Assert
Assert.Equal(3, chunkTree.Chunks.Count);

View File

@ -486,11 +486,11 @@ namespace Microsoft.AspNet.Mvc.Razor
public string InheritedChunkTreePagePath { get; private set; }
public override IReadOnlyList<ChunkTree> GetInheritedChunkTrees([NotNull] string pagePath)
public override IReadOnlyList<ChunkTreeResult> GetInheritedChunkTreeResults([NotNull] string pagePath)
{
InheritedChunkTreePagePath = pagePath;
return new ChunkTree[0];
return new ChunkTreeResult[0];
}
}
@ -533,7 +533,7 @@ namespace Microsoft.AspNet.Mvc.Razor
protected override CSharpCodeWriter CreateCodeWriter()
{
// We normalize newlines so no matter what platform we're on
// We normalize newlines so no matter what platform we're on
// they're consistent (for code generation tests).
var codeWriter = base.CreateCodeWriter();
codeWriter.NewLine = "\r\n";