Introduce RazorCompilation

This commit is contained in:
Pranav K 2017-02-14 16:47:32 -08:00
parent cb5b8a45f1
commit a6d611aecd
32 changed files with 771 additions and 74 deletions

View File

@ -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.Linq;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution
@ -15,5 +16,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
return true;
}
protected override void OnDocumentStructureCreated(
RazorCodeDocument codeDocument,
NamespaceDeclarationIRNode @namespace,
ClassDeclarationIRNode @class,
RazorMethodDeclarationIRNode method)
{
var configuration = Engine.Features.OfType<DefaultDocumentClassifierPassFeature>().FirstOrDefault();
if (configuration != null)
{
for (var i = 0; i < configuration.ConfigureClass.Count; i++)
{
var configureClass = configuration.ConfigureClass[i];
configureClass(codeDocument, @class);
}
for (var i = 0; i < configuration.ConfigureNamespace.Count; i++)
{
var configureNamespace = configuration.ConfigureNamespace[i];
configureNamespace(codeDocument, @namespace);
}
for (var i = 0; i < configuration.ConfigureMethod.Count; i++)
{
var configureMethod = configuration.ConfigureMethod[i];
configureMethod(codeDocument, @method);
}
}
}
}
}

View File

@ -0,0 +1,23 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution
{
internal class DefaultDocumentClassifierPassFeature : IRazorEngineFeature
{
public RazorEngine Engine { get; set; }
public IList<Action<RazorCodeDocument, ClassDeclarationIRNode>> ConfigureClass { get; } =
new List<Action<RazorCodeDocument, ClassDeclarationIRNode>>();
public IList<Action<RazorCodeDocument, NamespaceDeclarationIRNode>> ConfigureNamespace { get; } =
new List<Action<RazorCodeDocument, NamespaceDeclarationIRNode>>();
public IList<Action<RazorCodeDocument, RazorMethodDeclarationIRNode>> ConfigureMethod { get; } =
new List<Action<RazorCodeDocument, RazorMethodDeclarationIRNode>>();
}
}

View File

@ -218,6 +218,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
}
/// <summary>
/// The item '{0}' could not be found.
/// </summary>
internal static string RazorTemplateEngine_ItemCouldNotBeFound
{
get { return GetString("RazorTemplateEngine_ItemCouldNotBeFound"); }
}
/// <summary>
/// The item '{0}' could not be found.
/// </summary>
internal static string FormatRazorTemplateEngine_ItemCouldNotBeFound(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("RazorTemplateEngine_ItemCouldNotBeFound"), p0);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution
{

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
namespace Microsoft.AspNetCore.Razor.Evolution
@ -23,6 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return builder.Build();
}
public static RazorEngine CreateDesignTime()
{
return CreateDesignTime(configure: null);
@ -73,6 +75,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution
// Default Runtime Targets
builder.AddTargetExtension(new TemplateTargetExtension());
// Default configuration
var configurationFeature = new DefaultDocumentClassifierPassFeature();
configurationFeature.ConfigureClass.Add((document, @class) =>
{
@class.Name = "Template";
@class.AccessModifier = "public";
});
configurationFeature.ConfigureNamespace.Add((document, @namespace) =>
{
@namespace.Content = "Razor";
});
configurationFeature.ConfigureMethod.Add((document, @method) =>
{
@method.Name = "ExecuteAsync";
@method.ReturnType = $"global::{typeof(Task).FullName}";
@method.AccessModifier = "public";
method.Modifiers = new[] { "async", "override" };
});
builder.Features.Add(configurationFeature);
}
internal static void AddRuntimeDefaults(IRazorEngineBuilder builder)

View File

@ -45,6 +45,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return builder;
}
public static IRazorEngineBuilder SetBaseType(this IRazorEngineBuilder builder, string baseType)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
var configurationFeature = GetDefaultDocumentClassifierPassFeature(builder);
configurationFeature.ConfigureClass.Add((document, @class) => @class.BaseType = baseType);
return builder;
}
private static IRazorDirectiveFeature GetDirectiveFeature(IRazorEngineBuilder builder)
{
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
@ -68,5 +80,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return targetExtensionFeature;
}
private static DefaultDocumentClassifierPassFeature GetDefaultDocumentClassifierPassFeature(IRazorEngineBuilder builder)
{
var configurationFeature = builder.Features.OfType<DefaultDocumentClassifierPassFeature>().FirstOrDefault();
if (configurationFeature == null)
{
configurationFeature = new DefaultDocumentClassifierPassFeature();
builder.Features.Add(configurationFeature);
}
return configurationFeature;
}
}
}

View File

@ -48,6 +48,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return ReadFromInternal(stream, filename, encoding);
}
public static RazorSourceDocument ReadFrom(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
var path = projectItem.PhysicalPath;
if (string.IsNullOrEmpty(path))
{
path = projectItem.Path;
}
using (var inputStream = projectItem.Read())
{
return ReadFrom(inputStream, path);
}
}
private static RazorSourceDocument ReadFromInternal(Stream stream, string filename, Encoding encoding)
{
var streamLength = (int)stream.Length;

View File

@ -0,0 +1,216 @@
// 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.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Evolution
{
/// <summary>
/// Entry point to parse Razor files and generate code.
/// </summary>
public class RazorTemplateEngine
{
private RazorTemplateEngineOptions _options;
/// <summary>
/// Initializes a new instance of <see cref="RazorTemplateEngine"/>.
/// </summary>
/// <param name="engine">The <see cref="RazorEngine"/>.</param>
/// <param name="project">The <see cref="RazorProject"/>.</param>
public RazorTemplateEngine(
RazorEngine engine,
RazorProject project)
{
if (engine == null)
{
throw new ArgumentNullException(nameof(engine));
}
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
Engine = engine;
Project = project;
_options = new RazorTemplateEngineOptions();
}
/// <summary>
/// Gets the <see cref="RazorEngine"/>.
/// </summary>
public RazorEngine Engine { get; }
/// <summary>
/// Gets the <see cref="RazorProject"/>.
/// </summary>
public RazorProject Project { get; }
/// <summary>
/// Options to configure <see cref="RazorTemplateEngine"/>.
/// </summary>
public RazorTemplateEngineOptions Options
{
get
{
return _options;
}
set
{
_options = value ?? throw new ArgumentNullException(nameof(value));
}
}
/// <summary>
/// Parses the template specified by the project item <paramref name="path"/>.
/// </summary>
/// <param name="path">The template path.</param>
/// <returns>The <see cref="RazorCSharpDocument"/>.</returns>
public RazorCSharpDocument GenerateCode(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(path));
}
var projectItem = Project.GetItem(path);
return GenerateCode(projectItem);
}
/// <summary>
/// Parses the template specified by <paramref name="projectItem"/>.
/// </summary>
/// <param name="projectItem">The <see cref="RazorProjectItem"/>.</param>
/// <returns>The <see cref="RazorCSharpDocument"/>.</returns>
public RazorCSharpDocument GenerateCode(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
if (!projectItem.Exists)
{
throw new InvalidOperationException(Resources.FormatRazorTemplateEngine_ItemCouldNotBeFound(projectItem.Path));
}
var codeDocument = CreateCodeDocument(projectItem);
return GenerateCode(codeDocument);
}
/// <summary>
/// Parses the template specified by <paramref name="codeDocument"/>.
/// </summary>
/// <param name="codeDocument">The <see cref="RazorProjectItem"/>.</param>
/// <returns>The <see cref="RazorCSharpDocument"/>.</returns>
public virtual RazorCSharpDocument GenerateCode(RazorCodeDocument codeDocument)
{
if (codeDocument == null)
{
throw new ArgumentNullException(nameof(codeDocument));
}
Engine.Process(codeDocument);
return codeDocument.GetCSharpDocument();
}
/// <summary>
/// Generates a <see cref="RazorCodeDocument"/> for the specified <paramref name="path"/>.
/// </summary>
/// <param name="path">The template path.</param>
/// <returns>The created <see cref="RazorCodeDocument"/>.</returns>
public virtual RazorCodeDocument CreateCodeDocument(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(path));
}
var projectItem = Project.GetItem(path);
return CreateCodeDocument(projectItem);
}
/// <summary>
/// Generates a <see cref="RazorCodeDocument"/> for the specified <paramref name="projectItem"/>.
/// </summary>
/// <param name="projectItem">The <see cref="RazorProjectItem"/>.</param>
/// <returns>The created <see cref="RazorCodeDocument"/>.</returns>
public virtual RazorCodeDocument CreateCodeDocument(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
if (!projectItem.Exists)
{
throw new InvalidOperationException(Resources.FormatRazorTemplateEngine_ItemCouldNotBeFound(projectItem.Path));
}
var source = RazorSourceDocument.ReadFrom(projectItem);
var imports = GetImports(projectItem);
return RazorCodeDocument.Create(source, imports);
}
/// <summary>
/// Gets <see cref="RazorSourceDocument"/> that are applicable to the specified <paramref name="path"/>.
/// </summary>
/// <param name="path">The template path.</param>
/// <returns>The sequence of applicable <see cref="RazorSourceDocument"/>.</returns>
public IEnumerable<RazorSourceDocument> GetImports(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(path));
}
var projectItem = Project.GetItem(path);
return GetImports(projectItem);
}
/// <summary>
/// Gets <see cref="RazorSourceDocument"/> that are applicable to the specified <paramref name="projectItem"/>.
/// </summary>
/// <param name="projectItem">The <see cref="RazorProjectItem"/>.</param>
/// <returns>The sequence of applicable <see cref="RazorSourceDocument"/>.</returns>
public virtual IEnumerable<RazorSourceDocument> GetImports(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
if (!projectItem.Exists)
{
throw new InvalidOperationException(Resources.FormatRazorTemplateEngine_ItemCouldNotBeFound(projectItem.Path));
}
var importsFileName = Options.ImportsFileName;
if (string.IsNullOrEmpty(importsFileName))
{
return Enumerable.Empty<RazorSourceDocument>();
}
var result = new List<RazorSourceDocument>();
var importProjectItems = Project.FindHierarchicalItems(projectItem.Path, importsFileName);
foreach (var importItem in importProjectItems)
{
if (importItem.Exists)
{
// We want items in descending order. FindHierarchicalItems returns items in ascending order.
result.Insert(0, RazorSourceDocument.ReadFrom(importItem));
}
}
if (Options.DefaultImports != null)
{
result.Insert(0, Options.DefaultImports);
}
return result;
}
}
}

View File

@ -0,0 +1,21 @@
// 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.Evolution
{
/// <summary>
/// Options for code generation in the <see cref="RazorTemplateEngine"/>.
/// </summary>
public class RazorTemplateEngineOptions
{
/// <summary>
/// Gets or sets the file name of the imports file (e.g. _ViewImports.cshtml).
/// </summary>
public string ImportsFileName { get; set; }
/// <summary>
/// Gets or sets the default set of imports.
/// </summary>
public RazorSourceDocument DefaultImports { get; set; }
}
}

View File

@ -156,4 +156,7 @@
<data name="DocumentMissingTarget" xml:space="preserve">
<value>The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.</value>
</data>
<data name="RazorTemplateEngine_ItemCouldNotBeFound" xml:space="preserve">
<value>The item '{0}' could not be found.</value>
</data>
</root>

View File

@ -11,12 +11,9 @@ using System.Runtime.Remoting.Messaging;
#else
using System.Threading;
#endif
using System.Text;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Xunit;
using Xunit.Sdk;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
@ -24,7 +21,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
public abstract class IntegrationTestBase
{
private static readonly string ThisProjectName = typeof(IntializeTestFileAttribute).GetTypeInfo().Assembly.GetName().Name;
private static readonly string TestProjectRoot = FindTestProjectRoot();
private static string FindTestProjectRoot()
{
@ -54,6 +50,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
private static readonly AsyncLocal<string> _filename = new AsyncLocal<string>();
#endif
protected static string TestProjectRoot { get; } = FindTestProjectRoot();
// Used by the test framework to set the 'base' name for test files.
public static string Filename
{

View File

@ -0,0 +1,53 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
public class RazorTemplateEngineIntegrationTest : IntegrationTestBase
{
[Fact]
public void GenerateCodeWithDefaults()
{
// Arrange
var filePath = Path.Combine(TestProjectRoot, $"{Filename}.cshtml");
var content = File.ReadAllText(filePath);
var projectItem = new TestRazorProjectItem($"{Filename}.cshtml", "")
{
Content = content,
};
var project = new TestRazorProject(new[] { projectItem });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act
var resultcSharpDocument = templateEngine.GenerateCode(projectItem.Path);
// Assert
AssertCSharpDocumentMatchesBaseline(resultcSharpDocument);
}
[Fact]
public void GenerateCodeWithBaseType()
{
// Arrange
var filePath = Path.Combine(TestProjectRoot, $"{Filename}.cshtml");
var content = File.ReadAllText(filePath);
var projectItem = new TestRazorProjectItem($"{Filename}.cshtml", "")
{
Content = content,
};
var project = new TestRazorProject(new[] { projectItem });
var razorEngine = RazorEngine.Create(engine => engine.SetBaseType("MyBaseType"));
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act
var cSharpDocument = templateEngine.GenerateCode(projectItem.Path);
// Assert
AssertCSharpDocumentMatchesBaseline(cSharpDocument);
}
}
}

View File

@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFrameworks>netcoreapp1.1;net46</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp1.1</TargetFrameworks>
<!-- To generate baselines, run tests with /p:GenerateBaselines=true -->
<DefineConstants Condition="'$(GenerateBaselines)'=='true'">$(DefineConstants);GENERATE_BASELINES</DefineConstants>
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_BASELINES</DefineConstants>
<DefaultItemExcludes>$(DefaultItemExcludes);TestFiles\**\*</DefaultItemExcludes>
</PropertyGroup>

View File

@ -3,9 +3,9 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
using Moq;
using Xunit;
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
namespace Microsoft.AspNetCore.Razor.Evolution
{
@ -155,6 +155,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
feature => Assert.IsType<DirectiveRemovalIROptimizationPass>(feature),
feature => Assert.IsType<DefaultDocumentClassifierPassFeature>(feature),
feature => Assert.IsType<RazorPreallocatedTagHelperAttributeOptimizationPass>(feature));
}
@ -183,6 +184,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
feature => Assert.IsType<DirectiveRemovalIROptimizationPass>(feature),
feature => Assert.IsType<DefaultDocumentClassifierPassFeature>(feature),
feature => Assert.IsType<RazorEngine.ConfigureDesignTimeOptions>(feature),
feature => Assert.IsType<RazorDesignTimeIRPass>(feature));
}

View File

@ -1,8 +1,6 @@
// 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.IO;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution
@ -15,7 +13,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
// Arrange
var emptyBasePath = "/";
var path = "/foo/bar.cshtml";
var projectItem = new TestRazorProjectItem(emptyBasePath, path);
var projectItem = new TestRazorProjectItem(path, basePath: emptyBasePath);
// Act
var combinedPath = projectItem.CombinedPath;
@ -31,7 +29,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
// Arrange
var path = "/foo/bar.cshtml";
var projectItem = new TestRazorProjectItem(basePath, path);
var projectItem = new TestRazorProjectItem(path, basePath: basePath);
// Act
var combinedPath = projectItem.CombinedPath;
@ -46,7 +44,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public void Extension_ReturnsNullIfFileDoesNotHaveExtension(string path)
{
// Arrange
var projectItem = new TestRazorProjectItem("/views", path);
var projectItem = new TestRazorProjectItem(path, basePath: "/views");
// Act
var extension = projectItem.Extension;
@ -62,7 +60,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public void Extension_ReturnsFileExtension(string path, string expected)
{
// Arrange
var projectItem = new TestRazorProjectItem("/views", path);
var projectItem = new TestRazorProjectItem(path, basePath: "/views");
// Act
var extension = projectItem.Extension;
@ -77,7 +75,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public void FileName_ReturnsFileNameWithExtension(string path, string expected)
{
// Arrange
var projectItem = new TestRazorProjectItem("/", path);
var projectItem = new TestRazorProjectItem(path, basePath: "/");
// Act
var fileName = projectItem.Filename;
@ -94,7 +92,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public void PathWithoutExtension_ExcludesExtension(string path, string expected)
{
// Arrange
var projectItem = new TestRazorProjectItem("/", path);
var projectItem = new TestRazorProjectItem(path, basePath: "/");
// Act
var fileName = projectItem.PathWithoutExtension;
@ -102,34 +100,5 @@ namespace Microsoft.AspNetCore.Razor.Evolution
// Assert
Assert.Equal(expected, fileName);
}
private class TestRazorProjectItem : RazorProjectItem
{
public TestRazorProjectItem(string basePath, string path)
{
BasePath = basePath;
Path = path;
}
public override string BasePath { get; }
public override string Path { get; }
public override string PhysicalPath
{
get
{
throw new NotImplementedException();
}
}
public override bool Exists => true;
public override Stream Read()
{
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,195 @@
// 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.IO;
using Microsoft.AspNetCore.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution
{
public class RazorTemplateEngineTest
{
[Fact]
public void GenerateCode_ThrowsIfItemCannotBeFound()
{
// Arrange
var project = new TestRazorProject(new RazorProjectItem[] { });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(
() => templateEngine.GenerateCode("/does-not-exist.cstml"));
Assert.Equal("The item '/does-not-exist.cstml' could not be found.", ex.Message);
}
[Fact]
public void SettingOptions_ThrowsIfValueIsNull()
{
// Arrange
var project = new TestRazorProject(new RazorProjectItem[] { });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act & Assert
ExceptionAssert.ThrowsArgumentNull(
() => templateEngine.Options = null,
"value");
}
[Fact]
public void GenerateCode_WithPath()
{
// Arrange
var path = "/Views/Home/Index.cshtml";
var projectItem = new TestRazorProjectItem(path);
var project = new TestRazorProject(new[] { projectItem });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act
var cSharpDocument = templateEngine.GenerateCode(path);
// Assert
Assert.NotNull(cSharpDocument);
Assert.NotEmpty(cSharpDocument.GeneratedCode);
Assert.Empty(cSharpDocument.Diagnostics);
}
[Fact]
public void GenerateCode_ThrowsIfProjectItemCannotBeFound()
{
// Arrange
var path = "/Views/Home/Index.cshtml";
var project = new TestRazorProject(new RazorProjectItem[] { });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => templateEngine.GenerateCode(path));
Assert.Equal($"The item '{path}' could not be found.", ex.Message);
}
[Fact]
public void GenerateCode_WithProjectItem()
{
// Arrange
var path = "/Views/Home/Index.cshtml";
var projectItem = new TestRazorProjectItem(path);
var project = new TestRazorProject(new[] { projectItem });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act
var cSharpDocument = templateEngine.GenerateCode(projectItem);
// Assert
Assert.NotNull(cSharpDocument);
Assert.NotEmpty(cSharpDocument.GeneratedCode);
Assert.Empty(cSharpDocument.Diagnostics);
}
[Fact]
public void GenerateCode_WithCodeDocument()
{
// Arrange
var path = "/Views/Home/Index.cshtml";
var projectItem = new TestRazorProjectItem(path);
var project = new TestRazorProject(new[] { projectItem });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project);
// Act - 1
var codeDocument = templateEngine.CreateCodeDocument(path);
// Assert - 1
Assert.NotNull(codeDocument);
// Act - 2
var cSharpDocument = templateEngine.GenerateCode(codeDocument);
// Assert
Assert.NotNull(cSharpDocument);
Assert.NotEmpty(cSharpDocument.GeneratedCode);
Assert.Empty(cSharpDocument.Diagnostics);
}
[Fact]
public void CreateCodeDocument_ThrowsIfPathCannotBeFound()
{
// Arrange
var projectItem = new TestRazorProjectItem("/Views/Home/Index.cshtml");
var project = new TestRazorProject(new[] { projectItem });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project)
{
Options =
{
ImportsFileName = "MyImport.cshtml",
}
};
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => templateEngine.CreateCodeDocument("/DoesNotExist.cshtml"));
// Assert
Assert.Equal("The item '/DoesNotExist.cshtml' could not be found.", ex.Message);
}
[Fact]
public void CreateCodeDocument_IncludesImportsIfFileIsPresent()
{
// Arrange
var projectItem = new TestRazorProjectItem("/Views/Home/Index.cshtml");
var import1 = new TestRazorProjectItem("/MyImport.cshtml");
var import2 = new TestRazorProjectItem("/Views/Home/MyImport.cshtml");
var project = new TestRazorProject(new[] { import1, import2, projectItem });
var razorEngine = RazorEngine.Create();
var templateEngine = new RazorTemplateEngine(razorEngine, project)
{
Options =
{
ImportsFileName = "MyImport.cshtml",
}
};
// Act
var codeDocument = templateEngine.CreateCodeDocument("/Views/Home/Index.cshtml");
// Assert
Assert.Collection(codeDocument.Imports,
import => Assert.Equal("/MyImport.cshtml", import.Filename),
import => Assert.Equal("/Views/Home/MyImport.cshtml", import.Filename));
}
[Fact]
public void CreateCodeDocument_IncludesDefaultImportIfNotNull()
{
// Arrange
var projectItem = new TestRazorProjectItem("/Views/Home/Index.cshtml");
var import1 = new TestRazorProjectItem("/MyImport.cshtml");
var import2 = new TestRazorProjectItem("/Views/Home/MyImport.cshtml");
var project = new TestRazorProject(new[] { import1, import2, projectItem });
var razorEngine = RazorEngine.Create();
var defaultImport = RazorSourceDocument.ReadFrom(new MemoryStream(), "Default.cshtml");
var templateEngine = new RazorTemplateEngine(razorEngine, project)
{
Options =
{
ImportsFileName = "MyImport.cshtml",
DefaultImports = defaultImport,
}
};
// Act
var codeDocument = templateEngine.CreateCodeDocument(projectItem);
// Assert
Assert.Collection(codeDocument.Imports,
import => Assert.Same(defaultImport, import),
import => Assert.Equal("/MyImport.cshtml", import.Filename),
import => Assert.Equal("/Views/Home/MyImport.cshtml", import.Filename));
}
}
}

View File

@ -1,8 +1,7 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
RazorMethodDeclaration - - - - -
Directive - - test_directive
ClassDeclaration - - public - Template - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync

View File

@ -1,7 +1,7 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
RazorMethodDeclaration - - - - -
ClassDeclaration - - public - Template - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync

View File

@ -1,8 +1,8 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
RazorMethodDeclaration - - - - -
ClassDeclaration - - public - Template - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [13] HelloWorld.cshtml) - Hello, World!

View File

@ -1,10 +1,10 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
RazorMethodDeclaration - - - - -
ClassDeclaration - - public - Template - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [25] HtmlWithConditionalAttribute.cshtml) - <html>\n<body>\n <span
HtmlAttribute - (25:2,9 [13] HtmlWithConditionalAttribute.cshtml) - val=" - "
CSharpAttributeValue - (31:2,15 [6] HtmlWithConditionalAttribute.cshtml) -

View File

@ -1,10 +1,10 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
RazorMethodDeclaration - - - - -
ClassDeclaration - - public - Template - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [36] HtmlWithDataDashAttribute.cshtml) - <html>\n<body>\n <span data-val="
CSharpExpression - (37:2,21 [5] HtmlWithDataDashAttribute.cshtml)
RazorIRToken - (37:2,21 [5] HtmlWithDataDashAttribute.cshtml) - CSharp - Hello

View File

@ -1,10 +1,10 @@
#pragma checksum "TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "5898df8049fc1522f646a774050e93cbf5cafb84"
namespace
namespace Razor
{
#line hidden
using System;
using System.Threading.Tasks;
class
public class Template
{
private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute __tagHelperAttribute_0 = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute("value", "Hello", global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute __tagHelperAttribute_1 = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute("type", new global::Microsoft.AspNetCore.Html.HtmlString("text"), global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.SingleQuotes);
@ -30,7 +30,7 @@ namespace
private global::FormTagHelper __FormTagHelper = null;
private global::InputTagHelper __InputTagHelper = null;
#pragma warning disable 1998
()
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
BeginContext(31, 28, true);
WriteLiteral("<span someattr>Hola</span>\r\n");

View File

@ -1,14 +1,14 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
ClassDeclaration - - public - Template - -
DeclarePreallocatedTagHelperAttribute - - __tagHelperAttribute_0 - value - Hello - HtmlAttributeValueStyle.DoubleQuotes
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_1 - type - text - HtmlAttributeValueStyle.SingleQuotes
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_2 - unbound - foo - HtmlAttributeValueStyle.DoubleQuotes
DeclareTagHelperFields - - FormTagHelper - InputTagHelper
RazorMethodDeclaration - - - - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
CSharpStatement - - BeginContext(31, 28, true);
HtmlContent - (31:1,0 [28] BasicTest.cshtml) - <span someattr>Hola</span>\n
CSharpStatement - - EndContext();

View File

@ -0,0 +1,16 @@
#pragma checksum "TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithBaseType.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "247d02621904d87ab805e437f33c5d03b9a0de91"
namespace Razor
{
#line hidden
using System;
using System.Threading.Tasks;
public class Template : MyBaseType
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("<h1>Hello world!</h1>");
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,16 @@
#pragma checksum "TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithDefaults.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "247d02621904d87ab805e437f33c5d03b9a0de91"
namespace Razor
{
#line hidden
using System;
using System.Threading.Tasks;
public class Template
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("<h1>Hello world!</h1>");
}
#pragma warning restore 1998
}
}

View File

@ -1,14 +1,14 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
ClassDeclaration - - public - Template - -
DeclarePreallocatedTagHelperAttribute - - __tagHelperAttribute_0 - value - Hello - HtmlAttributeValueStyle.DoubleQuotes
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_1 - type - text - HtmlAttributeValueStyle.SingleQuotes
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_2 - unbound - foo - HtmlAttributeValueStyle.DoubleQuotes
DeclareTagHelperFields - - PTagHelper - FormTagHelper - InputTagHelper
RazorMethodDeclaration - - - - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
TagHelper - (31:1,0 [20] NestedTagHelpers.cshtml)
InitializeTagHelperStructure - - p - TagMode.StartTagAndEndTag
HtmlContent - (43:1,12 [4] NestedTagHelpers.cshtml) - Hola

View File

@ -1,13 +1,13 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
ClassDeclaration - - public - Template - -
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_0 - value - Hello - HtmlAttributeValueStyle.SingleQuotes
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_1 - type - text - HtmlAttributeValueStyle.SingleQuotes
DeclareTagHelperFields - - InputTagHelper
RazorMethodDeclaration - - - - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (31:1,0 [25] SimpleTagHelpers.cshtml) - <p>Hola</p>\n<form>\n
TagHelper - (56:3,4 [35] SimpleTagHelpers.cshtml)
InitializeTagHelperStructure - - input - TagMode.SelfClosing

View File

@ -1,12 +1,12 @@
Document -
Checksum -
NamespaceDeclaration - -
NamespaceDeclaration - - Razor
UsingStatement - - System
UsingStatement - - System.Threading.Tasks
ClassDeclaration - - - - -
ClassDeclaration - - public - Template - -
DeclarePreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_0 - type - text - HtmlAttributeValueStyle.SingleQuotes
DeclareTagHelperFields - - InputTagHelper
RazorMethodDeclaration - - - - -
RazorMethodDeclaration - - public - async, override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (31:1,0 [12] TagHelpersWithBoundAttributes.cshtml) - <form>\n
TagHelper - (43:2,4 [34] TagHelpersWithBoundAttributes.cshtml)
InitializeTagHelperStructure - - input - TagMode.SelfClosing

View File

@ -0,0 +1,34 @@
// 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.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Evolution
{
public class TestRazorProject : RazorProject
{
private readonly Dictionary<string, RazorProjectItem> _lookup;
public TestRazorProject(IList<RazorProjectItem> items)
{
_lookup = items.ToDictionary(item => item.Path);
}
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
{
throw new NotImplementedException();
}
public override RazorProjectItem GetItem(string path)
{
if (!_lookup.TryGetValue(path, out var value))
{
value = new NotFoundProjectItem("", path);
}
return value;
}
}
}

View File

@ -0,0 +1,33 @@
// 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 System.Text;
namespace Microsoft.AspNetCore.Razor.Evolution
{
public class TestRazorProjectItem : RazorProjectItem
{
public TestRazorProjectItem(
string path,
string physicalPath = null,
string basePath = "/")
{
Path = path;
PhysicalPath = physicalPath;
BasePath = basePath;
}
public override string BasePath { get; }
public override string Path { get; }
public override string PhysicalPath { get; }
public override bool Exists => true;
public string Content { get; set; } = "Default content";
public override Stream Read() => new MemoryStream(Encoding.UTF8.GetBytes(Content));
}
}