Flow FileKind from project items -> code document

This is a bit of a rework of how we initially set this up, but with more
forethought to how this will work in the project system. I have not yet
surfaced this through VS.

My immediate next step is to light up the component integration tests
and something like this is on the critical path to get that work since
we need a way to specify in tests that a document should be treated as
a component.
\n\nCommit migrated from 6b81da3f02
This commit is contained in:
Ryan Nowak 2018-12-18 17:58:02 -08:00
parent ceb1189b2e
commit c37dada0dc
10 changed files with 95 additions and 63 deletions

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
{
return codeDocument.GetInputDocumentKind() == InputDocumentKind.Component;
return string.Equals(codeDocument.GetFileKind(), FileKinds.Component);
}
protected override void OnDocumentStructureCreated(RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method)

View File

@ -68,10 +68,10 @@ namespace Microsoft.AspNetCore.Razor.Language
var importItems = importFeature.GetImports(projectItem);
var importSourceDocuments = GetImportSourceDocuments(importItems);
return CreateCodeDocumentCore(sourceDocument, importSourceDocuments, tagHelpers: null);
return CreateCodeDocumentCore(sourceDocument, projectItem.FileKind, importSourceDocuments, tagHelpers: null);
}
internal override RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument sourceDocument, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
internal override RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument sourceDocument, string fileKind, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
if (sourceDocument == null)
{
@ -84,6 +84,11 @@ namespace Microsoft.AspNetCore.Razor.Language
var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
codeDocument.SetTagHelpers(tagHelpers);
if (fileKind != null)
{
codeDocument.SetFileKind(fileKind);
}
return codeDocument;
}
@ -100,10 +105,10 @@ namespace Microsoft.AspNetCore.Razor.Language
var importItems = importFeature.GetImports(projectItem);
var importSourceDocuments = GetImportSourceDocuments(importItems, suppressExceptions: true);
return CreateCodeDocumentDesignTimeCore(sourceDocument, importSourceDocuments, tagHelpers: null);
return CreateCodeDocumentDesignTimeCore(sourceDocument, projectItem.FileKind, importSourceDocuments, tagHelpers: null);
}
internal override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument sourceDocument, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
internal override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument sourceDocument, string fileKind, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
if (sourceDocument == null)
{
@ -116,6 +121,11 @@ namespace Microsoft.AspNetCore.Razor.Language
var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
codeDocument.SetTagHelpers(tagHelpers);
if (fileKind != null)
{
codeDocument.SetFileKind(fileKind);
}
return codeDocument;
}

View File

@ -34,12 +34,13 @@ namespace Microsoft.AspNetCore.Razor.Language
return directory
.EnumerateFiles("*.cshtml", SearchOption.AllDirectories)
.Concat(directory.EnumerateFiles("*.razor", SearchOption.AllDirectories))
.Select(file =>
{
var relativePhysicalPath = file.FullName.Substring(absoluteBasePath.Length + 1); // Include leading separator
var filePath = "/" + relativePhysicalPath.Replace(Path.DirectorySeparatorChar, '/');
return new DefaultRazorProjectItem(basePath, filePath, relativePhysicalPath, file);
return new DefaultRazorProjectItem(basePath, filePath, relativePhysicalPath, fileKind: null, file);
});
}
@ -57,7 +58,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var relativePhysicalPath = file.FullName.Substring(absoluteBasePath.Length + 1); // Include leading separator
var filePath = "/" + relativePhysicalPath.Replace(Path.DirectorySeparatorChar, '/');
return new DefaultRazorProjectItem("/", filePath, relativePhysicalPath, new FileInfo(absolutePath));
return new DefaultRazorProjectItem("/", filePath, relativePhysicalPath, fileKind: null, new FileInfo(absolutePath));
}
protected override string NormalizeAndEnsureValidPath(string path)

View File

@ -7,18 +7,22 @@ namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultRazorProjectItem : RazorProjectItem
{
private readonly string _fileKind;
/// <summary>
/// Initializes a new instance of <see cref="DefaultRazorProjectItem"/>.
/// </summary>
/// <param name="basePath">The base path.</param>
/// <param name="relativePhysicalPath">The physical path of the base path.</param>
/// <param name="filePath">The path.</param>
/// <param name="fileKind">The file kind. If null, the document kind will be inferred from the file extension.</param>
/// <param name="file">The <see cref="FileInfo"/>.</param>
public DefaultRazorProjectItem(string basePath, string filePath, string relativePhysicalPath, FileInfo file)
public DefaultRazorProjectItem(string basePath, string filePath, string relativePhysicalPath, string fileKind, FileInfo file)
{
BasePath = basePath;
FilePath = filePath;
RelativePhysicalPath = relativePhysicalPath;
_fileKind = fileKind;
File = file;
}
@ -34,6 +38,8 @@ namespace Microsoft.AspNetCore.Razor.Language
public override string RelativePhysicalPath { get; }
public override string FileKind => _fileKind ?? base.FileKind;
public override Stream Read() => new FileStream(PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
}
}

View File

@ -1,12 +1,12 @@
// 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.
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
namespace Microsoft.AspNetCore.Razor.Language
{
internal static class InputDocumentKind
internal static class FileKinds
{
public static readonly string Component = "component";
public static readonly string MvcFile = "mvc";
public static readonly string Legacy = "mvc";
}
}

View File

@ -169,24 +169,24 @@ namespace Microsoft.AspNetCore.Razor.Language
document.Items[typeof(RazorCodeGenerationOptions)] = codeGenerationOptions;
}
public static string GetInputDocumentKind(this RazorCodeDocument document)
public static string GetFileKind(this RazorCodeDocument document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return (string)document.Items[typeof(InputDocumentKind)];
return (string)document.Items[typeof(FileKinds)];
}
public static void SetInputDocumentKind(this RazorCodeDocument document, string kind)
public static void SetFileKind(this RazorCodeDocument document, string fileKind)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
document.Items[typeof(InputDocumentKind)] = kind;
document.Items[typeof(FileKinds)] = fileKind;
}
private class ImportSyntaxTreesHolder

View File

@ -35,14 +35,14 @@ namespace Microsoft.AspNetCore.Razor.Language
return codeDocument;
}
internal virtual RazorCodeDocument Process(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
internal virtual RazorCodeDocument Process(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var codeDocument = CreateCodeDocumentCore(source, importSources, tagHelpers);
var codeDocument = CreateCodeDocumentCore(source, fileKind, importSources, tagHelpers);
ProcessCore(codeDocument);
return codeDocument;
}
@ -59,30 +59,40 @@ namespace Microsoft.AspNetCore.Razor.Language
return codeDocument;
}
internal virtual RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
internal virtual RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var codeDocument = CreateCodeDocumentDesignTimeCore(source, importSources, tagHelpers);
var codeDocument = CreateCodeDocumentDesignTimeCore(source, fileKind, importSources, tagHelpers);
ProcessCore(codeDocument);
return codeDocument;
}
protected abstract RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem);
internal virtual RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
internal virtual RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
return RazorCodeDocument.Create(source, importSources);
var codeDocument = RazorCodeDocument.Create(source, importSources);
if (fileKind != null)
{
codeDocument.SetFileKind(fileKind);
}
return codeDocument;
}
protected abstract RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem);
internal virtual RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument source, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
internal virtual RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
return RazorCodeDocument.Create(source, importSources);
var codeDocument = RazorCodeDocument.Create(source, importSources);
if (fileKind != null)
{
codeDocument.SetFileKind(fileKind);
}
return codeDocument;
}
protected abstract void ProcessCore(RazorCodeDocument codeDocument);

View File

@ -1,8 +1,11 @@
// 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 System;
using System.Diagnostics;
using System.IO;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
namespace Microsoft.AspNetCore.Razor.Language
{
@ -35,6 +38,28 @@ namespace Microsoft.AspNetCore.Razor.Language
/// </summary>
public virtual string RelativePhysicalPath => null;
/// <summary>
/// Gets the document kind that should be used for the generated document. If possible this will be inferred from the file path. May be null.
/// </summary>
public virtual string FileKind
{
get
{
if (FilePath == null)
{
return null;
}
else if (string.Equals(".razor", Path.GetExtension(FilePath), StringComparison.OrdinalIgnoreCase))
{
return FileKinds.Component;
}
else
{
return FileKinds.Legacy;
}
}
}
/// <summary>
/// Gets the file contents as readonly <see cref="Stream"/>.
/// </summary>

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
Sources = Option("-s", ".cshtml files to compile", CommandOptionType.MultipleValue);
Outputs = Option("-o", "Generated output file path", CommandOptionType.MultipleValue);
RelativePaths = Option("-r", "Relative path", CommandOptionType.MultipleValue);
DocumentKinds = Option("-k", "Document kind", CommandOptionType.MultipleValue);
FileKinds = Option("-k", "File kind", CommandOptionType.MultipleValue);
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
TagHelperManifest = Option("-t", "tag helper manifest file", CommandOptionType.SingleValue);
Version = Option("-v|--version", "Razor language version", CommandOptionType.SingleValue);
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
public CommandOption RelativePaths { get; }
public CommandOption DocumentKinds { get; }
public CommandOption FileKinds { get; }
public CommandOption ProjectDirectory { get; }
@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
var version = RazorLanguageVersion.Parse(Version.Value());
var configuration = RazorConfiguration.Create(version, Configuration.Value(), extensions);
var sourceItems = GetSourceItems(ProjectDirectory.Value(), Sources.Values, Outputs.Values, RelativePaths.Values, DocumentKinds.Values);
var sourceItems = GetSourceItems(ProjectDirectory.Value(), Sources.Values, Outputs.Values, RelativePaths.Values, FileKinds.Values);
var result = ExecuteCore(
configuration: configuration,
@ -105,11 +105,11 @@ namespace Microsoft.AspNetCore.Razor.Tools
return false;
}
if (DocumentKinds.Values.Count != 0 && DocumentKinds.Values.Count != Sources.Values.Count)
if (FileKinds.Values.Count != 0 && FileKinds.Values.Count != Sources.Values.Count)
{
// 2.x tasks do not specify DocumentKinds - in which case, no values will be present. If a kind for one document is specified, we expect as many kind entries
// 2.x tasks do not specify FileKinds - in which case, no values will be present. If a kind for one file is specified, we expect as many kind entries
// as sources.
Error.WriteLine($"{Sources.Description} has {Sources.Values.Count}, but {DocumentKinds.Description} has {DocumentKinds.Values.Count} values.");
Error.WriteLine($"{Sources.Description} has {Sources.Values.Count}, but {FileKinds.Description} has {FileKinds.Values.Count} values.");
return false;
}
@ -171,7 +171,6 @@ namespace Microsoft.AspNetCore.Razor.Tools
var engine = RazorProjectEngine.Create(configuration, compositeFileSystem, b =>
{
b.Features.Add(new StaticTagHelperFeature() { TagHelpers = tagHelpers, });
b.Features.Add(new InputDocumentKindClassifierPass(sourceItems));
if (GenerateDeclaration.HasValue())
{
@ -223,6 +222,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
basePath: "/",
filePath: item.FilePath,
relativePhysicalPath: item.RelativePhysicalPath,
fileKind: item.FileKind,
file: new FileInfo(item.SourcePath));
project.Add(projectItem);
@ -251,15 +251,15 @@ namespace Microsoft.AspNetCore.Razor.Tools
}
}
private SourceItem[] GetSourceItems(string projectDirectory, List<string> sources, List<string> outputs, List<string> relativePath, List<string> documentKinds)
private SourceItem[] GetSourceItems(string projectDirectory, List<string> sources, List<string> outputs, List<string> relativePath, List<string> fileKinds)
{
var items = new SourceItem[sources.Count];
for (var i = 0; i < items.Length; i++)
{
var outputPath = Path.Combine(projectDirectory, outputs[i]);
var documentKind = documentKinds.Count > 0 ? documentKinds[i] : "mvc";
var fileKind = fileKinds.Count > 0 ? fileKinds[i] : "mvc";
items[i] = new SourceItem(sources[i], outputs[i], relativePath[i], documentKind);
items[i] = new SourceItem(sources[i], outputs[i], relativePath[i], fileKind);
}
return items;
@ -297,7 +297,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
private readonly struct SourceItem
{
public SourceItem(string sourcePath, string outputPath, string physicalRelativePath, string documentKind)
public SourceItem(string sourcePath, string outputPath, string physicalRelativePath, string fileKind)
{
SourcePath = sourcePath;
OutputPath = outputPath;
@ -305,7 +305,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
FilePath = '/' + physicalRelativePath
.Replace(Path.DirectorySeparatorChar, '/')
.Replace("//", "/");
DocumentKind = documentKind;
FileKind = fileKind;
}
public string SourcePath { get; }
@ -316,7 +316,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
public string FilePath { get; }
public string DocumentKind { get; }
public string FileKind { get; }
}
private class StaticTagHelperFeature : ITagHelperFeature
@ -342,31 +342,5 @@ namespace Microsoft.AspNetCore.Razor.Tools
options.SuppressPrimaryMethodBody = true;
}
}
private class InputDocumentKindClassifierPass : RazorEngineFeatureBase, IRazorDocumentClassifierPass
{
public InputDocumentKindClassifierPass(SourceItem[] sourceItems)
{
DocumentKinds = new Dictionary<string, string>(sourceItems.Length, StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < sourceItems.Length; i++)
{
var item = sourceItems[i];
DocumentKinds[item.SourcePath] = item.DocumentKind;
}
}
// Run before other document classifiers
public int Order => -1000;
public Dictionary<string, string> DocumentKinds { get; }
public void Execute(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
{
if (DocumentKinds.TryGetValue(codeDocument.Source.FilePath, out var kind))
{
codeDocument.SetInputDocumentKind(kind);
}
}
}
}
}

View File

@ -8,20 +8,26 @@ namespace Microsoft.AspNetCore.Razor.Language
{
public class TestRazorProjectItem : RazorProjectItem
{
private readonly string _fileKind;
public TestRazorProjectItem(
string filePath,
string physicalPath = null,
string relativePhysicalPath = null,
string basePath = "/")
string basePath = "/",
string fileKind = null)
{
FilePath = filePath;
PhysicalPath = physicalPath;
RelativePhysicalPath = relativePhysicalPath;
BasePath = basePath;
_fileKind = fileKind;
}
public override string BasePath { get; }
public override string FileKind => _fileKind ?? base.FileKind;
public override string FilePath { get; }
public override string PhysicalPath { get; }