From e200b69511e10d3c3c367a7ea96a3d2465c8dd7c Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Wed, 14 Feb 2018 14:37:09 -0800 Subject: [PATCH] Change IImportProjectFeature to consume RazorProjectItems. - Updated all implementations of `IImportProjectFeature`; for MVC I went ahead and made a single project item that's always returned for MVC scenarios. That project item is smart about returning its content in a light-weight stream fashion. - Had to add a `RazorProjectItem` => `RazorSourceDocument` conversion mechanic into `DefaultRazorProjectEngine`. - Added tests for `DefaultRazorProjectItem.ConvertToSourceDocument`. - Removed the `ProjectEngine` API from `VisualStudioRazorParser`. This was unrelated but was missed feedback. #2068 --- .../MvcImportProjectFeature.cs | 85 +++++++++--------- .../MvcImportProjectFeature.cs | 89 ++++++++++--------- .../DefaultImportProjectFeature.cs | 2 +- .../DefaultRazorProjectEngine.cs | 25 +++++- .../IImportProjectFeature.cs | 2 +- .../RazorProjectEngineBuilderExtensions.cs | 11 +-- .../DefaultImportDocumentManager.cs | 15 +--- .../DefaultVisualStudioRazorParser.cs | 4 +- .../VisualStudioRazorParser.cs | 2 - src/RazorPageGenerator/Program.cs | 36 +++++++- ...Test.cs => MvcImportProjectFeatureTest.cs} | 8 +- ...Test.cs => MvcImportProjectFeatureTest.cs} | 8 +- ...efaultRazorProjectEngineIntegrationTest.cs | 5 +- .../DefaultRazorProjectEngineTest.cs | 37 ++++++++ .../DefaultRazorProjectFileSystemTest.cs | 2 +- ...tVisualStudioRazorParserIntegrationTest.cs | 2 +- .../RazorSyntaxTreePartialParserTest.cs | 2 +- 17 files changed, 208 insertions(+), 127 deletions(-) rename test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/{DefaultMvcImportFeatureTest.cs => MvcImportProjectFeatureTest.cs} (92%) rename test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/{DefaultMvcImportFeatureTest.cs => MvcImportProjectFeatureTest.cs} (92%) create mode 100644 test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineTest.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcImportProjectFeature.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcImportProjectFeature.cs index b7ef608871..254c57c1c4 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcImportProjectFeature.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcImportProjectFeature.cs @@ -14,14 +14,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X { private const string ImportsFileName = "_ViewImports.cshtml"; - public IReadOnlyList GetImports(RazorProjectItem projectItem) + public IReadOnlyList GetImports(RazorProjectItem projectItem) { if (projectItem == null) { throw new ArgumentNullException(nameof(projectItem)); } - var imports = new List(); + var imports = new List(); AddDefaultDirectivesImport(imports); // We add hierarchical imports second so any default directive imports can be overridden. @@ -31,54 +31,59 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X } // Internal for testing - internal static void AddDefaultDirectivesImport(List imports) + internal static void AddDefaultDirectivesImport(List imports) { - using (var stream = new MemoryStream()) - using (var writer = new StreamWriter(stream, Encoding.UTF8)) - { - writer.WriteLine("@using System"); - writer.WriteLine("@using System.Collections.Generic"); - writer.WriteLine("@using System.Linq"); - writer.WriteLine("@using System.Threading.Tasks"); - writer.WriteLine("@using Microsoft.AspNetCore.Mvc"); - writer.WriteLine("@using Microsoft.AspNetCore.Mvc.Rendering"); - writer.WriteLine("@using Microsoft.AspNetCore.Mvc.ViewFeatures"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider"); - writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor"); - writer.Flush(); - - stream.Position = 0; - var defaultMvcImports = RazorSourceDocument.ReadFrom(stream, fileName: null, encoding: Encoding.UTF8); - imports.Add(defaultMvcImports); - } + imports.Add(DefaultDirectivesProjectItem.Instance); } // Internal for testing - internal void AddHierarchicalImports(RazorProjectItem projectItem, List imports) + internal void AddHierarchicalImports(RazorProjectItem projectItem, List imports) { // We want items in descending order. FindHierarchicalItems returns items in ascending order. var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(projectItem.FilePath, ImportsFileName).Reverse(); - foreach (var importProjectItem in importProjectItems) + imports.AddRange(importProjectItems); + } + + private class DefaultDirectivesProjectItem : RazorProjectItem + { + private readonly byte[] _defaultImportBytes; + + private DefaultDirectivesProjectItem() { - RazorSourceDocument importSourceDocument; + var preamble = Encoding.UTF8.GetPreamble(); + var content = @" +@using System +@using System.Collections.Generic +@using System.Linq +@using System.Threading.Tasks +@using Microsoft.AspNetCore.Mvc +@using Microsoft.AspNetCore.Mvc.Rendering +@using Microsoft.AspNetCore.Mvc.ViewFeatures +@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html +@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json +@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component +@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url +@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider +@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor +"; + var contentBytes = Encoding.UTF8.GetBytes(content); - if (importProjectItem.Exists) - { - importSourceDocument = RazorSourceDocument.ReadFrom(importProjectItem); - } - else - { - // File doesn't exist on disk so just add a marker source document as an identifier for "there could be something here". - var sourceDocumentProperties = new RazorSourceDocumentProperties(importProjectItem.FilePath, importProjectItem.RelativePhysicalPath); - importSourceDocument = RazorSourceDocument.Create(string.Empty, sourceDocumentProperties); - } - - imports.Add(importSourceDocument); + _defaultImportBytes = new byte[preamble.Length + contentBytes.Length]; + preamble.CopyTo(_defaultImportBytes, 0); + contentBytes.CopyTo(_defaultImportBytes, preamble.Length); } + + public override string BasePath => null; + + public override string FilePath => null; + + public override string PhysicalPath => null; + + public override bool Exists => true; + + public static DefaultDirectivesProjectItem Instance { get; } = new DefaultDirectivesProjectItem(); + + public override Stream Read() => new MemoryStream(_defaultImportBytes); } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcImportProjectFeature.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcImportProjectFeature.cs index 999dd6fdbb..5c6ee7361d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcImportProjectFeature.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcImportProjectFeature.cs @@ -14,14 +14,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions { private const string ImportsFileName = "_ViewImports.cshtml"; - public IReadOnlyList GetImports(RazorProjectItem projectItem) + public IReadOnlyList GetImports(RazorProjectItem projectItem) { if (projectItem == null) { throw new ArgumentNullException(nameof(projectItem)); } - var imports = new List(); + var imports = new List(); AddDefaultDirectivesImport(imports); // We add hierarchical imports second so any default directive imports can be overridden. @@ -31,56 +31,61 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions } // Internal for testing - internal static void AddDefaultDirectivesImport(List imports) + internal static void AddDefaultDirectivesImport(List imports) { - using (var stream = new MemoryStream()) - using (var writer = new StreamWriter(stream, Encoding.UTF8)) - { - writer.WriteLine("@using System"); - writer.WriteLine("@using System.Collections.Generic"); - writer.WriteLine("@using System.Linq"); - writer.WriteLine("@using System.Threading.Tasks"); - writer.WriteLine("@using Microsoft.AspNetCore.Mvc"); - writer.WriteLine("@using Microsoft.AspNetCore.Mvc.Rendering"); - writer.WriteLine("@using Microsoft.AspNetCore.Mvc.ViewFeatures"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url"); - writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider"); - writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor"); - writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor"); - writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor"); - writer.Flush(); - - stream.Position = 0; - var defaultMvcImports = RazorSourceDocument.ReadFrom(stream, fileName: null, encoding: Encoding.UTF8); - imports.Add(defaultMvcImports); - } + imports.Add(DefaultDirectivesProjectItem.Instance); } // Internal for testing - internal void AddHierarchicalImports(RazorProjectItem projectItem, List imports) + internal void AddHierarchicalImports(RazorProjectItem projectItem, List imports) { // We want items in descending order. FindHierarchicalItems returns items in ascending order. var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(projectItem.FilePath, ImportsFileName).Reverse(); - foreach (var importProjectItem in importProjectItems) + imports.AddRange(importProjectItems); + } + + private class DefaultDirectivesProjectItem : RazorProjectItem + { + private readonly byte[] _defaultImportBytes; + + private DefaultDirectivesProjectItem() { - RazorSourceDocument importSourceDocument; + var preamble = Encoding.UTF8.GetPreamble(); + var content = @" +@using System +@using System.Collections.Generic +@using System.Linq +@using System.Threading.Tasks +@using Microsoft.AspNetCore.Mvc +@using Microsoft.AspNetCore.Mvc.Rendering +@using Microsoft.AspNetCore.Mvc.ViewFeatures +@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html +@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json +@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component +@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url +@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider +@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor +@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor +@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor +"; + var contentBytes = Encoding.UTF8.GetBytes(content); - if (importProjectItem.Exists) - { - importSourceDocument = RazorSourceDocument.ReadFrom(importProjectItem); - } - else - { - // File doesn't exist on disk so just add a marker source document as an identifier for "there could be something here". - var sourceDocumentProperties = new RazorSourceDocumentProperties(importProjectItem.FilePath, importProjectItem.RelativePhysicalPath); - importSourceDocument = RazorSourceDocument.Create(string.Empty, sourceDocumentProperties); - } - - imports.Add(importSourceDocument); + _defaultImportBytes = new byte[preamble.Length + contentBytes.Length]; + preamble.CopyTo(_defaultImportBytes, 0); + contentBytes.CopyTo(_defaultImportBytes, preamble.Length); } + + public override string BasePath => null; + + public override string FilePath => null; + + public override string PhysicalPath => null; + + public override bool Exists => true; + + public static DefaultDirectivesProjectItem Instance { get; } = new DefaultDirectivesProjectItem(); + + public override Stream Read() => new MemoryStream(_defaultImportBytes); } } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultImportProjectFeature.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultImportProjectFeature.cs index 15fc2635cd..92778322d7 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/DefaultImportProjectFeature.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultImportProjectFeature.cs @@ -8,6 +8,6 @@ namespace Microsoft.AspNetCore.Razor.Language { internal class DefaultImportProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature { - public IReadOnlyList GetImports(RazorProjectItem projectItem) => Array.Empty(); + public IReadOnlyList GetImports(RazorProjectItem projectItem) => Array.Empty(); } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs index b432fd2f9a..e7eaafec22 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorProjectEngine.cs @@ -64,12 +64,13 @@ namespace Microsoft.AspNetCore.Razor.Language var sourceDocument = RazorSourceDocument.ReadFrom(projectItem); var importFeature = GetRequiredFeature(); - var imports = importFeature.GetImports(projectItem); + var importItems = importFeature.GetImports(projectItem); + var importSourceDocuments = importItems.Select(ConvertToSourceDocument); var parserOptions = GetRequiredFeature().Create(ConfigureParserOptions); var codeGenerationOptions = GetRequiredFeature().Create(ConfigureCodeGenerationOptions); - return RazorCodeDocument.Create(sourceDocument, imports, parserOptions, codeGenerationOptions); + return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions); } protected override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem) @@ -82,12 +83,14 @@ namespace Microsoft.AspNetCore.Razor.Language var sourceDocument = RazorSourceDocument.ReadFrom(projectItem); var importFeature = GetRequiredFeature(); - var imports = importFeature.GetImports(projectItem); + var importItems = importFeature.GetImports(projectItem); + var importSourceDocuments = importItems.Select(ConvertToSourceDocument); var parserOptions = GetRequiredFeature().Create(ConfigureDesignTimeParserOptions); var codeGenerationOptions = GetRequiredFeature().Create(ConfigureDesignTimeCodeGenerationOptions); - return RazorCodeDocument.Create(sourceDocument, imports, parserOptions, codeGenerationOptions); + + return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions); } protected override void ProcessCore(RazorCodeDocument codeDocument) @@ -133,5 +136,19 @@ namespace Microsoft.AspNetCore.Razor.Language builder.SuppressChecksum = true; builder.SuppressMetadataAttributes = true; } + + // Internal for testing + internal static RazorSourceDocument ConvertToSourceDocument(RazorProjectItem importItem) + { + if (importItem.Exists) + { + // Normal import, has file paths, content etc. + return RazorSourceDocument.ReadFrom(importItem); + } + + // Marker import, doesn't exist, used as an identifier for "there could be something here". + var sourceDocumentProperties = new RazorSourceDocumentProperties(importItem.FilePath, importItem.RelativePhysicalPath); + return RazorSourceDocument.Create(string.Empty, sourceDocumentProperties); + } } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/IImportProjectFeature.cs b/src/Microsoft.AspNetCore.Razor.Language/IImportProjectFeature.cs index 90bd6638f4..98d8ffe558 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/IImportProjectFeature.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/IImportProjectFeature.cs @@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Razor.Language { public interface IImportProjectFeature : IRazorProjectEngineFeature { - IReadOnlyList GetImports(RazorProjectItem projectItem); + IReadOnlyList GetImports(RazorProjectItem projectItem); } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngineBuilderExtensions.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngineBuilderExtensions.cs index 4c3fe12cc7..48695a6f97 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngineBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngineBuilderExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.AspNetCore.Razor.Language.CodeGeneration; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -144,13 +145,13 @@ namespace Microsoft.AspNetCore.Razor.Language } /// - /// Adds the provided documents as imports to all documents processed + /// Adds the provided s as imports to all project items processed /// by the . /// /// The . /// The collection of imports. /// The . - public static RazorProjectEngineBuilder AddDefaultImports(this RazorProjectEngineBuilder builder, params RazorSourceDocument[] imports) + public static RazorProjectEngineBuilder AddDefaultImports(this RazorProjectEngineBuilder builder, params RazorProjectItem[] imports) { if (builder == null) { @@ -203,7 +204,7 @@ namespace Microsoft.AspNetCore.Razor.Language private class AdditionalImportsProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature { private readonly IImportProjectFeature _existingImportFeature; - private readonly RazorSourceDocument[] _imports; + private readonly RazorProjectItem[] _imports; public override RazorProjectEngine ProjectEngine { @@ -215,13 +216,13 @@ namespace Microsoft.AspNetCore.Razor.Language } } - public AdditionalImportsProjectFeature(IImportProjectFeature existingImportFeature, params RazorSourceDocument[] imports) + public AdditionalImportsProjectFeature(IImportProjectFeature existingImportFeature, params RazorProjectItem[] imports) { _existingImportFeature = existingImportFeature; _imports = imports; } - public IReadOnlyList GetImports(RazorProjectItem projectItem) + public IReadOnlyList GetImports(RazorProjectItem projectItem) { var imports = _existingImportFeature.GetImports(projectItem).ToList(); imports.AddRange(_imports); diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultImportDocumentManager.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultImportDocumentManager.cs index 0783713d7c..2e8c5a450a 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultImportDocumentManager.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultImportDocumentManager.cs @@ -123,19 +123,10 @@ namespace Microsoft.VisualStudio.Editor.Razor // There should always be an import feature unless someone has misconfigured their RazorProjectEngine. // In that case once we attempt to parse the Razor file we'll explode and give the a user a decent // error message; for now, lets just be extra protective and assume 0 imports to not give a bad error. - var imports = importFeature?.GetImports(trackerItem) ?? Enumerable.Empty(); - var physicalImports = imports.Where(import => import.FilePath != null); + var importItems = importFeature?.GetImports(trackerItem) ?? Enumerable.Empty(); + var physicalImports = importItems.Where(import => import.FilePath != null); - // Now that we have non-dynamic imports we need to get their RazorProjectItem equivalents so we have their - // physical file paths (according to the FileSystem). - var projectItems = new List(); - foreach (var physicalImport in physicalImports) - { - var projectItem = projectEngine.FileSystem.GetItem(physicalImport.FilePath); - projectItems.Add(projectItem); - } - - return projectItems; + return physicalImports; } private void OnChanged(ImportTracker importTracker, FileChangeKind changeKind) diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs index 7225f0dd8e..ddbd000f0b 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs @@ -85,8 +85,6 @@ namespace Microsoft.VisualStudio.Editor.Razor _documentTracker.ContextChanged += DocumentTracker_ContextChanged; } - public override RazorProjectEngine ProjectEngine => _projectEngine; - public override string FilePath => _documentTracker.FilePath; public override RazorCodeDocument CodeDocument => _codeDocument; @@ -171,7 +169,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var projectDirectory = Path.GetDirectoryName(_documentTracker.ProjectPath); _projectEngine = _projectEngineFactory.Create(projectDirectory, ConfigureProjectEngine); - _parser = new BackgroundParser(ProjectEngine, FilePath, projectDirectory); + _parser = new BackgroundParser(_projectEngine, FilePath, projectDirectory); _parser.ResultsReady += OnResultsReady; _parser.Start(); diff --git a/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioRazorParser.cs b/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioRazorParser.cs index 41810db7c9..c1d71a6111 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioRazorParser.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/VisualStudioRazorParser.cs @@ -11,8 +11,6 @@ namespace Microsoft.VisualStudio.Editor.Razor { public abstract event EventHandler DocumentStructureChanged; - public abstract RazorProjectEngine ProjectEngine { get; } - public abstract string FilePath { get; } public abstract RazorCodeDocument CodeDocument { get; } diff --git a/src/RazorPageGenerator/Program.cs b/src/RazorPageGenerator/Program.cs index 4646463889..1aae7c86ef 100644 --- a/src/RazorPageGenerator/Program.cs +++ b/src/RazorPageGenerator/Program.cs @@ -68,10 +68,7 @@ Examples: configure(builder); } - builder.AddDefaultImports(RazorSourceDocument.Create(@" -@using System -@using System.Threading.Tasks -", fileName: null)); + builder.AddDefaultImports(DefaultImportItem.Instance); }); return projectEngine; } @@ -156,6 +153,37 @@ Examples: } } + private class DefaultImportItem : RazorProjectItem + { + private readonly byte[] _defaultImportBytes; + + private DefaultImportItem() + { + var preamble = Encoding.UTF8.GetPreamble(); + var content = @" +@using System +@using System.Threading.Tasks +"; + var contentBytes = Encoding.UTF8.GetBytes(content); + + _defaultImportBytes = new byte[preamble.Length + contentBytes.Length]; + preamble.CopyTo(_defaultImportBytes, 0); + contentBytes.CopyTo(_defaultImportBytes, preamble.Length); + } + + public override string BasePath => null; + + public override string FilePath => null; + + public override string PhysicalPath => null; + + public override bool Exists => true; + + public static DefaultImportItem Instance { get; } = new DefaultImportItem(); + + public override Stream Read() => new MemoryStream(_defaultImportBytes); + } + private class FileSystemRazorProjectItemWrapper : RazorProjectItem { private readonly RazorProjectItem _source; diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/DefaultMvcImportFeatureTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcImportProjectFeatureTest.cs similarity index 92% rename from test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/DefaultMvcImportFeatureTest.cs rename to test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcImportProjectFeatureTest.cs index ccd59408f9..7e811ee261 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/DefaultMvcImportFeatureTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcImportProjectFeatureTest.cs @@ -8,13 +8,13 @@ using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions { - public class DefaultMvcImportFeatureTest + public class MvcImportProjectFeatureTest { [Fact] public void AddDefaultDirectivesImport_AddsSingleDynamicImport() { // Arrange - var imports = new List(); + var imports = new List(); // Act MvcImportProjectFeature.AddDefaultDirectivesImport(imports); @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions public void AddHierarchicalImports_AddsViewImportSourceDocumentsOnDisk() { // Arrange - var imports = new List(); + var imports = new List(); var projectItem = new TestRazorProjectItem("/Contact/Index.cshtml"); var testFileSystem = new TestRazorProjectFileSystem(new[] { @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions public void AddHierarchicalImports_AddsViewImportSourceDocumentsNotOnDisk() { // Arrange - var imports = new List(); + var imports = new List(); var projectItem = new TestRazorProjectItem("/Pages/Contact/Index.cshtml"); var testFileSystem = new TestRazorProjectFileSystem(new[] { projectItem }); var mvcImportFeature = new MvcImportProjectFeature() diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/DefaultMvcImportFeatureTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcImportProjectFeatureTest.cs similarity index 92% rename from test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/DefaultMvcImportFeatureTest.cs rename to test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcImportProjectFeatureTest.cs index 680ab9ba1e..e53046ed5a 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/DefaultMvcImportFeatureTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcImportProjectFeatureTest.cs @@ -8,13 +8,13 @@ using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X { - public class DefaultMvcImportFeatureTest + public class MvcImportProjectFeatureTest { [Fact] public void AddDefaultDirectivesImport_AddsSingleDynamicImport() { // Arrange - var imports = new List(); + var imports = new List(); // Act MvcImportProjectFeature.AddDefaultDirectivesImport(imports); @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X public void AddHierarchicalImports_AddsViewImportSourceDocumentsOnDisk() { // Arrange - var imports = new List(); + var imports = new List(); var projectItem = new TestRazorProjectItem("/Contact/Index.cshtml"); var testFileSystem = new TestRazorProjectFileSystem(new[] { @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X public void AddHierarchicalImports_AddsViewImportSourceDocumentsNotOnDisk() { // Arrange - var imports = new List(); + var imports = new List(); var projectItem = new TestRazorProjectItem("/Pages/Contact/Index.cshtml"); var testFileSystem = new TestRazorProjectFileSystem(new[] { projectItem }); var mvcImportFeature = new MvcImportProjectFeature() diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineIntegrationTest.cs index 0edbd89311..427b34c1b6 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineIntegrationTest.cs @@ -1,6 +1,7 @@ // 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.IO; using Moq; using Xunit; @@ -56,7 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Language // Arrange var projectItem = new TestRazorProjectItem("Index.cshtml"); - var testImport = TestRazorSourceDocument.Create(); + var testImport = Mock.Of(i => i.Read() == new MemoryStream() && i.FilePath == "testvalue"); var importFeature = new Mock(); importFeature .Setup(feature => feature.GetImports(It.IsAny())) @@ -72,7 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert var import = Assert.Single(codeDocument.Imports); - Assert.Same(testImport, import); + Assert.Equal("testvalue", import.FilePath); } [Fact] diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineTest.cs new file mode 100644 index 0000000000..171556ecf0 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectEngineTest.cs @@ -0,0 +1,37 @@ +// 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 Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Language +{ + public class DefaultRazorProjectEngineTest + { + [Fact] + public void ConvertToSourceDocument_ConvertsNormalImports() + { + // Arrange + var projectItem = new TestRazorProjectItem("Index.cshtml"); + + // Act + var sourceDocument = DefaultRazorProjectEngine.ConvertToSourceDocument(projectItem); + + // Assert + Assert.NotNull(sourceDocument); + } + + [Fact] + public void ConvertToSourceDocument_ConvertsMarkerImports() + { + // Arrange + var projectItem = Mock.Of(item => item.FilePath == "Index.cshtml" && item.Exists == false); + + // Act + var sourceDocument = DefaultRazorProjectEngine.ConvertToSourceDocument(projectItem); + + // Assert + Assert.NotNull(sourceDocument); + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectFileSystemTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectFileSystemTest.cs index addc55ebea..af48d1e900 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectFileSystemTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorProjectFileSystemTest.cs @@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.Razor.Language Assert.Equal("/_ViewImports.cshtml", item.FilePath); Assert.Equal("/Views", item.BasePath); Assert.Equal(Path.Combine(TestFolder, "Views", "_ViewImports.cshtml"), item.PhysicalPath); - Assert.Equal(Path.Combine( "_ViewImports.cshtml"), item.RelativePhysicalPath); + Assert.Equal(Path.Combine("_ViewImports.cshtml"), item.RelativePhysicalPath); }, item => { diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserIntegrationTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserIntegrationTest.cs index 6f4c0086cd..2b07a09545 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserIntegrationTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserIntegrationTest.cs @@ -533,7 +533,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { RazorExtensions.Register(builder); - builder.AddDefaultImports(RazorSourceDocument.Create("@addTagHelper *, Test", "_TestImports.cshtml")); + builder.AddDefaultImports(new TestRazorProjectItem("_TestImports.cshtml") { Content = "@addTagHelper *, Test" }); if (tagHelpers != null) { diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxTreePartialParserTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxTreePartialParserTest.cs index 529e0126e8..a35ee3c13a 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxTreePartialParserTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorSyntaxTreePartialParserTest.cs @@ -589,7 +589,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { RazorExtensions.Register(builder); - builder.AddDefaultImports(RazorSourceDocument.Create("@addTagHelper *, Test", "_TestImports.cshtml")); + builder.AddDefaultImports(new TestRazorProjectItem("_TestImports.cshtml") { Content = "@addTagHelper *, Test" }); if (tagHelpers != null) {