commit
8a1d04e0bc
|
|
@ -1,17 +0,0 @@
|
|||
init:
|
||||
- git config --global core.autocrlf true
|
||||
branches:
|
||||
only:
|
||||
- dev
|
||||
- /^release\/.*$/
|
||||
- /^(.*\/)?ci-.*$/
|
||||
build_script:
|
||||
- ps: .\run.ps1 default-build
|
||||
clone_depth: 1
|
||||
environment:
|
||||
global:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
test: 'off'
|
||||
deploy: 'off'
|
||||
os: Visual Studio 2017 Preview
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
Razor
|
||||
=====
|
||||
|
||||
AppVeyor: [](https://ci.appveyor.com/project/aspnetci/razor/branch/dev)
|
||||
|
||||
Travis: [](https://travis-ci.org/aspnet/Razor)
|
||||
|
||||
The Razor syntax provides a fast, terse, clean and lightweight way to combine server code with HTML to create dynamic web content. This repo contains the parser and the C# code generator for the Razor syntax.
|
||||
|
|
|
|||
20
Razor.sln
20
Razor.sln
|
|
@ -1,6 +1,7 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27107.3000
|
||||
VisualStudioVersion = 15.0.27130.2036
|
||||
MinimumVisualStudioVersion = 15.0.26730.03
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C0D6505-79B3-49D0-B4C3-176F0F1836ED}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
|
@ -92,9 +93,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Razo
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Tools", "src\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj", "{3E7F2D49-3B45-45A8-9893-F73EC1EEBAAB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.Razor", "src\Microsoft.NET.Sdk.Razor\Microsoft.NET.Sdk.Razor.csproj", "{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.Razor", "src\Microsoft.NET.Sdk.Razor\Microsoft.NET.Sdk.Razor.csproj", "{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Razor.Tools.Test", "test\Microsoft.AspNetCore.Razor.Tools.Test\Microsoft.AspNetCore.Razor.Tools.Test.csproj", "{6EA56B2B-89EC-4C38-A384-97D203375B06}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Tools.Test", "test\Microsoft.AspNetCore.Razor.Tools.Test\Microsoft.AspNetCore.Razor.Tools.Test.csproj", "{6EA56B2B-89EC-4C38-A384-97D203375B06}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib", "test\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib.csproj", "{72E89155-86C7-454E-BDD9-39F497F2F61B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -398,6 +401,14 @@ Global
|
|||
{6EA56B2B-89EC-4C38-A384-97D203375B06}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6EA56B2B-89EC-4C38-A384-97D203375B06}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6EA56B2B-89EC-4C38-A384-97D203375B06}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -440,6 +451,7 @@ Global
|
|||
{3E7F2D49-3B45-45A8-9893-F73EC1EEBAAB} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{6EA56B2B-89EC-4C38-A384-97D203375B06} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{72E89155-86C7-454E-BDD9-39F497F2F61B} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// 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.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
|
|
@ -21,33 +20,22 @@ namespace Microsoft.AspNetCore.Razor.Performance
|
|||
}
|
||||
|
||||
var root = current;
|
||||
|
||||
var engine = RazorEngine.Create(b => { RazorExtensions.Register(b); });
|
||||
|
||||
var fileSystem = RazorProjectFileSystem.Create(root.FullName);
|
||||
|
||||
ProjectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, b => RazorExtensions.Register(b)); ;
|
||||
|
||||
DesignTimeTemplateEngine = new MvcRazorTemplateEngine(RazorEngine.CreateDesignTime(b => { RazorExtensions.Register(b); }), fileSystem);
|
||||
RuntimeTemplateEngine = new MvcRazorTemplateEngine(RazorEngine.Create(b => { RazorExtensions.Register(b); }), fileSystem);
|
||||
|
||||
var codeDocument = RuntimeTemplateEngine.CreateCodeDocument(Path.Combine(root.FullName, "MSN.cshtml"));
|
||||
|
||||
Imports = codeDocument.Imports;
|
||||
MSN = codeDocument.Source;
|
||||
MSN = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"));
|
||||
}
|
||||
|
||||
public RazorTemplateEngine DesignTimeTemplateEngine { get; }
|
||||
public RazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
public RazorTemplateEngine RuntimeTemplateEngine { get; }
|
||||
|
||||
public IReadOnlyList<RazorSourceDocument> Imports { get; }
|
||||
|
||||
public RazorSourceDocument MSN { get; }
|
||||
public RazorProjectItem MSN { get; }
|
||||
|
||||
[Benchmark(Description = "Razor Design Time Code Generation of MSN.com")]
|
||||
public void CodeGeneration_DesignTime_LargeStaticFile()
|
||||
{
|
||||
var codeDocument = RazorCodeDocument.Create(MSN, Imports);
|
||||
var generated = DesignTimeTemplateEngine.GenerateCode(codeDocument);
|
||||
var codeDocument = ProjectEngine.ProcessDesignTime(MSN);
|
||||
var generated = codeDocument.GetCSharpDocument();
|
||||
|
||||
if (generated.Diagnostics.Count != 0)
|
||||
{
|
||||
|
|
@ -58,8 +46,8 @@ namespace Microsoft.AspNetCore.Razor.Performance
|
|||
[Benchmark(Description = "Razor Runtime Code Generation of MSN.com")]
|
||||
public void CodeGeneration_Runtime_LargeStaticFile()
|
||||
{
|
||||
var codeDocument = RazorCodeDocument.Create(MSN, Imports);
|
||||
var generated = RuntimeTemplateEngine.GenerateCode(codeDocument);
|
||||
var codeDocument = ProjectEngine.Process(MSN);
|
||||
var generated = codeDocument.GetCSharpDocument();
|
||||
|
||||
if (generated.Diagnostics.Count != 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,23 +4,23 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Label="Package Versions">
|
||||
<BenchmarkDotNetPackageVersion>0.10.11</BenchmarkDotNetPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15698</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftBuildFrameworkPackageVersion>15.3.409</MicrosoftBuildFrameworkPackageVersion>
|
||||
<MicrosoftBuildPackageVersion>15.3.409</MicrosoftBuildPackageVersion>
|
||||
<MicrosoftBuildUtilitiesCorePackageVersion>15.3.409</MicrosoftBuildUtilitiesCorePackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15726</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30230</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview2-30230</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30230</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftBuildFrameworkPackageVersion>15.6.82</MicrosoftBuildFrameworkPackageVersion>
|
||||
<MicrosoftBuildPackageVersion>15.6.82</MicrosoftBuildPackageVersion>
|
||||
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
|
||||
<MicrosoftCodeAnalysisCommonPackageVersion>2.6.1</MicrosoftCodeAnalysisCommonPackageVersion>
|
||||
<MicrosoftCodeAnalysisCSharpPackageVersion>2.6.1</MicrosoftCodeAnalysisCSharpPackageVersion>
|
||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-25711-01</MicrosoftExtensionsDependencyModelPackageVersion>
|
||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26130-04</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.3.0</MicrosoftNETTestSdkPackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26225-03</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.0</MicrosoftNETTestSdkPackageVersion>
|
||||
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
|
||||
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
|
||||
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>15.6.161-preview</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
||||
|
|
@ -42,8 +42,8 @@
|
|||
<NETStandardLibrary20PackageVersion>2.0.1</NETStandardLibrary20PackageVersion>
|
||||
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
||||
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview2-26130-01</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemValueTuplePackageVersion>4.5.0-preview2-26130-01</SystemValueTuplePackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview2-26224-02</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemValueTuplePackageVersion>4.5.0-preview2-26224-02</SystemValueTuplePackageVersion>
|
||||
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<Project>
|
||||
<Project>
|
||||
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
||||
|
||||
<PropertyGroup Label="RestoreSources">
|
||||
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
||||
$(RestoreSources);
|
||||
https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/msbuild/api/v3/index.json;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
version:2.1.0-preview2-15698
|
||||
commithash:7216e5068cb1957e09d45fcbe58a744dd5c2de73
|
||||
version:2.1.0-preview2-15726
|
||||
commithash:599e691c41f502ed9e062b1822ce13b673fc916e
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||
{
|
||||
internal class DefaultMvcImportFeature : RazorProjectEngineFeatureBase, IRazorImportFeature
|
||||
{
|
||||
private const string ImportsFileName = "_ViewImports.cshtml";
|
||||
|
||||
public IReadOnlyList<RazorSourceDocument> GetImports(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var imports = new List<RazorSourceDocument>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
// We add hierarchical imports second so any default directive imports can be overridden.
|
||||
AddHierarchicalImports(projectItem, imports);
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static void AddDefaultDirectivesImport(List<RazorSourceDocument> 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<TModel> 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void AddHierarchicalImports(RazorProjectItem projectItem, List<RazorSourceDocument> 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)
|
||||
{
|
||||
RazorSourceDocument importSourceDocument;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new Pass(builder.Configuration.DesignTime));
|
||||
builder.Features.Add(new Pass());
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -64,13 +64,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
|
||||
internal class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
private readonly bool _designTime;
|
||||
|
||||
public Pass(bool designTime)
|
||||
{
|
||||
_designTime = designTime;
|
||||
}
|
||||
|
||||
// Runs after the @inherits directive
|
||||
public override int Order => 5;
|
||||
|
||||
|
|
@ -79,7 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
var visitor = new Visitor();
|
||||
var modelType = GetModelType(documentNode, visitor);
|
||||
|
||||
if (_designTime)
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
// Alias the TModel token to a known type.
|
||||
// This allows design time compilation to succeed for Razor files where the token isn't replaced.
|
||||
|
|
@ -143,7 +136,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new Pass(builder.DesignTime));
|
||||
builder.Features.Add(new Pass());
|
||||
return builder;
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||
{
|
||||
internal class MvcImportProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
|
||||
{
|
||||
private const string ImportsFileName = "_ViewImports.cshtml";
|
||||
|
||||
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var imports = new List<RazorProjectItem>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
// We add hierarchical imports second so any default directive imports can be overridden.
|
||||
AddHierarchicalImports(projectItem, imports);
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static void AddDefaultDirectivesImport(List<RazorProjectItem> imports)
|
||||
{
|
||||
imports.Add(DefaultDirectivesProjectItem.Instance);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void AddHierarchicalImports(RazorProjectItem projectItem, List<RazorProjectItem> imports)
|
||||
{
|
||||
// We want items in descending order. FindHierarchicalItems returns items in ascending order.
|
||||
var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(projectItem.FilePath, ImportsFileName).Reverse();
|
||||
imports.AddRange(importProjectItems);
|
||||
}
|
||||
|
||||
private class DefaultDirectivesProjectItem : RazorProjectItem
|
||||
{
|
||||
private readonly byte[] _defaultImportBytes;
|
||||
|
||||
private DefaultDirectivesProjectItem()
|
||||
{
|
||||
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<TModel> 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);
|
||||
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
EnsureDesignTime(builder);
|
||||
|
||||
InjectDirective.Register(builder);
|
||||
ModelDirective.Register(builder);
|
||||
|
||||
|
|
@ -37,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
builder.Features.Add(new ModelExpressionPass());
|
||||
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
||||
|
||||
builder.SetImportFeature(new DefaultMvcImportFeature());
|
||||
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||
}
|
||||
|
||||
public static void RegisterViewComponentTagHelpers(RazorProjectEngineBuilder builder)
|
||||
|
|
@ -47,22 +45,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
EnsureDesignTime(builder);
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperPass());
|
||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||
}
|
||||
|
||||
private static void EnsureDesignTime(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
if (builder.Configuration.DesignTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotSupportedException(Resources.RuntimeCodeGenerationNotSupported);
|
||||
}
|
||||
|
||||
#region Obsolete
|
||||
public static void Register(IRazorEngineBuilder builder)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var @namespace = documentNode.FindPrimaryNamespace();
|
||||
if (@namespace == null || string.IsNullOrEmpty(@namespace.Content))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
internal class DefaultMvcImportFeature : RazorProjectEngineFeatureBase, IRazorImportFeature
|
||||
{
|
||||
private const string ImportsFileName = "_ViewImports.cshtml";
|
||||
|
||||
public IReadOnlyList<RazorSourceDocument> GetImports(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var imports = new List<RazorSourceDocument>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
// We add hierarchical imports second so any default directive imports can be overridden.
|
||||
AddHierarchicalImports(projectItem, imports);
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static void AddDefaultDirectivesImport(List<RazorSourceDocument> 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<TModel> 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void AddHierarchicalImports(RazorProjectItem projectItem, List<RazorSourceDocument> 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)
|
||||
{
|
||||
RazorSourceDocument importSourceDocument;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
internal class ExtensionInitializer : RazorExtensionInitializer
|
||||
{
|
||||
public override void Initialize(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
RazorExtensions.Register(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var walker = new Visitor();
|
||||
walker.VisitDocument(documentNode);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new Pass(builder.Configuration.DesignTime));
|
||||
builder.Features.Add(new Pass());
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -71,13 +71,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
|
||||
internal class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
private readonly bool _designTime;
|
||||
|
||||
public Pass(bool designTime)
|
||||
{
|
||||
_designTime = designTime;
|
||||
}
|
||||
|
||||
// Runs after the @inherits directive
|
||||
public override int Order => 5;
|
||||
|
||||
|
|
@ -86,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var visitor = new Visitor();
|
||||
var modelType = GetModelType(documentNode, visitor);
|
||||
|
||||
if (_designTime)
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
// Alias the TModel token to a known type.
|
||||
// This allows design time compilation to succeed for Razor files where the token isn't replaced.
|
||||
|
|
@ -150,7 +143,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new Pass(builder.DesignTime));
|
||||
builder.Features.Add(new Pass());
|
||||
return builder;
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
internal class MvcImportProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
|
||||
{
|
||||
private const string ImportsFileName = "_ViewImports.cshtml";
|
||||
|
||||
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var imports = new List<RazorProjectItem>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
// We add hierarchical imports second so any default directive imports can be overridden.
|
||||
AddHierarchicalImports(projectItem, imports);
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static void AddDefaultDirectivesImport(List<RazorProjectItem> imports)
|
||||
{
|
||||
imports.Add(DefaultDirectivesProjectItem.Instance);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void AddHierarchicalImports(RazorProjectItem projectItem, List<RazorProjectItem> imports)
|
||||
{
|
||||
// We want items in descending order. FindHierarchicalItems returns items in ascending order.
|
||||
var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(projectItem.FilePath, ImportsFileName).Reverse();
|
||||
imports.AddRange(importProjectItems);
|
||||
}
|
||||
|
||||
private class DefaultDirectivesProjectItem : RazorProjectItem
|
||||
{
|
||||
private readonly byte[] _defaultImportBytes;
|
||||
|
||||
private DefaultDirectivesProjectItem()
|
||||
{
|
||||
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<TModel> 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);
|
||||
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
[assembly: ProvideRazorExtensionInitializer("MVC-2.0", typeof(ExtensionInitializer))]
|
||||
[assembly: ProvideRazorExtensionInitializer("MVC-2.1", typeof(ExtensionInitializer))]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
InheritsDirective.Register(builder);
|
||||
SectionDirective.Register(builder);
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||
|
||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||
builder.AddTargetExtension(new TemplateTargetExtension()
|
||||
{
|
||||
|
|
@ -36,14 +38,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
builder.Features.Add(new ViewComponentTagHelperPass());
|
||||
builder.Features.Add(new RazorPageDocumentClassifierPass());
|
||||
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
||||
builder.Features.Add(new AssemblyAttributeInjectionPass());
|
||||
builder.Features.Add(new InstrumentationPass());
|
||||
|
||||
if (!builder.Configuration.DesignTime)
|
||||
{
|
||||
builder.Features.Add(new AssemblyAttributeInjectionPass());
|
||||
builder.Features.Add(new InstrumentationPass());
|
||||
}
|
||||
|
||||
builder.SetImportFeature(new DefaultMvcImportFeature());
|
||||
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||
}
|
||||
|
||||
#region Obsolete
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
Set the primary configuration supported by this pacakge as the default configuration for Razor.
|
||||
-->
|
||||
<RazorDefaultConfiguration Condition="'$(RazorDefaultConfiguration)'==''">MVC-2.1</RazorDefaultConfiguration>
|
||||
|
||||
<!-- Override for testing. This path is only correct inside a nuget package. -->
|
||||
<_MvcExtensionAssemblyPath Condition="'$(_MvcExtensionAssemblyPath)'==''">$(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll</_MvcExtensionAssemblyPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -26,7 +29,7 @@
|
|||
<ItemGroup>
|
||||
<RazorExtension Include="MVC-2.1">
|
||||
<AssemblyName>Microsoft.AspNetCore.Mvc.Razor.Extensions</AssemblyName>
|
||||
<AssemblyFilePath>$(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll</AssemblyFilePath>
|
||||
<AssemblyFilePath>$(_MvcExtensionAssemblyPath)</AssemblyFilePath>
|
||||
</RazorExtension>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -5,5 +5,10 @@
|
|||
MVC will generally want to add support for runtime compilation, but only for applications.
|
||||
-->
|
||||
<GenerateRazorAssemblyInfo Condition="'$(GenerateRazorAssemblyInfo)'=='' and '$(OutputType)'=='Exe'">true</GenerateRazorAssemblyInfo>
|
||||
|
||||
<!--
|
||||
Use the suffix .Views when producing compiled view assemblies. This matches the requirements for Mvc's ViewsFeatureProvider.
|
||||
-->
|
||||
<RazorTargetNameSuffix Condition="'$(RazorTargetNameSuffix)'==''">.Views</RazorTargetNameSuffix>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -60,11 +60,11 @@
|
|||
-->
|
||||
<Touch
|
||||
Files="$(_RazorTagHelperInputCache)"
|
||||
AlwaysCreate="true">
|
||||
<Output
|
||||
TaskParameter="TouchedFiles"
|
||||
ItemName="FileWrites" />
|
||||
</Touch>
|
||||
AlwaysCreate="true" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_RazorTagHelperInputCache)" />
|
||||
</ItemGroup>
|
||||
|
||||
<RazorTagHelper
|
||||
Debug="$(_RazorDebugTagHelperTask)"
|
||||
|
|
@ -73,6 +73,9 @@
|
|||
UseServer="$(UseRazorBuildServer)"
|
||||
ForceServer="$(_RazorForceBuildServer)"
|
||||
PipeName="$(_RazorBuildServerPipeName)"
|
||||
Version="$(RazorLangVersion)"
|
||||
Configuration="@(ResolvedRazorConfiguration)"
|
||||
Extensions="@(ResolvedRazorExtension)"
|
||||
Assemblies="@(RazorReferencePath)"
|
||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||
TagHelperManifest="$(_RazorTagHelperOutputCache)">
|
||||
|
|
@ -121,6 +124,9 @@
|
|||
UseServer="$(UseRazorBuildServer)"
|
||||
ForceServer="$(_RazorForceBuildServer)"
|
||||
PipeName="$(_RazorBuildServerPipeName)"
|
||||
Version="$(RazorLangVersion)"
|
||||
Configuration="@(ResolvedRazorConfiguration)"
|
||||
Extensions="@(ResolvedRazorExtension)"
|
||||
Sources="@(RazorGenerateWithTargetPath)"
|
||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||
TagHelperManifest="$(_RazorTagHelperOutputCache)" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// 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.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class AssemblyExtension : RazorExtension
|
||||
{
|
||||
public AssemblyExtension(string extensionName, Assembly assembly)
|
||||
{
|
||||
if (extensionName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensionName));
|
||||
}
|
||||
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
ExtensionName = extensionName;
|
||||
Assembly = assembly;
|
||||
}
|
||||
|
||||
public override string ExtensionName { get; }
|
||||
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
internal RazorExtensionInitializer CreateInitializer()
|
||||
{
|
||||
// It's not an error to have an assembly with no initializers. This is useful to specify a dependency
|
||||
// that doesn't really provide any Razor configuration.
|
||||
var attributes = Assembly.GetCustomAttributes<ProvideRazorExtensionInitializerAttribute>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
// Using extension names and requiring them to line up allows a single assembly to ship multiple
|
||||
// extensions/initializers for different configurations.
|
||||
if (!string.Equals(attribute.ExtensionName, ExtensionName, StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// There's no real protection/exception handling here because this set isn't really user-extensible
|
||||
// right now. This would be a great place to add some additional diagnostics and hardening in the
|
||||
// future.
|
||||
var initializer = (RazorExtensionInitializer)Activator.CreateInstance(attribute.InitializerType);
|
||||
return initializer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -540,7 +540,7 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
public CSharpCodeWritingScope(CodeWriter writer, int tabSize = 4, bool autoSpace = true)
|
||||
{
|
||||
_writer = writer;
|
||||
_autoSpace = true;
|
||||
_autoSpace = autoSpace;
|
||||
_tabSize = tabSize;
|
||||
_startIndent = -1; // Set in WriteStartScope
|
||||
|
||||
|
|
|
|||
|
|
@ -159,11 +159,35 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
Context.CodeWriter.Write(" ");
|
||||
}
|
||||
|
||||
Context.CodeWriter
|
||||
.Write(node.ReturnType)
|
||||
.Write(" ")
|
||||
.Write(node.MethodName)
|
||||
.WriteLine("()");
|
||||
Context.CodeWriter.Write(node.ReturnType);
|
||||
Context.CodeWriter.Write(" ");
|
||||
|
||||
Context.CodeWriter.Write(node.MethodName);
|
||||
Context.CodeWriter.Write("(");
|
||||
|
||||
for (var i = 0; i < node.Parameters.Count; i++)
|
||||
{
|
||||
var parameter = node.Parameters[i];
|
||||
|
||||
for (var j = 0; j < parameter.Modifiers.Count; j++)
|
||||
{
|
||||
Context.CodeWriter.Write(parameter.Modifiers[j]);
|
||||
Context.CodeWriter.Write(" ");
|
||||
}
|
||||
|
||||
Context.CodeWriter.Write(parameter.TypeName);
|
||||
Context.CodeWriter.Write(" ");
|
||||
|
||||
Context.CodeWriter.Write(parameter.ParameterName);
|
||||
|
||||
if (i < node.Parameters.Count - 1)
|
||||
{
|
||||
Context.CodeWriter.Write(", ");
|
||||
}
|
||||
}
|
||||
|
||||
Context.CodeWriter.Write(")");
|
||||
Context.CodeWriter.WriteLine();
|
||||
|
||||
using (Context.CodeWriter.BuildScope())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,12 +2,30 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public abstract class DocumentWriter
|
||||
{
|
||||
public static DocumentWriter CreateDefault(CodeTarget codeTarget, RazorCodeGenerationOptions options)
|
||||
{
|
||||
if (codeTarget == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(codeTarget));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
return new DefaultDocumentWriter(codeTarget, options);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("This method was intended to be static, use CreateDefault instead.")]
|
||||
public DocumentWriter Create(CodeTarget codeTarget, RazorCodeGenerationOptions options)
|
||||
{
|
||||
if (codeTarget == null)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class DefaultRazorImportFeature : RazorProjectEngineFeatureBase, IRazorImportFeature
|
||||
internal class DefaultImportProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
|
||||
{
|
||||
public IReadOnlyList<RazorSourceDocument> GetImports(RazorProjectItem projectItem) => Array.Empty<RazorSourceDocument>();
|
||||
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem) => Array.Empty<RazorProjectItem>();
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var writer = new DefaultDocumentWriter(documentNode.Target, documentNode.Options);
|
||||
var writer = DocumentWriter.CreateDefault(documentNode.Target, documentNode.Options);
|
||||
var cSharpDocument = writer.WriteDocument(codeDocument, documentNode);
|
||||
codeDocument.SetCSharpDocument(cSharpDocument);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,30 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class DefaultRazorCodeGenerationOptionsBuilder : RazorCodeGenerationOptionsBuilder
|
||||
{
|
||||
public DefaultRazorCodeGenerationOptionsBuilder(bool designTime)
|
||||
private bool _designTime;
|
||||
|
||||
public DefaultRazorCodeGenerationOptionsBuilder(RazorConfiguration configuration)
|
||||
{
|
||||
DesignTime = designTime;
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DesignTime { get; }
|
||||
public DefaultRazorCodeGenerationOptionsBuilder(bool designTime)
|
||||
{
|
||||
_designTime = designTime;
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
public override bool DesignTime => _designTime;
|
||||
|
||||
public override int IndentSize { get; set; } = 4;
|
||||
|
||||
|
|
@ -22,5 +36,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
return new DefaultRazorCodeGenerationOptions(IndentWithTabs, IndentSize, DesignTime, SuppressChecksum, SuppressMetadataAttributes);
|
||||
}
|
||||
|
||||
public override void SetDesignTime(bool designTime)
|
||||
{
|
||||
_designTime = designTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class DefaultRazorCodeGenerationOptionsFactoryProjectFeature : RazorProjectEngineFeatureBase, IRazorCodeGenerationOptionsFactoryProjectFeature
|
||||
{
|
||||
private IConfigureRazorCodeGenerationOptionsFeature[] _configureOptions;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_configureOptions = ProjectEngine.EngineFeatures.OfType<IConfigureRazorCodeGenerationOptionsFeature>().ToArray();
|
||||
}
|
||||
|
||||
public RazorCodeGenerationOptions Create(Action<RazorCodeGenerationOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new DefaultRazorCodeGenerationOptionsBuilder(ProjectEngine.Configuration);
|
||||
configure?.Invoke(builder);
|
||||
|
||||
for (var i = 0; i < _configureOptions.Length; i++)
|
||||
{
|
||||
_configureOptions[i].Configure(builder);
|
||||
}
|
||||
|
||||
var options = builder.Build();
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ using System.Linq;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
internal class DefaultRazorCodeGenerationOptionsFeature : RazorEngineFeatureBase, IRazorCodeGenerationOptionsFeature
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
{
|
||||
private readonly bool _designTime;
|
||||
private IConfigureRazorCodeGenerationOptionsFeature[] _configureOptions;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.Language.Legacy;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
internal class DefaultRazorIntermediateNodeLoweringPhase : RazorEnginePhaseBase, IRazorIntermediateNodeLoweringPhase
|
||||
{
|
||||
private IRazorCodeGenerationOptionsFeature _optionsFeature;
|
||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
var document = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
|
||||
document.Options = _optionsFeature.GetOptions();
|
||||
document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions();
|
||||
|
||||
var namespaces = new Dictionary<string, SourceSpan?>(StringComparer.Ordinal);
|
||||
|
||||
|
|
@ -785,4 +786,5 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
private static bool IsMalformed(List<RazorDiagnostic> diagnostics)
|
||||
=> diagnostics.Count > 0 && diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,23 +9,43 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
internal class DefaultRazorParserOptionsBuilder : RazorParserOptionsBuilder
|
||||
{
|
||||
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
|
||||
private bool _designTime;
|
||||
|
||||
public DefaultRazorParserOptionsBuilder(RazorConfiguration configuration)
|
||||
{
|
||||
DesignTime = designTime;
|
||||
Version = version;
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
Configuration = configuration;
|
||||
LanguageVersion = configuration.LanguageVersion;
|
||||
}
|
||||
|
||||
public override bool DesignTime { get; }
|
||||
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
|
||||
{
|
||||
_designTime = designTime;
|
||||
LanguageVersion = version;
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
public override bool DesignTime => _designTime;
|
||||
|
||||
public override ICollection<DirectiveDescriptor> Directives { get; } = new List<DirectiveDescriptor>();
|
||||
|
||||
public override bool ParseLeadingDirectives { get; set; }
|
||||
|
||||
public override RazorLanguageVersion Version { get; }
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public override RazorParserOptions Build()
|
||||
{
|
||||
return new DefaultRazorParserOptions(Directives.ToArray(), DesignTime, ParseLeadingDirectives, Version);
|
||||
return new DefaultRazorParserOptions(Directives.ToArray(), DesignTime, ParseLeadingDirectives, LanguageVersion);
|
||||
}
|
||||
|
||||
public override void SetDesignTime(bool designTime)
|
||||
{
|
||||
_designTime = designTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class DefaultRazorParserOptionsFactoryProjectFeature : RazorProjectEngineFeatureBase, IRazorParserOptionsFactoryProjectFeature
|
||||
{
|
||||
private IConfigureRazorParserOptionsFeature[] _configureOptions;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_configureOptions = ProjectEngine.EngineFeatures.OfType<IConfigureRazorParserOptionsFeature>().ToArray();
|
||||
}
|
||||
|
||||
public RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new DefaultRazorParserOptionsBuilder(ProjectEngine.Configuration);
|
||||
configure?.Invoke(builder);
|
||||
|
||||
for (var i = 0; i < _configureOptions.Length; i++)
|
||||
{
|
||||
_configureOptions[i].Configure(builder);
|
||||
}
|
||||
|
||||
var options = builder.Build();
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ using System.Linq;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
internal class DefaultRazorParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
{
|
||||
private readonly bool _designTime;
|
||||
private readonly RazorLanguageVersion _version;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
internal class DefaultRazorParsingPhase : RazorEnginePhaseBase, IRazorParsingPhase
|
||||
{
|
||||
private IRazorParserOptionsFeature _optionsFeature;
|
||||
|
|
@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var options = _optionsFeature.GetOptions();
|
||||
var options = codeDocument.GetParserOptions() ??_optionsFeature.GetOptions();
|
||||
var syntaxTree = RazorSyntaxTree.Parse(codeDocument.Source, options);
|
||||
codeDocument.SetSyntaxTree(syntaxTree);
|
||||
|
||||
|
|
@ -26,4 +27,5 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
codeDocument.SetImportSyntaxTrees(importSyntaxTrees);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,16 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
internal class DefaultRazorProjectEngine : RazorProjectEngine
|
||||
{
|
||||
public DefaultRazorProjectEngine(
|
||||
RazorConfiguration configuration,
|
||||
RazorEngine engine,
|
||||
RazorProjectFileSystem fileSystem,
|
||||
IReadOnlyList<IRazorProjectEngineFeature> features)
|
||||
IReadOnlyList<IRazorProjectEngineFeature> projectFeatures)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (engine == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engine));
|
||||
|
|
@ -24,48 +30,81 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
if (features == null)
|
||||
if (projectFeatures == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(features));
|
||||
throw new ArgumentNullException(nameof(projectFeatures));
|
||||
}
|
||||
|
||||
Configuration = configuration;
|
||||
Engine = engine;
|
||||
FileSystem = fileSystem;
|
||||
Features = features;
|
||||
ProjectFeatures = projectFeatures;
|
||||
|
||||
for (var i = 0; i < features.Count; i++)
|
||||
for (var i = 0; i < projectFeatures.Count; i++)
|
||||
{
|
||||
features[i].ProjectEngine = this;
|
||||
projectFeatures[i].ProjectEngine = this;
|
||||
}
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
public override RazorProjectFileSystem FileSystem { get; }
|
||||
|
||||
public override RazorEngine Engine { get; }
|
||||
|
||||
public override IReadOnlyList<IRazorProjectEngineFeature> Features { get; }
|
||||
public override IReadOnlyList<IRazorProjectEngineFeature> ProjectFeatures { get; }
|
||||
|
||||
public override RazorCodeDocument Process(RazorProjectItem projectItem)
|
||||
protected override RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var importFeature = GetRequiredFeature<IRazorImportFeature>();
|
||||
var imports = importFeature.GetImports(projectItem);
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, imports);
|
||||
var importFeature = GetRequiredFeature<IImportProjectFeature>();
|
||||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems);
|
||||
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureParserOptions);
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(ConfigureCodeGenerationOptions);
|
||||
|
||||
return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
}
|
||||
|
||||
protected override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var importFeature = GetRequiredFeature<IImportProjectFeature>();
|
||||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems);
|
||||
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeParserOptions);
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeCodeGenerationOptions);
|
||||
|
||||
return RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
}
|
||||
|
||||
protected override void ProcessCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
if (codeDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(codeDocument));
|
||||
}
|
||||
|
||||
Engine.Process(codeDocument);
|
||||
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
private TFeature GetRequiredFeature<TFeature>() where TFeature : IRazorProjectEngineFeature
|
||||
{
|
||||
var feature = Features.OfType<TFeature>().FirstOrDefault();
|
||||
var feature = ProjectFeatures.OfType<TFeature>().FirstOrDefault();
|
||||
if (feature == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
|
|
@ -76,5 +115,43 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
return feature;
|
||||
}
|
||||
|
||||
private void ConfigureParserOptions(RazorParserOptionsBuilder builder)
|
||||
{
|
||||
}
|
||||
|
||||
private void ConfigureDesignTimeParserOptions(RazorParserOptionsBuilder builder)
|
||||
{
|
||||
builder.SetDesignTime(true);
|
||||
}
|
||||
|
||||
private void ConfigureCodeGenerationOptions(RazorCodeGenerationOptionsBuilder builder)
|
||||
{
|
||||
}
|
||||
|
||||
private void ConfigureDesignTimeCodeGenerationOptions(RazorCodeGenerationOptionsBuilder builder)
|
||||
{
|
||||
builder.SetDesignTime(true);
|
||||
builder.SuppressChecksum = true;
|
||||
builder.SuppressMetadataAttributes = true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static IReadOnlyList<RazorSourceDocument> GetImportSourceDocuments(IReadOnlyList<RazorProjectItem> importItems)
|
||||
{
|
||||
var imports = new List<RazorSourceDocument>();
|
||||
for (var i = 0; i < importItems.Count; i++)
|
||||
{
|
||||
var importItem = importItems[i];
|
||||
|
||||
if (importItem.Exists)
|
||||
{
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(importItem);
|
||||
imports.Add(sourceDocument);
|
||||
}
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,29 +22,19 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
Phases = new List<IRazorEnginePhase>();
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
public override RazorProjectFileSystem FileSystem { get; }
|
||||
|
||||
public override ICollection<IRazorFeature> Features { get; }
|
||||
|
||||
public override IList<IRazorEnginePhase> Phases { get; }
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
|
||||
public override RazorProjectEngine Build()
|
||||
{
|
||||
RazorEngine engine = null;
|
||||
|
||||
if (Configuration.DesignTime)
|
||||
{
|
||||
engine = RazorEngine.CreateDesignTimeEmpty(ConfigureRazorEngine);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine = RazorEngine.CreateEmpty(ConfigureRazorEngine);
|
||||
}
|
||||
|
||||
var projectEngineFeatures = Features.OfType<IRazorProjectEngineFeature>().ToArray();
|
||||
var projectEngine = new DefaultRazorProjectEngine(engine, FileSystem, projectEngineFeatures);
|
||||
var engine = RazorEngine.CreateEmpty(ConfigureRazorEngine);
|
||||
var projectFeatures = Features.OfType<IRazorProjectEngineFeature>().ToArray();
|
||||
var projectEngine = new DefaultRazorProjectEngine(Configuration, engine, FileSystem, projectFeatures);
|
||||
|
||||
return projectEngine;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class EmptyProjectFileSystem : RazorProjectFileSystem
|
||||
{
|
||||
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
|
||||
{
|
||||
NormalizeAndEnsureValidPath(basePath);
|
||||
return Enumerable.Empty<RazorProjectItem>();
|
||||
}
|
||||
|
||||
public override RazorProjectItem GetItem(string path)
|
||||
{
|
||||
NormalizeAndEnsureValidPath(path);
|
||||
return new NotFoundProjectItem(string.Empty, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
{
|
||||
private static readonly string[] PrivateModifiers = new string[] { "private" };
|
||||
|
||||
public bool DesignTime { get; set; }
|
||||
|
||||
public string RunnerVariableName { get; set; } = "__tagHelperRunner";
|
||||
|
||||
public string StringValueBufferVariableName { get; set; } = "__tagHelperStringValueBuffer";
|
||||
|
|
@ -82,7 +80,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (DesignTime)
|
||||
if (context.Options.DesignTime)
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
}
|
||||
|
|
@ -136,7 +134,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
.Write(CreateTagHelperMethodName)
|
||||
.WriteLine("<global::" + node.TypeName + ">();");
|
||||
|
||||
if (!DesignTime)
|
||||
if (!context.Options.DesignTime)
|
||||
{
|
||||
context.CodeWriter.WriteInstanceMethodInvocation(
|
||||
ExecutionContextVariableName,
|
||||
|
|
@ -153,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (!DesignTime)
|
||||
if (!context.Options.DesignTime)
|
||||
{
|
||||
context.CodeWriter
|
||||
.Write("await ")
|
||||
|
|
@ -200,7 +198,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (DesignTime)
|
||||
if (context.Options.DesignTime)
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
}
|
||||
|
|
@ -284,7 +282,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (!DesignTime)
|
||||
if (!context.Options.DesignTime)
|
||||
{
|
||||
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
|
||||
if (node.IsIndexerNameMatch &&
|
||||
|
|
@ -338,7 +336,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
// If we get there, this is the first time seeing this property so we need to evaluate the expression.
|
||||
if (node.BoundAttribute.ExpectsStringValue(node.AttributeName))
|
||||
{
|
||||
if (DesignTime)
|
||||
if (context.Options.DesignTime)
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
|
||||
|
|
@ -370,7 +368,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
else
|
||||
{
|
||||
if (DesignTime)
|
||||
if (context.Options.DesignTime)
|
||||
{
|
||||
var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null) as IntermediateNode;
|
||||
var valueStart = firstMappedChild?.Source;
|
||||
|
|
@ -456,7 +454,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
}
|
||||
|
||||
if (!DesignTime)
|
||||
if (!context.Options.DesignTime)
|
||||
{
|
||||
// We need to inform the context of the attribute value.
|
||||
context.CodeWriter
|
||||
|
|
@ -474,7 +472,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperRuntime(CodeRenderingContext context, DefaultTagHelperRuntimeIntermediateNode node)
|
||||
{
|
||||
if (!DesignTime)
|
||||
if (!context.Options.DesignTime)
|
||||
{
|
||||
context.CodeWriter.WriteLine("#line hidden");
|
||||
|
||||
|
|
@ -566,7 +564,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
else if (node is IntermediateToken token)
|
||||
{
|
||||
if (DesignTime && node.Source != null)
|
||||
if (context.Options.DesignTime && node.Source != null)
|
||||
{
|
||||
context.AddSourceMappingFor(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
// Only supports design time. This pass rewrites directives so they will have the right design time
|
||||
// behavior and would break things if it ran for runtime.
|
||||
if (!documentNode.Options.DesignTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var walker = new DesignTimeHelperWalker();
|
||||
walker.VisitDocument(documentNode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
// There's no value in executing this pass at design time, it just prevents some allocations.
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var walker = new PreallocatedTagHelperWalker();
|
||||
walker.VisitDocument(documentNode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public interface IRazorImportFeature : IRazorProjectEngineFeature
|
||||
public interface IImportProjectFeature : IRazorProjectEngineFeature
|
||||
{
|
||||
IReadOnlyList<RazorSourceDocument> GetImports(RazorProjectItem projectItem);
|
||||
IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal interface IRazorCodeGenerationOptionsFactoryProjectFeature : IRazorProjectEngineFeature
|
||||
{
|
||||
RazorCodeGenerationOptions Create(Action<RazorCodeGenerationOptionsBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
[Obsolete("In Razor 2.1 and newer, use RazorCodeDocument.GetCodeGenerationOptions().")]
|
||||
public interface IRazorCodeGenerationOptionsFeature : IRazorEngineFeature
|
||||
{
|
||||
RazorCodeGenerationOptions GetOptions();
|
||||
|
|
|
|||
|
|
@ -0,0 +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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal interface IRazorParserOptionsFactoryProjectFeature : IRazorProjectEngineFeature
|
||||
{
|
||||
RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
[Obsolete("In Razor 2.1 and newer, use RazorCodeDocument.GetParserOptions().")]
|
||||
public interface IRazorParserOptionsFeature : IRazorEngineFeature
|
||||
{
|
||||
RazorParserOptions GetOptions();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
|
||||
public string MethodName { get; set; }
|
||||
|
||||
public IList<MethodParameter> Parameters { get; } = new List<MethodParameter>();
|
||||
|
||||
public string ReturnType { get; set; }
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public sealed class MethodParameter
|
||||
{
|
||||
public IList<string> Modifiers { get; } = new List<string>();
|
||||
|
||||
public string TypeName { get; set; }
|
||||
|
||||
public string ParameterName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.GenerateTool, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
|
||||
public class ProvideRazorExtensionInitializerAttribute : Attribute
|
||||
{
|
||||
public ProvideRazorExtensionInitializerAttribute(string extensionName, Type initializerType)
|
||||
{
|
||||
if (extensionName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensionName));
|
||||
}
|
||||
|
||||
if (initializerType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(initializerType));
|
||||
}
|
||||
|
||||
ExtensionName = extensionName;
|
||||
InitializerType = initializerType;
|
||||
}
|
||||
|
||||
public string ExtensionName { get; }
|
||||
|
||||
public Type InitializerType { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,23 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return new DefaultRazorCodeDocument(source, imports);
|
||||
}
|
||||
|
||||
public static RazorCodeDocument Create(
|
||||
RazorSourceDocument source,
|
||||
IEnumerable<RazorSourceDocument> imports,
|
||||
RazorParserOptions parserOptions,
|
||||
RazorCodeGenerationOptions codeGenerationOptions)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = new DefaultRazorCodeDocument(source, imports);
|
||||
codeDocument.SetParserOptions(parserOptions);
|
||||
codeDocument.SetCodeGenerationOptions(codeGenerationOptions);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
public abstract IReadOnlyList<RazorSourceDocument> Imports { get; }
|
||||
|
||||
public abstract ItemCollection Items { get; }
|
||||
|
|
|
|||
|
|
@ -109,6 +109,46 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
document.Items[typeof(RazorCSharpDocument)] = csharp;
|
||||
}
|
||||
|
||||
public static RazorParserOptions GetParserOptions(this RazorCodeDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
return (RazorParserOptions)document.Items[typeof(RazorParserOptions)];
|
||||
}
|
||||
|
||||
public static void SetParserOptions(this RazorCodeDocument document, RazorParserOptions parserOptions)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
document.Items[typeof(RazorParserOptions)] = parserOptions;
|
||||
}
|
||||
|
||||
public static RazorCodeGenerationOptions GetCodeGenerationOptions(this RazorCodeDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
return (RazorCodeGenerationOptions)document.Items[typeof(RazorCodeGenerationOptions)];
|
||||
}
|
||||
|
||||
public static void SetCodeGenerationOptions(this RazorCodeDocument document, RazorCodeGenerationOptions codeGenerationOptions)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
document.Items[typeof(RazorCodeGenerationOptions)] = codeGenerationOptions;
|
||||
}
|
||||
|
||||
private class ImportSyntaxTreesHolder
|
||||
{
|
||||
public ImportSyntaxTreesHolder(IReadOnlyList<RazorSyntaxTree> syntaxTrees)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
public abstract class RazorCodeGenerationOptionsBuilder
|
||||
{
|
||||
public virtual RazorConfiguration Configuration => null;
|
||||
|
||||
public abstract bool DesignTime { get; }
|
||||
|
||||
public abstract int IndentSize { get; set; }
|
||||
|
|
@ -41,5 +43,9 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
public virtual bool SuppressMetadataAttributes { get; set; }
|
||||
|
||||
public abstract RazorCodeGenerationOptions Build();
|
||||
|
||||
public virtual void SetDesignTime(bool designTime)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,22 +12,12 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
public static readonly RazorConfiguration Default = new RazorConfiguration(
|
||||
RazorLanguageVersion.Latest,
|
||||
"unnamed",
|
||||
Array.Empty<RazorExtension>(),
|
||||
designTime: false);
|
||||
|
||||
// This is used only in some back-compat scenarios. We don't expose it because there's no
|
||||
// use case for anyone else to use it.
|
||||
internal static readonly RazorConfiguration DefaultDesignTime = new RazorConfiguration(
|
||||
RazorLanguageVersion.Latest,
|
||||
"unnamed",
|
||||
Array.Empty<RazorExtension>(),
|
||||
designTime: true);
|
||||
Array.Empty<RazorExtension>());
|
||||
|
||||
public RazorConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
string configurationName,
|
||||
IEnumerable<RazorExtension> extensions,
|
||||
bool designTime)
|
||||
IEnumerable<RazorExtension> extensions)
|
||||
{
|
||||
if (languageVersion == null)
|
||||
{
|
||||
|
|
@ -47,7 +37,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
LanguageVersion = languageVersion;
|
||||
ConfigurationName = configurationName;
|
||||
Extensions = extensions.ToArray();
|
||||
DesignTime = designTime;
|
||||
}
|
||||
|
||||
public string ConfigurationName { get; }
|
||||
|
|
@ -55,7 +44,5 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
public IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
|
||||
public RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public bool DesignTime { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,27 +17,27 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return Create(configure: null);
|
||||
}
|
||||
|
||||
public static RazorEngine Create(Action<IRazorEngineBuilder> configure) => CreateCore(RazorConfiguration.Default, configure);
|
||||
public static RazorEngine Create(Action<IRazorEngineBuilder> configure) => CreateCore(RazorConfiguration.Default, false, configure);
|
||||
|
||||
public static RazorEngine CreateDesignTime()
|
||||
{
|
||||
return CreateDesignTime(configure: null);
|
||||
}
|
||||
|
||||
public static RazorEngine CreateDesignTime(Action<IRazorEngineBuilder> configure) => CreateCore(RazorConfiguration.DefaultDesignTime, configure);
|
||||
public static RazorEngine CreateDesignTime(Action<IRazorEngineBuilder> configure) => CreateCore(RazorConfiguration.Default, true, configure);
|
||||
|
||||
// Internal since RazorEngine APIs are going to be obsolete.
|
||||
internal static RazorEngine CreateCore(RazorConfiguration configuration, Action<IRazorEngineBuilder> configure)
|
||||
internal static RazorEngine CreateCore(RazorConfiguration configuration, bool designTime, Action<IRazorEngineBuilder> configure)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
var builder = new DefaultRazorEngineBuilder(configuration.DesignTime);
|
||||
var builder = new DefaultRazorEngineBuilder(designTime);
|
||||
AddDefaults(builder);
|
||||
|
||||
if (configuration.DesignTime)
|
||||
if (designTime)
|
||||
{
|
||||
AddDefaultDesignTimeFeatures(configuration, builder.Features);
|
||||
}
|
||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
var targetExtension = features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
||||
Debug.Assert(targetExtension != null);
|
||||
|
||||
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension() { DesignTime = false });
|
||||
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension());
|
||||
targetExtension.TargetExtensions.Add(new PreallocatedAttributeTargetExtension());
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
var targetExtension = features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
||||
Debug.Assert(targetExtension != null);
|
||||
|
||||
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension() { DesignTime = true });
|
||||
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension());
|
||||
targetExtension.TargetExtensions.Add(new DesignTimeDirectiveTargetExtension());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
// 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
|
||||
{
|
||||
public abstract class RazorExtensionInitializer
|
||||
{
|
||||
public abstract void Initialize(RazorProjectEngineBuilder builder);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,26 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public abstract class RazorParserOptionsBuilder
|
||||
{
|
||||
public virtual RazorConfiguration Configuration => null;
|
||||
|
||||
public abstract bool DesignTime { get; }
|
||||
|
||||
public abstract ICollection<DirectiveDescriptor> Directives { get; }
|
||||
|
||||
public abstract bool ParseLeadingDirectives { get; set; }
|
||||
|
||||
public virtual RazorLanguageVersion Version { get; }
|
||||
public virtual RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public abstract RazorParserOptions Build();
|
||||
|
||||
public virtual void SetDesignTime(bool designTime)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,26 +3,61 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public abstract class RazorProjectEngine
|
||||
{
|
||||
public abstract RazorConfiguration Configuration { get; }
|
||||
|
||||
public abstract RazorProjectFileSystem FileSystem { get; }
|
||||
|
||||
public abstract RazorEngine Engine { get; }
|
||||
|
||||
public abstract IReadOnlyList<IRazorProjectEngineFeature> Features { get; }
|
||||
public IReadOnlyList<IRazorEngineFeature> EngineFeatures => Engine.Features;
|
||||
|
||||
public abstract RazorCodeDocument Process(RazorProjectItem projectItem);
|
||||
public IReadOnlyList<IRazorEnginePhase> Phases => Engine.Phases;
|
||||
|
||||
public static RazorProjectEngine Create(RazorProjectFileSystem fileSystem) => Create(fileSystem, configure: null);
|
||||
public abstract IReadOnlyList<IRazorProjectEngineFeature> ProjectFeatures { get; }
|
||||
|
||||
public static RazorProjectEngine Create(RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure) => Create(fileSystem, RazorConfiguration.Default, configure);
|
||||
public virtual RazorCodeDocument Process(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentCore(projectItem);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
public virtual RazorCodeDocument ProcessDesignTime(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentDesignTimeCore(projectItem);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected abstract RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem);
|
||||
|
||||
protected abstract RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem);
|
||||
|
||||
protected abstract void ProcessCore(RazorCodeDocument codeDocument);
|
||||
|
||||
public static RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem) => Create(configuration, fileSystem, configure: null);
|
||||
|
||||
public static RazorProjectEngine Create(
|
||||
RazorProjectFileSystem fileSystem,
|
||||
RazorConfiguration configuration,
|
||||
RazorProjectFileSystem fileSystem,
|
||||
Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (fileSystem == null)
|
||||
|
|
@ -37,54 +72,96 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
var builder = new DefaultRazorProjectEngineBuilder(configuration, fileSystem);
|
||||
|
||||
AddDefaults(builder);
|
||||
// The intialization order is somewhat important.
|
||||
//
|
||||
// Defaults -> Extensions -> Additional customization
|
||||
//
|
||||
// This allows extensions to rely on default features, and customizations to override choices made by
|
||||
// extensions.
|
||||
RazorEngine.AddDefaultPhases(builder.Phases);
|
||||
AddDefaultsFeatures(builder.Features);
|
||||
|
||||
if (configuration.DesignTime)
|
||||
{
|
||||
AddDesignTimeDefaults(builder);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeDefaults(builder);
|
||||
}
|
||||
LoadExtensions(builder, configuration.Extensions);
|
||||
|
||||
configure?.Invoke(builder);
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private static void AddDefaults(RazorProjectEngineBuilder builder)
|
||||
|
||||
private static void AddDefaultsFeatures(ICollection<IRazorFeature> features)
|
||||
{
|
||||
builder.Features.Add(new DefaultRazorImportFeature());
|
||||
}
|
||||
features.Add(new DefaultImportProjectFeature());
|
||||
|
||||
private static void AddDesignTimeDefaults(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
var engineFeatures = new List<IRazorEngineFeature>();
|
||||
RazorEngine.AddDefaultFeatures(engineFeatures);
|
||||
RazorEngine.AddDefaultDesignTimeFeatures(builder.Configuration, engineFeatures);
|
||||
// General extensibility
|
||||
features.Add(new DefaultRazorDirectiveFeature());
|
||||
features.Add(new DefaultMetadataIdentifierFeature());
|
||||
|
||||
AddEngineFeaturesAndPhases(builder, engineFeatures);
|
||||
}
|
||||
// Options features
|
||||
features.Add(new DefaultRazorParserOptionsFactoryProjectFeature());
|
||||
features.Add(new DefaultRazorCodeGenerationOptionsFactoryProjectFeature());
|
||||
|
||||
private static void AddRuntimeDefaults(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
var engineFeatures = new List<IRazorEngineFeature>();
|
||||
RazorEngine.AddDefaultFeatures(engineFeatures);
|
||||
RazorEngine.AddDefaultRuntimeFeatures(builder.Configuration, engineFeatures);
|
||||
// Legacy options features
|
||||
//
|
||||
// These features are obsolete as of 2.1. Our code will resolve this but not invoke them.
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Version_2_0));
|
||||
features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
|
||||
AddEngineFeaturesAndPhases(builder, engineFeatures);
|
||||
}
|
||||
// Syntax Tree passes
|
||||
features.Add(new DefaultDirectiveSyntaxTreePass());
|
||||
features.Add(new HtmlNodeOptimizationPass());
|
||||
features.Add(new PreallocatedTagHelperAttributeOptimizationPass());
|
||||
|
||||
private static void AddEngineFeaturesAndPhases(RazorProjectEngineBuilder builder, IReadOnlyList<IRazorEngineFeature> engineFeatures)
|
||||
{
|
||||
for (var i = 0; i < engineFeatures.Count; i++)
|
||||
// Intermediate Node Passes
|
||||
features.Add(new DefaultDocumentClassifierPass());
|
||||
features.Add(new MetadataAttributePass());
|
||||
features.Add(new DesignTimeDirectivePass());
|
||||
features.Add(new DirectiveRemovalOptimizationPass());
|
||||
features.Add(new DefaultTagHelperOptimizationPass());
|
||||
|
||||
// Default Code Target Extensions
|
||||
var targetExtensionFeature = new DefaultRazorTargetExtensionFeature();
|
||||
features.Add(targetExtensionFeature);
|
||||
targetExtensionFeature.TargetExtensions.Add(new MetadataAttributeTargetExtension());
|
||||
targetExtensionFeature.TargetExtensions.Add(new DefaultTagHelperTargetExtension());
|
||||
targetExtensionFeature.TargetExtensions.Add(new PreallocatedAttributeTargetExtension());
|
||||
targetExtensionFeature.TargetExtensions.Add(new DesignTimeDirectiveTargetExtension());
|
||||
|
||||
// Default configuration
|
||||
var configurationFeature = new DefaultDocumentClassifierPassFeature();
|
||||
features.Add(configurationFeature);
|
||||
configurationFeature.ConfigureClass.Add((document, @class) =>
|
||||
{
|
||||
var engineFeature = engineFeatures[i];
|
||||
builder.Features.Add(engineFeature);
|
||||
}
|
||||
@class.ClassName = "Template";
|
||||
@class.Modifiers.Add("public");
|
||||
});
|
||||
|
||||
RazorEngine.AddDefaultPhases(builder.Phases);
|
||||
configurationFeature.ConfigureNamespace.Add((document, @namespace) =>
|
||||
{
|
||||
@namespace.Content = "Razor";
|
||||
});
|
||||
|
||||
configurationFeature.ConfigureMethod.Add((document, method) =>
|
||||
{
|
||||
method.MethodName = "ExecuteAsync";
|
||||
method.ReturnType = $"global::{typeof(Task).FullName}";
|
||||
|
||||
method.Modifiers.Add("public");
|
||||
method.Modifiers.Add("async");
|
||||
method.Modifiers.Add("override");
|
||||
});
|
||||
}
|
||||
|
||||
private static void LoadExtensions(RazorProjectEngineBuilder builder, IReadOnlyList<RazorExtension> extensions)
|
||||
{
|
||||
for (var i = 0; i < extensions.Count; i++)
|
||||
{
|
||||
// For now we only handle AssemblyExtension - which is not user-constructable. We're keeping a tight
|
||||
// lid on how things work until we add official support for extensibility everywhere. So, this is
|
||||
// intentionally inflexible for the time being.
|
||||
var extension = extensions[i] as AssemblyExtension;
|
||||
var initializer = extension?.CreateInitializer();
|
||||
initializer?.Initialize(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
public abstract class RazorProjectEngineBuilder
|
||||
{
|
||||
public abstract RazorConfiguration Configuration { get; }
|
||||
|
||||
public abstract RazorProjectFileSystem FileSystem { get; }
|
||||
|
||||
public abstract ICollection<IRazorFeature> Features { get; }
|
||||
|
||||
public abstract IList<IRazorEnginePhase> Phases { get; }
|
||||
|
||||
public abstract RazorConfiguration Configuration { get; }
|
||||
|
||||
public abstract RazorProjectEngine Build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,80 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public static class RazorProjectEngineBuilderExtensions
|
||||
{
|
||||
public static void SetImportFeature(this RazorProjectEngineBuilder builder, IRazorImportFeature feature)
|
||||
/// <summary>
|
||||
/// Registers a class configuration delegate that gets invoked during code generation.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
/// <param name="configureClass"><see cref="Action"/> invoked to configure
|
||||
/// <see cref="ClassDeclarationIntermediateNode"/> during code generation.</param>
|
||||
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
|
||||
public static RazorProjectEngineBuilder ConfigureClass(
|
||||
this RazorProjectEngineBuilder builder,
|
||||
Action<RazorCodeDocument, ClassDeclarationIntermediateNode> configureClass)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (configureClass == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureClass));
|
||||
}
|
||||
|
||||
var configurationFeature = GetDefaultDocumentClassifierPassFeature(builder);
|
||||
configurationFeature.ConfigureClass.Add(configureClass);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the base type for generated types.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
/// <param name="baseType">The name of the base type.</param>
|
||||
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
|
||||
public static RazorProjectEngineBuilder SetBaseType(this RazorProjectEngineBuilder 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the namespace for generated types.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
/// <param name="namespaceName">The name of the namespace.</param>
|
||||
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
|
||||
public static RazorProjectEngineBuilder SetNamespace(this RazorProjectEngineBuilder builder, string namespaceName)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
var configurationFeature = GetDefaultDocumentClassifierPassFeature(builder);
|
||||
configurationFeature.ConfigureNamespace.Add((document, @namespace) => @namespace.Content = namespaceName);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static void SetImportFeature(this RazorProjectEngineBuilder builder, IImportProjectFeature feature)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
|
|
@ -22,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
|
||||
// Remove any existing import features in favor of the new one we're given.
|
||||
var existingFeatures = builder.Features.OfType<IRazorImportFeature>().ToArray();
|
||||
var existingFeatures = builder.Features.OfType<IImportProjectFeature>().ToArray();
|
||||
foreach (var existingFeature in existingFeatures)
|
||||
{
|
||||
builder.Features.Remove(existingFeature);
|
||||
|
|
@ -79,6 +145,27 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided <see cref="RazorProjectItem" />s as imports to all project items processed
|
||||
/// by the <see cref="RazorProjectEngine"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
/// <param name="imports">The collection of imports.</param>
|
||||
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
|
||||
public static RazorProjectEngineBuilder AddDefaultImports(this RazorProjectEngineBuilder builder, params string[] imports)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
var existingImportFeature = builder.Features.OfType<IImportProjectFeature>().First();
|
||||
var testImportFeature = new AdditionalImportsProjectFeature(existingImportFeature, imports);
|
||||
builder.SetImportFeature(testImportFeature);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IRazorDirectiveFeature GetDirectiveFeature(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||
|
|
@ -102,5 +189,77 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
return targetExtensionFeature;
|
||||
}
|
||||
|
||||
private static DefaultDocumentClassifierPassFeature GetDefaultDocumentClassifierPassFeature(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
var configurationFeature = builder.Features.OfType<DefaultDocumentClassifierPassFeature>().FirstOrDefault();
|
||||
if (configurationFeature == null)
|
||||
{
|
||||
configurationFeature = new DefaultDocumentClassifierPassFeature();
|
||||
builder.Features.Add(configurationFeature);
|
||||
}
|
||||
|
||||
return configurationFeature;
|
||||
}
|
||||
|
||||
private class AdditionalImportsProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
|
||||
{
|
||||
private readonly IImportProjectFeature _existingImportFeature;
|
||||
private readonly IEnumerable<RazorProjectItem> _imports;
|
||||
|
||||
public override RazorProjectEngine ProjectEngine
|
||||
{
|
||||
get => base.ProjectEngine;
|
||||
set
|
||||
{
|
||||
_existingImportFeature.ProjectEngine = value;
|
||||
base.ProjectEngine = value;
|
||||
}
|
||||
}
|
||||
|
||||
public AdditionalImportsProjectFeature(IImportProjectFeature existingImportFeature, params string[] imports)
|
||||
{
|
||||
_existingImportFeature = existingImportFeature;
|
||||
_imports = imports.Select(import => new InMemoryProjectItem(import));
|
||||
}
|
||||
|
||||
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem)
|
||||
{
|
||||
var imports = _existingImportFeature.GetImports(projectItem).ToList();
|
||||
imports.AddRange(_imports);
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
private class InMemoryProjectItem : RazorProjectItem
|
||||
{
|
||||
private readonly byte[] _importBytes;
|
||||
|
||||
public InMemoryProjectItem(string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(content));
|
||||
}
|
||||
|
||||
var preamble = Encoding.UTF8.GetPreamble();
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
_importBytes = new byte[preamble.Length + contentBytes.Length];
|
||||
preamble.CopyTo(_importBytes, 0);
|
||||
contentBytes.CopyTo(_importBytes, preamble.Length);
|
||||
}
|
||||
|
||||
public override string BasePath => null;
|
||||
|
||||
public override string FilePath => null;
|
||||
|
||||
public override string PhysicalPath => null;
|
||||
|
||||
public override bool Exists => true;
|
||||
|
||||
public override Stream Read() => new MemoryStream(_importBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
public abstract class RazorProjectFileSystem : RazorProject
|
||||
{
|
||||
internal static readonly RazorProjectFileSystem Empty = new EmptyProjectFileSystem();
|
||||
|
||||
/// <summary>
|
||||
/// Create a Razor project file system based off of a root directory.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,28 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
{
|
||||
public class RazorGenerate : DotNetToolTask
|
||||
{
|
||||
private static readonly string[] SourceRequiredMetadata = new string[]
|
||||
{
|
||||
FullPath,
|
||||
GeneratedOutput,
|
||||
TargetPath,
|
||||
};
|
||||
|
||||
private const string GeneratedOutput = "GeneratedOutput";
|
||||
private const string TargetPath = "TargetPath";
|
||||
private const string FullPath = "FullPath";
|
||||
private const string Identity = "Identity";
|
||||
private const string AssemblyName = "AssemblyName";
|
||||
private const string AssemblyFilePath = "AssemblyFilePath";
|
||||
|
||||
[Required]
|
||||
public string Version { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] Configuration { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] Extensions { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] Sources { get; set; }
|
||||
|
|
@ -26,11 +45,28 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
|
||||
protected override bool ValidateParameters()
|
||||
{
|
||||
if (Configuration.Length == 0)
|
||||
{
|
||||
Log.LogError("The project {0} must provide a value for {1}.", ProjectRoot, nameof(Configuration));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < Sources.Length; i++)
|
||||
{
|
||||
if (!EnsureRequiredMetadata(Sources[i], FullPath) ||
|
||||
!EnsureRequiredMetadata(Sources[i], GeneratedOutput) ||
|
||||
!EnsureRequiredMetadata(Sources[i], TargetPath))
|
||||
{
|
||||
Log.LogError("The Razor source item '{0}' is missing a required metadata entry. Required metadata are: '{1}'", Sources[i], SourceRequiredMetadata);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < Extensions.Length; i++)
|
||||
{
|
||||
if (!EnsureRequiredMetadata(Extensions[i], Identity) ||
|
||||
!EnsureRequiredMetadata(Extensions[i], AssemblyName) ||
|
||||
!EnsureRequiredMetadata(Extensions[i], AssemblyFilePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -65,6 +101,21 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
builder.AppendLine("-t");
|
||||
builder.AppendLine(TagHelperManifest);
|
||||
|
||||
builder.AppendLine("-v");
|
||||
builder.AppendLine(Version);
|
||||
|
||||
builder.AppendLine("-c");
|
||||
builder.AppendLine(Configuration[0].GetMetadata(Identity));
|
||||
|
||||
for (var i = 0; i < Extensions.Length; i++)
|
||||
{
|
||||
builder.AppendLine("-n");
|
||||
builder.AppendLine(Extensions[i].GetMetadata(Identity));
|
||||
|
||||
builder.AppendLine("-e");
|
||||
builder.AppendLine(Path.GetFullPath(Extensions[i].GetMetadata(AssemblyFilePath)));
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,19 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
{
|
||||
public class RazorTagHelper : DotNetToolTask
|
||||
{
|
||||
private const string Identity = "Identity";
|
||||
private const string AssemblyName = "AssemblyName";
|
||||
private const string AssemblyFilePath = "AssemblyFilePath";
|
||||
|
||||
[Required]
|
||||
public string Version { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] Configuration { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] Extensions { get; set; }
|
||||
|
||||
[Required]
|
||||
public string[] Assemblies { get; set; }
|
||||
|
||||
|
|
@ -51,6 +64,21 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
builder.AppendLine("-p");
|
||||
builder.AppendLine(ProjectRoot);
|
||||
|
||||
builder.AppendLine("-v");
|
||||
builder.AppendLine(Version);
|
||||
|
||||
builder.AppendLine("-c");
|
||||
builder.AppendLine(Configuration[0].GetMetadata(Identity));
|
||||
|
||||
for (var i = 0; i < Extensions.Length; i++)
|
||||
{
|
||||
builder.AppendLine("-n");
|
||||
builder.AppendLine(Extensions[i].GetMetadata(Identity));
|
||||
|
||||
builder.AppendLine("-e");
|
||||
builder.AppendLine(Path.GetFullPath(Extensions[i].GetMetadata(AssemblyFilePath)));
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,19 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class Application : CommandLineApplication
|
||||
{
|
||||
public Application(CancellationToken cancellationToken)
|
||||
public Application(CancellationToken cancellationToken, ExtensionAssemblyLoader loader, ExtensionDependencyChecker checker, Func<string, MetadataReferenceProperties, PortableExecutableReference> assemblyReferenceProvider)
|
||||
{
|
||||
CancellationToken = cancellationToken;
|
||||
Checker = checker;
|
||||
Loader = loader;
|
||||
AssemblyReferenceProvider = assemblyReferenceProvider;
|
||||
|
||||
Name = "rzc";
|
||||
FullName = "Microsoft ASP.NET Core Razor CLI tool";
|
||||
|
|
@ -31,6 +35,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
|
||||
public CancellationToken CancellationToken { get; }
|
||||
|
||||
public ExtensionAssemblyLoader Loader { get; }
|
||||
|
||||
public ExtensionDependencyChecker Checker { get; }
|
||||
|
||||
public Func<string, MetadataReferenceProperties, PortableExecutableReference> AssemblyReferenceProvider { get; }
|
||||
|
||||
public new int Execute(params string[] args)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal sealed class CachingMetadataReference : PortableExecutableReference
|
||||
{
|
||||
private static readonly MetadataCache _metadataCache = new MetadataCache();
|
||||
|
||||
public CachingMetadataReference(string fullPath, MetadataReferenceProperties properties)
|
||||
: base(properties, fullPath)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DocumentationProvider CreateDocumentationProvider()
|
||||
{
|
||||
return DocumentationProvider.Default;
|
||||
}
|
||||
|
||||
protected override Metadata GetMetadataImpl()
|
||||
{
|
||||
return _metadataCache.GetMetadata(FilePath);
|
||||
}
|
||||
|
||||
protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties)
|
||||
{
|
||||
return new CachingMetadataReference(FilePath, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
|
|
@ -19,6 +21,25 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
|
||||
private class DefaultCompilerHost : CompilerHost
|
||||
{
|
||||
public DefaultCompilerHost()
|
||||
{
|
||||
// The loader needs to live for the lifetime of the server.
|
||||
//
|
||||
// This means that if a request tries to use a set of binaries that are inconsistent with what
|
||||
// the server already has, then it will be rejected to try again on the client.
|
||||
//
|
||||
// We also check each set of extensions for missing depenencies individually, so that we can
|
||||
// consistently reject a request that doesn't specify everything it needs. Otherwise the request
|
||||
// could succeed sometimes if it relies on transient state.
|
||||
Loader = new DefaultExtensionAssemblyLoader(Path.Combine(Path.GetTempPath(), "Razor-Server"));
|
||||
|
||||
AssemblyReferenceProvider = (path, properties) => new CachingMetadataReference(path, properties);
|
||||
}
|
||||
|
||||
public Func<string, MetadataReferenceProperties, PortableExecutableReference> AssemblyReferenceProvider { get; }
|
||||
|
||||
public ExtensionAssemblyLoader Loader { get; }
|
||||
|
||||
public override ServerResponse Execute(ServerRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!TryParseArguments(request, out var parsed))
|
||||
|
|
@ -28,28 +49,23 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
|
||||
var exitCode = 0;
|
||||
var output = string.Empty;
|
||||
var app = new Application(cancellationToken);
|
||||
var commandArgs = parsed.args.ToArray();
|
||||
|
||||
var writer = ServerLogger.IsLoggingEnabled ? new StringWriter() : TextWriter.Null;
|
||||
|
||||
var checker = new DefaultExtensionDependencyChecker(Loader, writer, writer);
|
||||
var app = new Application(cancellationToken, Loader, checker, AssemblyReferenceProvider)
|
||||
{
|
||||
Out = writer,
|
||||
Error = writer,
|
||||
};
|
||||
|
||||
exitCode = app.Execute(commandArgs);
|
||||
|
||||
if (ServerLogger.IsLoggingEnabled)
|
||||
{
|
||||
using (var writer = new StringWriter())
|
||||
{
|
||||
app.Out = writer;
|
||||
app.Error = writer;
|
||||
exitCode = app.Execute(commandArgs);
|
||||
output = writer.ToString();
|
||||
ServerLogger.Log(output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var writer = new StreamWriter(Stream.Null))
|
||||
{
|
||||
app.Out = writer;
|
||||
app.Error = writer;
|
||||
exitCode = app.Execute(commandArgs);
|
||||
}
|
||||
output = writer.ToString();
|
||||
ServerLogger.Log(output);
|
||||
}
|
||||
|
||||
return new CompletedServerResponse(exitCode, utf8output: false, output: string.Empty);
|
||||
|
|
|
|||
|
|
@ -3,24 +3,25 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class CompositeRazorProjectFileSystem : RazorProjectFileSystem
|
||||
{
|
||||
public CompositeRazorProjectFileSystem(IReadOnlyList<RazorProjectFileSystem> projects)
|
||||
public CompositeRazorProjectFileSystem(IReadOnlyList<RazorProjectFileSystem> fileSystems)
|
||||
{
|
||||
Projects = projects ?? throw new ArgumentNullException(nameof(projects));
|
||||
FileSystems = fileSystems ?? throw new ArgumentNullException(nameof(fileSystems));
|
||||
}
|
||||
|
||||
public IReadOnlyList<RazorProjectFileSystem> Projects { get; }
|
||||
public IReadOnlyList<RazorProjectFileSystem> FileSystems { get; }
|
||||
|
||||
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
|
||||
{
|
||||
foreach (var project in Projects)
|
||||
foreach (var fileSystem in FileSystems)
|
||||
{
|
||||
foreach (var result in project.EnumerateItems(basePath))
|
||||
foreach (var result in fileSystem.EnumerateItems(basePath))
|
||||
{
|
||||
yield return result;
|
||||
}
|
||||
|
|
@ -30,9 +31,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
public override RazorProjectItem GetItem(string path)
|
||||
{
|
||||
RazorProjectItem razorProjectItem = null;
|
||||
foreach (var project in Projects)
|
||||
foreach (var fileSystem in FileSystems)
|
||||
{
|
||||
razorProjectItem = project.GetItem(path);
|
||||
razorProjectItem = fileSystem.GetItem(path);
|
||||
if (razorProjectItem != null && razorProjectItem.Exists)
|
||||
{
|
||||
return razorProjectItem;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
// 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.Diagnostics;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Cache with a fixed size that evicts the least recently used members.
|
||||
/// Thread-safe.
|
||||
/// This was taken from https://github.com/dotnet/roslyn/blob/749c0ec135d7d080658dc1aa794d15229c3d10d2/src/Compilers/Core/Portable/InternalUtilities/ConcurrentLruCache.cs.
|
||||
/// </summary>
|
||||
internal class ConcurrentLruCache<TKey, TValue>
|
||||
{
|
||||
private readonly int _capacity;
|
||||
|
||||
private readonly Dictionary<TKey, CacheValue> _cache;
|
||||
private readonly LinkedList<TKey> _nodeList;
|
||||
// This is a naive course-grained lock, it can probably be optimized
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
public ConcurrentLruCache(int capacity)
|
||||
: this (capacity, EqualityComparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public ConcurrentLruCache(int capacity, IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
if (capacity <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
}
|
||||
_capacity = capacity;
|
||||
_cache = new Dictionary<TKey, CacheValue>(capacity, comparer);
|
||||
_nodeList = new LinkedList<TKey>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create cache from an array. The cache capacity will be the size
|
||||
/// of the array. All elements of the array will be added to the
|
||||
/// cache. If any duplicate keys are found in the array a
|
||||
/// <see cref="ArgumentException"/> will be thrown.
|
||||
/// </summary>
|
||||
public ConcurrentLruCache(KeyValuePair<TKey, TValue>[] array)
|
||||
: this(array.Length)
|
||||
{
|
||||
foreach (var kvp in array)
|
||||
{
|
||||
UnsafeAdd(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return _cache.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
UnsafeAdd(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TValue GetOrAdd(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (UnsafeTryGetValue(key, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
UnsafeAdd(key, value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return UnsafeTryGetValue(key, out value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return UnsafeRemove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For testing. Very expensive.
|
||||
/// </summary>
|
||||
internal IEnumerable<KeyValuePair<TKey, TValue>> TestingEnumerable
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
foreach (var key in _nodeList)
|
||||
{
|
||||
var kvp = new KeyValuePair<TKey, TValue>(key, _cache[key].Value);
|
||||
yield return kvp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't lock.
|
||||
/// </summary>
|
||||
private bool UnsafeTryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out var result))
|
||||
{
|
||||
MoveNodeToTop(result.Node);
|
||||
value = result.Value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveNodeToTop(LinkedListNode<TKey> node)
|
||||
{
|
||||
if (!object.ReferenceEquals(_nodeList.First, node))
|
||||
{
|
||||
_nodeList.Remove(node);
|
||||
_nodeList.AddFirst(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expects non-empty cache. Does not lock.
|
||||
/// </summary>
|
||||
private void UnsafeEvictLastNode()
|
||||
{
|
||||
Debug.Assert(_capacity > 0);
|
||||
var lastNode = _nodeList.Last;
|
||||
_nodeList.Remove(lastNode);
|
||||
_cache.Remove(lastNode.Value);
|
||||
}
|
||||
|
||||
private void UnsafeAddNodeToTop(TKey key, TValue value)
|
||||
{
|
||||
var node = new LinkedListNode<TKey>(key);
|
||||
_cache.Add(key, new CacheValue(value, node));
|
||||
_nodeList.AddFirst(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't lock.
|
||||
/// </summary>
|
||||
private void UnsafeAdd(TKey key, TValue value)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out var result))
|
||||
{
|
||||
throw new ArgumentException("Key already exists", nameof(key));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_cache.Count == _capacity)
|
||||
{
|
||||
UnsafeEvictLastNode();
|
||||
}
|
||||
UnsafeAddNodeToTop(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnsafeRemove(TKey key)
|
||||
{
|
||||
_nodeList.Remove(key);
|
||||
return _cache.Remove(key);
|
||||
}
|
||||
|
||||
private struct CacheValue
|
||||
{
|
||||
public CacheValue(TValue value, LinkedListNode<TKey> node)
|
||||
{
|
||||
Value = value;
|
||||
Node = node;
|
||||
}
|
||||
|
||||
public TValue Value { get; }
|
||||
|
||||
public LinkedListNode<TKey> Node { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
// 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.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Runtime.Loader;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class DefaultExtensionAssemblyLoader : ExtensionAssemblyLoader
|
||||
{
|
||||
private readonly string _baseDirectory;
|
||||
|
||||
private readonly object _lock = new object();
|
||||
private readonly Dictionary<string, (Assembly assembly, AssemblyIdentity identity)> _loadedByPath;
|
||||
private readonly Dictionary<AssemblyIdentity, Assembly> _loadedByIdentity;
|
||||
private readonly Dictionary<string, AssemblyIdentity> _identityCache;
|
||||
private readonly Dictionary<string, List<string>> _wellKnownAssemblies;
|
||||
|
||||
private ShadowCopyManager _shadowCopyManager;
|
||||
|
||||
public DefaultExtensionAssemblyLoader(string baseDirectory)
|
||||
{
|
||||
_baseDirectory = baseDirectory;
|
||||
|
||||
_loadedByPath = new Dictionary<string, (Assembly assembly, AssemblyIdentity identity)>(StringComparer.OrdinalIgnoreCase);
|
||||
_loadedByIdentity = new Dictionary<AssemblyIdentity, Assembly>();
|
||||
_identityCache = new Dictionary<string, AssemblyIdentity>(StringComparer.OrdinalIgnoreCase);
|
||||
_wellKnownAssemblies = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
LoadContext = new ExtensionAssemblyLoadContext(AssemblyLoadContext.GetLoadContext(typeof(ExtensionAssemblyLoader).Assembly), this);
|
||||
}
|
||||
|
||||
protected AssemblyLoadContext LoadContext { get; }
|
||||
|
||||
public override void AddAssemblyLocation(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(filePath))
|
||||
{
|
||||
throw new ArgumentException(nameof(filePath));
|
||||
}
|
||||
|
||||
var assemblyName = Path.GetFileNameWithoutExtension(filePath);
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_wellKnownAssemblies.TryGetValue(assemblyName, out var paths))
|
||||
{
|
||||
paths = new List<string>();
|
||||
_wellKnownAssemblies.Add(assemblyName, paths);
|
||||
}
|
||||
|
||||
if (!paths.Contains(filePath))
|
||||
{
|
||||
paths.Add(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Assembly Load(string assemblyName)
|
||||
{
|
||||
if (!AssemblyIdentity.TryParseDisplayName(assemblyName, out var identity))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// First, check if this loader already loaded the requested assembly:
|
||||
if (_loadedByIdentity.TryGetValue(identity, out var assembly))
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
|
||||
// Second, check if an assembly file of the same simple name was registered with the loader:
|
||||
if (_wellKnownAssemblies.TryGetValue(identity.Name, out var paths))
|
||||
{
|
||||
// Multiple assemblies of the same simple name but different identities might have been registered.
|
||||
// Load the one that matches the requested identity (if any).
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var candidateIdentity = GetIdentity(path);
|
||||
|
||||
if (identity.Equals(candidateIdentity))
|
||||
{
|
||||
return LoadFromPathUnsafe(path, candidateIdentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We only support loading by name from 'well-known' paths. If you need to load something by
|
||||
// name and you get here, then that means we don't know where to look.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override Assembly LoadFromPath(string filePath)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(filePath))
|
||||
{
|
||||
throw new ArgumentException(nameof(filePath));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
return LoadFromPathUnsafe(filePath, identity: null);
|
||||
}
|
||||
}
|
||||
|
||||
private Assembly LoadFromPathUnsafe(string filePath, AssemblyIdentity identity)
|
||||
{
|
||||
// If we've already loaded the assembly by path there should be nothing else to do,
|
||||
// all of our data is up to date.
|
||||
if (_loadedByPath.TryGetValue(filePath, out var entry))
|
||||
{
|
||||
return entry.assembly;
|
||||
}
|
||||
|
||||
// If we've already loaded the assembly by identity, then we might has some updating
|
||||
// to do.
|
||||
identity = identity ?? GetIdentity(filePath);
|
||||
if (identity != null && _loadedByIdentity.TryGetValue(identity, out var assembly))
|
||||
{
|
||||
// An assembly file might be replaced by another file with a different identity.
|
||||
// Last one wins.
|
||||
_loadedByPath[filePath] = (assembly, identity);
|
||||
return assembly;
|
||||
}
|
||||
|
||||
// Ok we don't have this cached. Let's actually try to load the assembly.
|
||||
assembly = LoadFromPathUnsafeCore(CopyAssembly(filePath));
|
||||
|
||||
identity = identity ?? AssemblyIdentity.FromAssemblyDefinition(assembly);
|
||||
|
||||
// It's possible an assembly was loaded by two different paths. Just use the original then.
|
||||
if (_loadedByIdentity.TryGetValue(identity, out var duplicate))
|
||||
{
|
||||
assembly = duplicate;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loadedByIdentity.Add(identity, assembly);
|
||||
}
|
||||
|
||||
_loadedByPath[filePath] = (assembly, identity);
|
||||
return assembly;
|
||||
}
|
||||
|
||||
private AssemblyIdentity GetIdentity(string filePath)
|
||||
{
|
||||
if (!_identityCache.TryGetValue(filePath, out var identity))
|
||||
{
|
||||
identity = ReadAssemblyIdentity(filePath);
|
||||
_identityCache.Add(filePath, identity);
|
||||
}
|
||||
|
||||
return identity;
|
||||
}
|
||||
|
||||
protected virtual string CopyAssembly(string filePath)
|
||||
{
|
||||
if (_baseDirectory == null)
|
||||
{
|
||||
// Don't shadow-copy when base directory is null. This means we're running as a CLI not
|
||||
// a server.
|
||||
return filePath;
|
||||
}
|
||||
|
||||
if (_shadowCopyManager == null)
|
||||
{
|
||||
_shadowCopyManager = new ShadowCopyManager(_baseDirectory);
|
||||
}
|
||||
|
||||
return _shadowCopyManager.AddAssembly(filePath);
|
||||
}
|
||||
|
||||
protected virtual Assembly LoadFromPathUnsafeCore(string filePath)
|
||||
{
|
||||
return LoadContext.LoadFromAssemblyPath(filePath);
|
||||
}
|
||||
|
||||
private static AssemblyIdentity ReadAssemblyIdentity(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
|
||||
using (var reader = new PEReader(stream))
|
||||
{
|
||||
var metadataReader = reader.GetMetadataReader();
|
||||
return metadataReader.GetAssemblyIdentity();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ExtensionAssemblyLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly AssemblyLoadContext _parent;
|
||||
private readonly DefaultExtensionAssemblyLoader _loader;
|
||||
|
||||
public ExtensionAssemblyLoadContext(AssemblyLoadContext parent, DefaultExtensionAssemblyLoader loader)
|
||||
{
|
||||
_parent = parent;
|
||||
_loader = loader;
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
// Try to load from well-known paths. This will be called when loading a dependency of an extension.
|
||||
var assembly = _loader.Load(assemblyName.ToString());
|
||||
if (assembly != null)
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
|
||||
// If we don't have an entry, then fall back to the default load context. This allows extensions
|
||||
// to resolve assemblies that are provided by the host.
|
||||
return _parent.LoadFromAssemblyName(assemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class DefaultExtensionDependencyChecker : ExtensionDependencyChecker
|
||||
{
|
||||
// These are treated as prefixes. So `Microsoft.CodeAnalysis.Razor` would be assumed to work.
|
||||
private static readonly string[] DefaultIgnoredAssemblies = new string[]
|
||||
{
|
||||
"mscorlib",
|
||||
"netstandard",
|
||||
"System",
|
||||
"Microsoft.CodeAnalysis",
|
||||
"Microsoft.AspNetCore.Razor.Language",
|
||||
};
|
||||
|
||||
private readonly ExtensionAssemblyLoader _loader;
|
||||
private readonly TextWriter _output;
|
||||
private readonly TextWriter _error;
|
||||
private readonly string[] _ignoredAssemblies;
|
||||
|
||||
public DefaultExtensionDependencyChecker(
|
||||
ExtensionAssemblyLoader loader,
|
||||
TextWriter output,
|
||||
TextWriter error,
|
||||
string[] ignoredAssemblies = null)
|
||||
{
|
||||
_loader = loader;
|
||||
_output = output;
|
||||
_error = error;
|
||||
_ignoredAssemblies = ignoredAssemblies ?? DefaultIgnoredAssemblies;
|
||||
}
|
||||
|
||||
public override bool Check(IEnumerable<string> assmblyFilePaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
return CheckCore(assmblyFilePaths);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_error.WriteLine("Exception performing Extension dependency check:");
|
||||
_error.WriteLine(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckCore(IEnumerable<string> assemblyFilePaths)
|
||||
{
|
||||
var items = assemblyFilePaths.Select(a => ExtensionVerificationItem.Create(a)).ToArray();
|
||||
var assemblies = new HashSet<AssemblyIdentity>(items.Select(i => i.Identity));
|
||||
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
_output.WriteLine($"Verifying assembly at {item.FilePath}");
|
||||
|
||||
if (!Path.IsPathRooted(item.FilePath))
|
||||
{
|
||||
_error.WriteLine($"The file path '{item.FilePath}' is not a rooted path. File paths must be absolute and fully-qualified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var reference in item.References)
|
||||
{
|
||||
if (_ignoredAssemblies.Any(n => reference.Name.StartsWith(n)))
|
||||
{
|
||||
// This is on the allow list, keep going.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (assemblies.Contains(reference))
|
||||
{
|
||||
// This was also provided as a dependency, keep going.
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get here we can't resolve this assembly. This is an error.
|
||||
_error.WriteLine($"Extension assembly '{item.Identity.Name}' depends on '{reference.ToString()} which is missing.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Assuming we get this far, the set of assemblies we have is at least a coherent set (barring
|
||||
// version conflicts). Register all of the paths with the loader so they can find each other by
|
||||
// name.
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
_loader.AddAssemblyLocation(items[i].FilePath);
|
||||
}
|
||||
|
||||
// Now try to load everything. This has the side effect of resolving all of these items
|
||||
// in the loader's caches.
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
item.Assembly = _loader.LoadFromPath(item.FilePath);
|
||||
}
|
||||
|
||||
// Third, check that the MVIDs of the files on disk match the MVIDs of the loaded assemblies.
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
if (item.Mvid != item.Assembly.ManifestModule.ModuleVersionId)
|
||||
{
|
||||
_error.WriteLine($"Extension assembly '{item.Identity.Name}' at '{item.FilePath}' has a different ModuleVersionId than loaded assembly '{item.Assembly.FullName}'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class ExtensionVerificationItem
|
||||
{
|
||||
public static ExtensionVerificationItem Create(string filePath)
|
||||
{
|
||||
using (var peReader = new PEReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
var metadataReader = peReader.GetMetadataReader();
|
||||
var identity = metadataReader.GetAssemblyIdentity();
|
||||
var mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid);
|
||||
var references = metadataReader.GetReferencedAssembliesOrThrow();
|
||||
|
||||
return new ExtensionVerificationItem(filePath, identity, mvid, references.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private ExtensionVerificationItem(string filePath, AssemblyIdentity identity, Guid mvid, AssemblyIdentity[] references)
|
||||
{
|
||||
FilePath = filePath;
|
||||
Identity = identity;
|
||||
Mvid = mvid;
|
||||
References = references;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public Assembly Assembly { get; set; }
|
||||
|
||||
public AssemblyIdentity Identity { get; }
|
||||
|
||||
public Guid Mvid { get; }
|
||||
|
||||
public IReadOnlyList<AssemblyIdentity> References { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -383,6 +383,8 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
ServerLogger.Log("End writing response.");
|
||||
|
||||
reason = ConnectionResult.Reason.CompilationCompleted;
|
||||
|
||||
_eventBus.CompilationCompleted();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ using System.Linq;
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
|
@ -26,6 +25,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
Assemblies = Argument("assemblies", "assemblies to search for tag helpers", multipleValues: true);
|
||||
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
||||
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
||||
Version = Option("-v|--version", "Razor language version", CommandOptionType.SingleValue);
|
||||
Configuration = Option("-c", "Razor configuration name", CommandOptionType.SingleValue);
|
||||
ExtensionNames = Option("-n", "extension name", CommandOptionType.MultipleValue);
|
||||
ExtensionFilePaths = Option("-e", "extension file path", CommandOptionType.MultipleValue);
|
||||
}
|
||||
|
||||
public CommandArgument Assemblies { get; }
|
||||
|
|
@ -34,6 +37,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
|
||||
public CommandOption ProjectDirectory { get; }
|
||||
|
||||
public CommandOption Version { get; }
|
||||
|
||||
public CommandOption Configuration { get; }
|
||||
|
||||
public CommandOption ExtensionNames { get; }
|
||||
|
||||
public CommandOption ExtensionFilePaths { get; }
|
||||
|
||||
protected override bool ValidateArguments()
|
||||
{
|
||||
if (string.IsNullOrEmpty(TagHelperManifest.Value()))
|
||||
|
|
@ -53,12 +64,61 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
ProjectDirectory.Values.Add(Environment.CurrentDirectory);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Version.Value()))
|
||||
{
|
||||
Error.WriteLine($"{Version.ValueName} must be specified.");
|
||||
return false;
|
||||
}
|
||||
else if (!RazorLanguageVersion.TryParse(Version.Value(), out _))
|
||||
{
|
||||
Error.WriteLine($"{Version.ValueName} is not a valid language version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Configuration.Value()))
|
||||
{
|
||||
Error.WriteLine($"{Configuration.ValueName} must be specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ExtensionNames.Values.Count != ExtensionFilePaths.Values.Count)
|
||||
{
|
||||
Error.WriteLine($"{ExtensionNames.ValueName} and {ExtensionFilePaths.ValueName} should have the same number of values.");
|
||||
}
|
||||
|
||||
foreach (var filePath in ExtensionFilePaths.Values)
|
||||
{
|
||||
if (!Path.IsPathRooted(filePath))
|
||||
{
|
||||
Error.WriteLine($"Extension file paths must be fully-qualified, absolute paths.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Parent.Checker.Check(ExtensionFilePaths.Values))
|
||||
{
|
||||
Error.WriteLine($"Extenions could not be loaded. See output for details.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override Task<int> ExecuteCoreAsync()
|
||||
{
|
||||
// Loading all of the extensions should succeed as the dependency checker will have already
|
||||
// loaded them.
|
||||
var extensions = new RazorExtension[ExtensionNames.Values.Count];
|
||||
for (var i = 0; i < ExtensionNames.Values.Count; i++)
|
||||
{
|
||||
extensions[i] = new AssemblyExtension(ExtensionNames.Values[i], Parent.Loader.LoadFromPath(ExtensionFilePaths.Values[i]));
|
||||
}
|
||||
|
||||
var version = RazorLanguageVersion.Parse(Version.Value());
|
||||
var configuration = new RazorConfiguration(version, Configuration.Value(), extensions);
|
||||
|
||||
var result = ExecuteCore(
|
||||
configuration: configuration,
|
||||
projectDirectory: ProjectDirectory.Value(),
|
||||
outputFilePath: TagHelperManifest.Value(),
|
||||
assemblies: Assemblies.Values.ToArray());
|
||||
|
|
@ -66,36 +126,31 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private int ExecuteCore(string projectDirectory, string outputFilePath, string[] assemblies)
|
||||
private int ExecuteCore(RazorConfiguration configuration, string projectDirectory, string outputFilePath, string[] assemblies)
|
||||
{
|
||||
outputFilePath = Path.Combine(projectDirectory, outputFilePath);
|
||||
|
||||
var metadataReferences = new MetadataReference[assemblies.Length];
|
||||
for (var i = 0; i < assemblies.Length; i++)
|
||||
{
|
||||
metadataReferences[i] = MetadataReference.CreateFromFile(assemblies[i]);
|
||||
metadataReferences[i] = Parent.AssemblyReferenceProvider(assemblies[i], default(MetadataReferenceProperties));
|
||||
}
|
||||
|
||||
var engine = RazorEngine.Create((b) =>
|
||||
var engine = RazorProjectEngine.Create(configuration, RazorProjectFileSystem.Empty, b =>
|
||||
{
|
||||
RazorExtensions.Register(b);
|
||||
|
||||
b.Features.Add(new DefaultMetadataReferenceFeature() { References = metadataReferences });
|
||||
b.Features.Add(new CompilationTagHelperFeature());
|
||||
|
||||
// TagHelperDescriptorProviders (actually do tag helper discovery)
|
||||
b.Features.Add(new DefaultTagHelperDescriptorProvider());
|
||||
b.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||
});
|
||||
|
||||
var feature = engine.Features.OfType<ITagHelperFeature>().Single();
|
||||
var feature = engine.Engine.Features.OfType<ITagHelperFeature>().Single();
|
||||
var tagHelpers = feature.GetDescriptors();
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
Serialize(stream, tagHelpers);
|
||||
|
||||
stream.Position = 0L;
|
||||
stream.Position = 0;
|
||||
|
||||
var newHash = Hash(stream);
|
||||
var existingHash = Hash(outputFilePath);
|
||||
|
|
@ -103,7 +158,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
if (!HashesEqual(newHash, existingHash))
|
||||
{
|
||||
stream.Position = 0;
|
||||
using (var output = File.OpenWrite(outputFilePath))
|
||||
using (var output = File.Open(outputFilePath, FileMode.Create))
|
||||
{
|
||||
stream.CopyTo(output);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
public virtual void ConnectionCompleted(int count)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a compilation is completed successfully and the response is written to the stream.
|
||||
/// </summary>
|
||||
public virtual void CompilationCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a bad client connection was detected and the server will be shutting down as a
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal abstract class ExtensionAssemblyLoader
|
||||
{
|
||||
public abstract void AddAssemblyLocation(string filePath);
|
||||
|
||||
public abstract Assembly Load(string assemblyName);
|
||||
|
||||
public abstract Assembly LoadFromPath(string filePath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal abstract class ExtensionDependencyChecker
|
||||
{
|
||||
public abstract bool Check(IEnumerable<string> extensionFilePaths);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
|
|
@ -13,13 +13,6 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class Builder<T>
|
||||
{
|
||||
public static Builder<T> Make(CommandBase result) => null;
|
||||
|
||||
public static Builder<T> Make(T result) => null;
|
||||
}
|
||||
|
||||
internal class GenerateCommand : CommandBase
|
||||
{
|
||||
public GenerateCommand(Application parent)
|
||||
|
|
@ -30,6 +23,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
RelativePaths = Option("-r", "Relative path", 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);
|
||||
Configuration = Option("-c", "Razor configuration name", CommandOptionType.SingleValue);
|
||||
ExtensionNames = Option("-n", "extension name", CommandOptionType.MultipleValue);
|
||||
ExtensionFilePaths = Option("-e", "extension file path", CommandOptionType.MultipleValue);
|
||||
}
|
||||
|
||||
public CommandOption Sources { get; }
|
||||
|
|
@ -42,9 +39,29 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
|
||||
public CommandOption TagHelperManifest { get; }
|
||||
|
||||
public CommandOption Version { get; }
|
||||
|
||||
public CommandOption Configuration { get; }
|
||||
|
||||
public CommandOption ExtensionNames { get; }
|
||||
|
||||
public CommandOption ExtensionFilePaths { get; }
|
||||
|
||||
protected override Task<int> ExecuteCoreAsync()
|
||||
{
|
||||
// Loading all of the extensions should succeed as the dependency checker will have already
|
||||
// loaded them.
|
||||
var extensions = new RazorExtension[ExtensionNames.Values.Count];
|
||||
for (var i = 0; i < ExtensionNames.Values.Count; i++)
|
||||
{
|
||||
extensions[i] = new AssemblyExtension(ExtensionNames.Values[i], Parent.Loader.LoadFromPath(ExtensionFilePaths.Values[i]));
|
||||
}
|
||||
|
||||
var version = RazorLanguageVersion.Parse(Version.Value());
|
||||
var configuration = new RazorConfiguration(version, Configuration.Value(), extensions);
|
||||
|
||||
var result = ExecuteCore(
|
||||
configuration: configuration,
|
||||
projectDirectory: ProjectDirectory.Value(),
|
||||
tagHelperManifest: TagHelperManifest.Value(),
|
||||
sources: Sources.Values,
|
||||
|
|
@ -77,10 +94,48 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
ProjectDirectory.Values.Add(Environment.CurrentDirectory);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Version.Value()))
|
||||
{
|
||||
Error.WriteLine($"{Version.ValueName} must be specified.");
|
||||
return false;
|
||||
}
|
||||
else if (!RazorLanguageVersion.TryParse(Version.Value(), out _))
|
||||
{
|
||||
Error.WriteLine($"{Version.ValueName} is not a valid language version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Configuration.Value()))
|
||||
{
|
||||
Error.WriteLine($"{Configuration.ValueName} must be specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ExtensionNames.Values.Count != ExtensionFilePaths.Values.Count)
|
||||
{
|
||||
Error.WriteLine($"{ExtensionNames.ValueName} and {ExtensionFilePaths.ValueName} should have the same number of values.");
|
||||
}
|
||||
|
||||
foreach (var filePath in ExtensionFilePaths.Values)
|
||||
{
|
||||
if (!Path.IsPathRooted(filePath))
|
||||
{
|
||||
Error.WriteLine($"Extension file paths must be fully-qualified, absolute paths.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Parent.Checker.Check(ExtensionFilePaths.Values))
|
||||
{
|
||||
Error.WriteLine($"Extensions could not be loaded. See output for details.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int ExecuteCore(
|
||||
RazorConfiguration configuration,
|
||||
string projectDirectory,
|
||||
string tagHelperManifest,
|
||||
List<string> sources,
|
||||
|
|
@ -90,26 +145,19 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
tagHelperManifest = Path.Combine(projectDirectory, tagHelperManifest);
|
||||
|
||||
var tagHelpers = GetTagHelpers(tagHelperManifest);
|
||||
|
||||
var engine = RazorEngine.Create(b =>
|
||||
var inputItems = GetInputItems(projectDirectory, sources, outputs, relativePaths);
|
||||
var compositeFileSystem = new CompositeRazorProjectFileSystem(new[]
|
||||
{
|
||||
GetVirtualRazorProjectSystem(inputItems),
|
||||
RazorProjectFileSystem.Create(projectDirectory),
|
||||
});
|
||||
|
||||
var engine = RazorProjectEngine.Create(configuration, compositeFileSystem, b =>
|
||||
{
|
||||
RazorExtensions.Register(b);
|
||||
|
||||
b.Features.Add(new StaticTagHelperFeature() { TagHelpers = tagHelpers, });
|
||||
});
|
||||
|
||||
|
||||
var inputItems = GetInputItems(projectDirectory, sources, outputs, relativePaths);
|
||||
var compositeProject = new CompositeRazorProjectFileSystem(
|
||||
new[]
|
||||
{
|
||||
GetVirtualRazorProjectSystem(inputItems),
|
||||
RazorProjectFileSystem.Create(projectDirectory),
|
||||
});
|
||||
|
||||
var templateEngine = new MvcRazorTemplateEngine(engine, compositeProject);
|
||||
|
||||
var results = GenerateCode(templateEngine, inputItems);
|
||||
var results = GenerateCode(engine, inputItems);
|
||||
|
||||
var success = true;
|
||||
|
||||
|
|
@ -180,14 +228,15 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return items;
|
||||
}
|
||||
|
||||
private OutputItem[] GenerateCode(RazorTemplateEngine templateEngine, SourceItem[] inputs)
|
||||
private OutputItem[] GenerateCode(RazorProjectEngine engine, SourceItem[] inputs)
|
||||
{
|
||||
var outputs = new OutputItem[inputs.Length];
|
||||
Parallel.For(0, outputs.Length, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, i =>
|
||||
Parallel.For(0, outputs.Length, new ParallelOptions() { MaxDegreeOfParallelism = Debugger.IsAttached ? 1 : 4 }, i =>
|
||||
{
|
||||
var inputItem = inputs[i];
|
||||
|
||||
var csharpDocument = templateEngine.GenerateCode(inputItem.FilePath);
|
||||
var codeDocument = engine.Process(engine.FileSystem.GetItem(inputItem.FilePath));
|
||||
var csharpDocument = codeDocument.GetCSharpDocument();
|
||||
outputs[i] = new OutputItem(inputItem, csharpDocument);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
// 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 System.Reflection.PortableExecutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class MetadataCache
|
||||
{
|
||||
// Store 1000 entries -- arbitrary number
|
||||
private const int CacheSize = 1000;
|
||||
private readonly ConcurrentLruCache<string, MetadataCacheEntry> _metadataCache =
|
||||
new ConcurrentLruCache<string, MetadataCacheEntry>(CacheSize, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// For testing purposes only.
|
||||
internal ConcurrentLruCache<string, MetadataCacheEntry> Cache => _metadataCache;
|
||||
|
||||
internal Metadata GetMetadata(string fullPath)
|
||||
{
|
||||
var timestamp = GetFileTimeStamp(fullPath);
|
||||
|
||||
// Check if we have an entry in the dictionary.
|
||||
if (_metadataCache.TryGetValue(fullPath, out var entry))
|
||||
{
|
||||
if (timestamp.HasValue && timestamp.Value == entry.Timestamp)
|
||||
{
|
||||
// The file has not changed since we cached it. Return the cached entry.
|
||||
return entry.Metadata;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The file has changed recently. Remove the cache entry.
|
||||
_metadataCache.Remove(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
Metadata metadata;
|
||||
using (var fileStream = File.OpenRead(fullPath))
|
||||
{
|
||||
metadata = AssemblyMetadata.CreateFromStream(fileStream, PEStreamOptions.PrefetchMetadata);
|
||||
}
|
||||
|
||||
_metadataCache.GetOrAdd(fullPath, new MetadataCacheEntry(timestamp.Value, metadata));
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static DateTime? GetFileTimeStamp(string fullPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.Assert(Path.IsPathRooted(fullPath));
|
||||
|
||||
return File.GetLastWriteTimeUtc(fullPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// There are several exceptions that can occur here: NotSupportedException or PathTooLongException
|
||||
// for a bad path, UnauthorizedAccessException for access denied, etc. Rather than listing them all,
|
||||
// just catch all exceptions and log.
|
||||
ServerLogger.LogException(e, $"Error getting timestamp of file {fullPath}.");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct MetadataCacheEntry
|
||||
{
|
||||
public MetadataCacheEntry(DateTime timestamp, Metadata metadata)
|
||||
{
|
||||
Debug.Assert(timestamp.Kind == DateTimeKind.Utc);
|
||||
|
||||
Timestamp = timestamp;
|
||||
Metadata = metadata;
|
||||
}
|
||||
|
||||
public DateTime Timestamp { get; }
|
||||
|
||||
public Metadata Metadata { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) Microsoft. 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.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal static class MetadataReaderExtensions
|
||||
{
|
||||
internal static AssemblyIdentity GetAssemblyIdentity(this MetadataReader reader)
|
||||
{
|
||||
if (!reader.IsAssembly)
|
||||
{
|
||||
throw new BadImageFormatException();
|
||||
}
|
||||
|
||||
var definition = reader.GetAssemblyDefinition();
|
||||
|
||||
return CreateAssemblyIdentity(
|
||||
reader,
|
||||
definition.Version,
|
||||
definition.Flags,
|
||||
definition.PublicKey,
|
||||
definition.Name,
|
||||
definition.Culture,
|
||||
isReference: false);
|
||||
}
|
||||
|
||||
internal static AssemblyIdentity[] GetReferencedAssembliesOrThrow(this MetadataReader reader)
|
||||
{
|
||||
var references = new List<AssemblyIdentity>();
|
||||
|
||||
foreach (var referenceHandle in reader.AssemblyReferences)
|
||||
{
|
||||
var reference = reader.GetAssemblyReference(referenceHandle);
|
||||
references.Add(CreateAssemblyIdentity(
|
||||
reader,
|
||||
reference.Version,
|
||||
reference.Flags,
|
||||
reference.PublicKeyOrToken,
|
||||
reference.Name,
|
||||
reference.Culture,
|
||||
isReference: true));
|
||||
}
|
||||
|
||||
return references.ToArray();
|
||||
}
|
||||
|
||||
private static AssemblyIdentity CreateAssemblyIdentity(
|
||||
MetadataReader reader,
|
||||
Version version,
|
||||
AssemblyFlags flags,
|
||||
BlobHandle publicKey,
|
||||
StringHandle name,
|
||||
StringHandle culture,
|
||||
bool isReference)
|
||||
{
|
||||
var publicKeyOrToken = reader.GetBlobContent(publicKey);
|
||||
bool hasPublicKey;
|
||||
|
||||
if (isReference)
|
||||
{
|
||||
hasPublicKey = (flags & AssemblyFlags.PublicKey) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assembly definitions never contain a public key token, they only can have a full key or nothing,
|
||||
// so the flag AssemblyFlags.PublicKey does not make sense for them and is ignored.
|
||||
// See Ecma-335, Partition II Metadata, 22.2 "Assembly : 0x20".
|
||||
// This also corresponds to the behavior of the native C# compiler and sn.exe tool.
|
||||
hasPublicKey = !publicKeyOrToken.IsEmpty;
|
||||
}
|
||||
|
||||
if (publicKeyOrToken.IsEmpty)
|
||||
{
|
||||
publicKeyOrToken = default;
|
||||
}
|
||||
|
||||
return new AssemblyIdentity(
|
||||
name: reader.GetString(name),
|
||||
version: version,
|
||||
cultureName: culture.IsNil ? null : reader.GetString(culture),
|
||||
publicKeyOrToken: publicKeyOrToken,
|
||||
hasPublicKey: hasPublicKey,
|
||||
isRetargetable: (flags & AssemblyFlags.Retargetable) != 0,
|
||||
contentType: (AssemblyContentType)((int)(flags & AssemblyFlags.ContentTypeMask) >> 9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.CodeAnalysis.Razor\Microsoft.CodeAnalysis.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- This makes it so that the runtimeconfig.json is included as part of the build output of the project that references this project. -->
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
|
|
@ -15,7 +16,16 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
var cancel = new CancellationTokenSource();
|
||||
Console.CancelKeyPress += (sender, e) => { cancel.Cancel(); };
|
||||
|
||||
var application = new Application(cancel.Token);
|
||||
// Prevent shadow copying.
|
||||
var loader = new DefaultExtensionAssemblyLoader(baseDirectory: null);
|
||||
var checker = new DefaultExtensionDependencyChecker(loader, Console.Out, Console.Error);
|
||||
|
||||
var application = new Application(
|
||||
cancel.Token,
|
||||
loader,
|
||||
checker,
|
||||
(path, properties) => MetadataReference.CreateFromFile(path, properties));
|
||||
|
||||
return application.Execute(args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
|
||||
var host = ConnectionHost.Create(Pipe.Value());
|
||||
|
||||
var compilerHost = CompilerHost.Create();
|
||||
ExecuteServerCore(host, compilerHost, Cancelled, eventBus: null, keepAlive: keepAlive);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
/// <summary>
|
||||
/// Write a Request to the stream.
|
||||
/// </summary>
|
||||
public async Task WriteAsync(Stream outStream, CancellationToken cancellationToken = default)
|
||||
public async Task WriteAsync(Stream outStream, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
// 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.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
// Note that this class has no thread-safety guarantees. The caller should use a lock
|
||||
// if concurrency is required.
|
||||
internal class ShadowCopyManager : IDisposable
|
||||
{
|
||||
// Note that this class uses the *existance* of the Mutex to lock a directory.
|
||||
//
|
||||
// Nothing in this code actually ever acquires the Mutex, we just try to see if it exists
|
||||
// already.
|
||||
private readonly Mutex _mutex;
|
||||
|
||||
private int _counter;
|
||||
|
||||
public ShadowCopyManager(string baseDirectory = null)
|
||||
{
|
||||
BaseDirectory = baseDirectory ?? Path.Combine(Path.GetTempPath(), "Razor", "ShadowCopy");
|
||||
|
||||
var guid = Guid.NewGuid().ToString("N").ToLowerInvariant();
|
||||
UniqueDirectory = Path.Combine(BaseDirectory, guid);
|
||||
|
||||
_mutex = new Mutex(initiallyOwned: false, name: guid);
|
||||
|
||||
Directory.CreateDirectory(UniqueDirectory);
|
||||
}
|
||||
|
||||
public string BaseDirectory { get; }
|
||||
|
||||
public string UniqueDirectory { get; }
|
||||
|
||||
public string AddAssembly(string filePath)
|
||||
{
|
||||
var assemblyDirectory = CreateUniqueDirectory();
|
||||
|
||||
var destination = Path.Combine(assemblyDirectory, Path.GetFileName(filePath));
|
||||
CopyFile(filePath, destination);
|
||||
|
||||
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
|
||||
var resourcesNameWithoutExtension = fileNameWithoutExtension + ".resources";
|
||||
var resourcesNameWithExtension = resourcesNameWithoutExtension + ".dll";
|
||||
|
||||
foreach (var directory in Directory.EnumerateDirectories(Path.GetDirectoryName(filePath)))
|
||||
{
|
||||
var directoryName = Path.GetFileName(directory);
|
||||
|
||||
var resourcesPath = Path.Combine(directory, resourcesNameWithExtension);
|
||||
if (File.Exists(resourcesPath))
|
||||
{
|
||||
var resourcesShadowCopyPath = Path.Combine(assemblyDirectory, directoryName, resourcesNameWithExtension);
|
||||
CopyFile(resourcesPath, resourcesShadowCopyPath);
|
||||
}
|
||||
|
||||
resourcesPath = Path.Combine(directory, resourcesNameWithoutExtension, resourcesNameWithExtension);
|
||||
if (File.Exists(resourcesPath))
|
||||
{
|
||||
var resourcesShadowCopyPath = Path.Combine(assemblyDirectory, directoryName, resourcesNameWithoutExtension, resourcesNameWithExtension);
|
||||
CopyFile(resourcesPath, resourcesShadowCopyPath);
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_mutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
public Task PurgeUnusedDirectoriesAsync()
|
||||
{
|
||||
return Task.Run((Action)PurgeUnusedDirectories);
|
||||
}
|
||||
|
||||
private string CreateUniqueDirectory()
|
||||
{
|
||||
var id = _counter++;
|
||||
|
||||
var directory = Path.Combine(UniqueDirectory, id.ToString());
|
||||
Directory.CreateDirectory(directory);
|
||||
return directory;
|
||||
}
|
||||
|
||||
private void CopyFile(string originalPath, string shadowCopyPath)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(shadowCopyPath);
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
File.Copy(originalPath, shadowCopyPath);
|
||||
|
||||
MakeWritable(new FileInfo(shadowCopyPath));
|
||||
}
|
||||
|
||||
private void MakeWritable(string directoryPath)
|
||||
{
|
||||
var directory = new DirectoryInfo(directoryPath);
|
||||
|
||||
foreach (var file in directory.EnumerateFiles(searchPattern: "*", searchOption: SearchOption.AllDirectories))
|
||||
{
|
||||
MakeWritable(file);
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeWritable(FileInfo file)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file.IsReadOnly)
|
||||
{
|
||||
file.IsReadOnly = false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// There are many reasons this could fail. Ignore it and keep going.
|
||||
}
|
||||
}
|
||||
|
||||
private void PurgeUnusedDirectories()
|
||||
{
|
||||
IEnumerable<string> directories;
|
||||
try
|
||||
{
|
||||
directories = Directory.EnumerateDirectories(BaseDirectory);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
Mutex mutex = null;
|
||||
try
|
||||
{
|
||||
// We only want to try deleting the directory if no-one else is currently using it.
|
||||
//
|
||||
// Note that the mutex name is the name of the directory. This is OK because we're using
|
||||
// GUIDs as directory/mutex names.
|
||||
if (!Mutex.TryOpenExisting(Path.GetFileName(directory).ToLowerInvariant(), out mutex))
|
||||
{
|
||||
MakeWritable(directory);
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If something goes wrong we will leave it to the next run to clean up.
|
||||
// Just swallow the exception and move on.
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mutex != null)
|
||||
{
|
||||
mutex.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,19 +57,21 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
var process = Process.GetProcessById(response.ServerProcessId);
|
||||
process.WaitForExit();
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// There is an inherent race here with the server process. If it has already shutdown
|
||||
// by the time we try to access it then the operation has succeed.
|
||||
// by the time we try to access it then the operation has succeeded.
|
||||
Error.Write(ex);
|
||||
}
|
||||
|
||||
Out.Write("Server pid:{0} shut down", response.ServerProcessId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) when (IsServerRunning())
|
||||
catch (Exception ex) when (IsServerRunning())
|
||||
{
|
||||
// Ignore an exception that occurred while the server was shutting down.
|
||||
Error.Write(ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.Composition;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class ExportCustomProjectEngineFactoryAttribute : ExportAttribute, ICustomProjectEngineFactoryMetadata
|
||||
{
|
||||
public ExportCustomProjectEngineFactoryAttribute(string configurationName)
|
||||
: base(typeof(IProjectEngineFactory))
|
||||
{
|
||||
if (configurationName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurationName));
|
||||
}
|
||||
|
||||
ConfigurationName = configurationName;
|
||||
}
|
||||
|
||||
public string ConfigurationName { get; }
|
||||
|
||||
public bool SupportsSerialization { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +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.CodeAnalysis.Razor
|
||||
{
|
||||
public interface ICustomProjectEngineFactoryMetadata
|
||||
{
|
||||
string ConfigurationName { get; }
|
||||
|
||||
bool SupportsSerialization { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public interface IProjectEngineFactory
|
||||
{
|
||||
RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@ using Microsoft.CodeAnalysis.Host;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class RazorTemplateEngineFactoryService : ILanguageService
|
||||
internal abstract class RazorProjectEngineFactoryService : ILanguageService
|
||||
{
|
||||
public abstract RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure);
|
||||
public abstract RazorProjectEngine Create(string projectPath, Action<RazorProjectEngineBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,16 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
***********************************************************************************************
|
||||
-->
|
||||
<Project ToolsVersion="14.0">
|
||||
<PropertyGroup>
|
||||
<!-- Determines if the Razor Sdk is responsible for importing Microsoft.NET.Sdk. Microsoft.NET.Sdk.Web may have previously imported this. -->
|
||||
<_RazorSdkImportsMicrosoftNetSdk Condition="'$(UsingMicrosoftNETSdk)' != 'true'">true</_RazorSdkImportsMicrosoftNetSdk>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\build\netstandard2.0\Sdk.Razor.CurrentVersion.props" />
|
||||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition="'$(_RazorSdkImportsMicrosoftNetSdk)' == 'true'" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RazorSdkCurrentVersionProps Condition="'$(RazorSdkCurrentVersionProps)' == ''">$(MSBuildThisFileDirectory)..\build\netstandard2.0\Sdk.Razor.CurrentVersion.props</RazorSdkCurrentVersionProps>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RazorSdkCurrentVersionProps)" />
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
-->
|
||||
<Project ToolsVersion="14.0">
|
||||
|
||||
<PropertyGroup Condition="'$(RazorSdkTargetsPath)' == ''">
|
||||
<RazorSdkTargetsPath Condition="'$(IsCrossTargetingBuild)' == 'true'">$(MSBuildThisFileDirectory)..\buildMultiTargeting\Sdk.Razor.CurrentVersion.MultiTargeting.targets</RazorSdkTargetsPath>
|
||||
<RazorSdkTargetsPath Condition="'$(IsCrossTargetingBuild)' != 'true'">$(MSBuildThisFileDirectory)..\build\netstandard2.0\Sdk.Razor.CurrentVersion.targets</RazorSdkTargetsPath>
|
||||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" Condition="'$(_RazorSdkImportsMicrosoftNetSdk)' == 'true'" />
|
||||
|
||||
<PropertyGroup Condition="'$(RazorSdkCurrentVersionTargets)' == ''">
|
||||
<RazorSdkCurrentVersionTargets Condition="'$(IsCrossTargetingBuild)' == 'true'">$(MSBuildThisFileDirectory)..\buildMultiTargeting\Sdk.Razor.CurrentVersion.MultiTargeting.targets</RazorSdkCurrentVersionTargets>
|
||||
<RazorSdkCurrentVersionTargets Condition="'$(IsCrossTargetingBuild)' != 'true'">$(MSBuildThisFileDirectory)..\build\netstandard2.0\Sdk.Razor.CurrentVersion.targets</RazorSdkCurrentVersionTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RazorSdkTargetsPath)" />
|
||||
<Import Project="$(RazorSdkCurrentVersionTargets)" />
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.NET.Sdk.Razor.DesignTime.targets
|
||||
|
||||
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
|
||||
created a backup copy. Incorrect changes to this file will make it
|
||||
impossible to load or build your projects from the command-line or the IDE.
|
||||
|
||||
Copyright (c) .NET Foundation. All rights reserved.
|
||||
***********************************************************************************************
|
||||
-->
|
||||
|
||||
<Project ToolsVersion="14.0">
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
Defines the generic .NET Core 'Razor' capability.
|
||||
|
||||
Note that we don't define any capabilities here that depend on the version of the runtime/toolset
|
||||
in use by the project. Those capabilities are defined by the relevant runtime packages so that
|
||||
we use the lack of the capability to detect downlevel scenarios.
|
||||
-->
|
||||
<ProjectCapability Include="DotNetCoreRazor"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
WebSdk imports these capabilities for nesting in DotNetCoreWeb projects.
|
||||
Conditinally import these capabilities if the project isn't targeting the WebSdk.
|
||||
-->
|
||||
<ItemGroup Condition="'$(UsingMicrosoftNETSdkWeb)'==''">
|
||||
<ProjectCapability Include="SupportHierarchyContextSvc" />
|
||||
<ProjectCapability Include="DynamicDependentFile" />
|
||||
<ProjectCapability Include="DynamicFileNesting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorConfiguration.xaml">
|
||||
<Context>File</Context>
|
||||
</PropertyPageSchema>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorExtension.xaml">
|
||||
<Context>File</Context>
|
||||
</PropertyPageSchema>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorGeneral.xaml">
|
||||
<Context>Project</Context>
|
||||
</PropertyPageSchema>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -10,10 +10,8 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
***********************************************************************************************
|
||||
-->
|
||||
<Project ToolsVersion="14.0">
|
||||
<Import Project="Sdk.Razor.CurrentVersion.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Redirect the SDK to use the targets from the current package -->
|
||||
<RazorSdkTargetsPath>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.targets</RazorSdkTargetsPath>
|
||||
<RazorSdkCurrentVersionProps>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.props</RazorSdkCurrentVersionProps>
|
||||
<RazorSdkCurrentVersionTargets>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.targets</RazorSdkCurrentVersionTargets>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.NET.Sdk.Razor.targets
|
||||
|
||||
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
|
||||
created a backup copy. Incorrect changes to this file will make it
|
||||
impossible to load or build your projects from the command-line or the IDE.
|
||||
|
||||
Copyright (c) .NET Foundation. All rights reserved.
|
||||
***********************************************************************************************
|
||||
-->
|
||||
<Project ToolsVersion="14.0">
|
||||
<Import Project="Sdk.Razor.CurrentVersion.targets" />
|
||||
</Project>
|
||||
|
|
@ -18,6 +18,12 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
Default properties for common Razor SDK behavior.
|
||||
-->
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
Set to true to automatically include certain file types, such as .cshtml files, as content in the project.
|
||||
When referenced via Microsoft.NET.Sdk.Web, this additionally includes all files under wwwroot, and any config files.
|
||||
-->
|
||||
<EnableDefaultContentItems Condition="'$(EnableDefaultContentItems)'==''">true</EnableDefaultContentItems>
|
||||
|
||||
<!--
|
||||
Set to true to automatically include Razor (.cshtml) files in @(RazorGenerate) from @(Content).
|
||||
-->
|
||||
|
|
@ -46,37 +52,24 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<RazorCompileToolset>Implicit</RazorCompileToolset>
|
||||
|
||||
<!--
|
||||
Set to true to allow a Razor code generation to use a persistent build server process.
|
||||
Configures whether all Razor content items (.cshtml files) will be marked to be included in the produced NuGet package as content.
|
||||
|
||||
All Content items are included in a NuGet package as content files. This setting can be used to control this behavior for Razor content items.
|
||||
-->
|
||||
<UseRazorBuildServer Condition="'$(UseRazorBuildServer)'==''">false</UseRazorBuildServer>
|
||||
<IncludeRazorContentInPack Condition="'$(IncludeRazorContentInPack)'==''">false</IncludeRazorContentInPack>
|
||||
|
||||
<!--
|
||||
Set to false to disable Razor code generation from using a persistent build server process.
|
||||
-->
|
||||
<UseRazorBuildServer Condition="'$(UseRazorBuildServer)'==''">true</UseRazorBuildServer>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(_WebSdkHeartsRazorSdk)' == 'true' AND '$(EnableDefaultItems)' == 'true' And '$(EnableDefaultContentItems)' == 'true'">
|
||||
<Content Include="**\*.cshtml" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(DefaultWebContentItemExcludes)" />
|
||||
<ItemGroup Condition="'$(EnableDefaultItems)' == 'true' And '$(EnableDefaultContentItems)' == 'true'">
|
||||
<Content Include="**\*.cshtml" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(DefaultWebContentItemExcludes)">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
|
||||
<None Remove="**\*.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
Defines the generic .NET Core 'Razor' capability.
|
||||
|
||||
Note that we don't define any capabilities here that depend on the version of the runtime/toolset
|
||||
in use by the project. Those capabilities are defined by the relevant runtime packages so that
|
||||
we use the lack of the capability to detect downlevel scenarios.
|
||||
-->
|
||||
<ProjectCapability Include="DotNetCoreRazor"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorConfiguration.xaml">
|
||||
<Context>File</Context>
|
||||
</PropertyPageSchema>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorExtension.xaml">
|
||||
<Context>File</Context>
|
||||
</PropertyPageSchema>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Rules\RazorGeneral.xaml">
|
||||
<Context>Project</Context>
|
||||
</PropertyPageSchema>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
ResolveRazorGenerateInputs;
|
||||
AssignRazorGenerateTargetPaths;
|
||||
ResolveAssemblyReferenceRazorGenerateInputs;
|
||||
_EnsureRazorCompilerReferenced;
|
||||
_CheckForMissingRazorCompiler;
|
||||
ResolveTagHelperRazorGenerateInputs
|
||||
</PrepareForRazorGenerateDependsOn>
|
||||
|
||||
<RazorGenerateDependsOn>
|
||||
PrepareForRazorGenerate;
|
||||
_EnsureRazorCompilerReferenced;
|
||||
_CheckForMissingRazorCompiler;
|
||||
RazorCoreGenerate
|
||||
</RazorGenerateDependsOn>
|
||||
|
||||
|
|
@ -50,20 +50,36 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
</PrepareForRazorCompileDependsOn>
|
||||
|
||||
<ResolveRazorCompileInputsDependsOn>
|
||||
ResolveRazorEmbeddedResources
|
||||
ResolveRazorEmbeddedResources
|
||||
</ResolveRazorCompileInputsDependsOn>
|
||||
|
||||
<RazorCompileDependsOn>
|
||||
PrepareForRazorCompile;
|
||||
RazorCoreCompile
|
||||
</RazorCompileDependsOn>
|
||||
|
||||
<BuiltProjectOutputGroupDependsOn>
|
||||
$(BuiltProjectOutputGroupDependsOn);
|
||||
_RazorAddBuiltProjectOutputGroupOutput
|
||||
</BuiltProjectOutputGroupDependsOn>
|
||||
|
||||
<DebugSymbolsProjectOutputGroupDependsOn>
|
||||
$(DebugSymbolsProjectOutputGroupDependsOn);
|
||||
_RazorAddDebugSymbolsProjectOutputGroupOutput
|
||||
</DebugSymbolsProjectOutputGroupDependsOn>
|
||||
|
||||
<PrepareForBuildDependsOn>
|
||||
_InitializePreserveCompilationContext;
|
||||
$(PrepareForBuildDependsOn)
|
||||
</PrepareForBuildDependsOn>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
Default values for properties that affect Razor targets to the standard build lifecycle.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(RazorCompileOnBuild)'==''">
|
||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||
<RazorCompileOnBuild>true</RazorCompileOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(RazorCompileOnPublish)'==''">
|
||||
|
|
@ -83,9 +99,12 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<PropertyGroup>
|
||||
<!-- Output directory used for generated files -->
|
||||
<RazorGenerateIntermediateOutputPath Condition="'$(RazorGenerateIntermediateOutputPath)'==''">$(IntermediateOutputPath)Razor\</RazorGenerateIntermediateOutputPath>
|
||||
|
||||
<!-- Suffix appended to $(TargetName) to produce $(RazorTargetName), the name of the assembly produced by Razor -->
|
||||
<RazorTargetNameSuffix Condition="'$(RazorTargetNameSuffix)' == ''">.Razor</RazorTargetNameSuffix>
|
||||
|
||||
<!-- File name (without extension) of the assembly produced by Razor -->
|
||||
<RazorTargetName Condition="'$(RazorTargetName)'==''">$(TargetName).PrecompiledViews</RazorTargetName>
|
||||
<RazorTargetName Condition="'$(RazorTargetName)'==''">$(TargetName)$(RazorTargetNameSuffix)</RazorTargetName>
|
||||
|
||||
<!--
|
||||
The compatibility zone - these properties were provided by the MVC Precompilation tool and they
|
||||
|
|
@ -129,7 +148,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
</PropertyGroup>
|
||||
|
||||
<!-- Resolve the toolset to use -->
|
||||
<PropertyGroup Condition="'$(_WebSdkHeartsRazorSdk)' == 'true'">
|
||||
<PropertyGroup>
|
||||
<!-- Default value for the property 'MvcRazorCompileOnPublish' is empty. If it has been explicitly enabled, continue using precompilation. -->
|
||||
<ResolvedRazorCompileToolset Condition="'$(MvcRazorCompileOnPublish)' == 'true'">PrecompilationTool</ResolvedRazorCompileToolset>
|
||||
|
||||
|
|
@ -154,6 +173,8 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<RazorIntermediateAssembly Condition="'$(RazorIntermediateAssembly)'==''" Include="$(IntermediateOutputPath)$(RazorTargetName).dll" />
|
||||
<!-- Used in Compilation.targets -->
|
||||
<_RazorDebugSymbolsIntermediatePath Condition="'$(_RazorDebugSymbolsProduced)'=='true'" Include="$(IntermediateOutputPath)$(RazorTargetName).pdb" />
|
||||
<!-- Add all cshtml files to UpToDateCheckInput - a collection used by the IDE's project system to determine if a project needs to be rebuilt -->
|
||||
<UpToDateCheckInput Condition="'$(RazorCompileOnBuild)'=='true'" Include="@(Content->WithMetadataValue('Extension', '.cshtml'))" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
|
|
@ -182,6 +203,16 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<Target Name="RazorCompile" DependsOnTargets="$(RazorCompileDependsOn)">
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
PreserveCompilationContext needs to enabled by default only for "applications" (OutputType = exe) which have one or more Razor files.
|
||||
We cannot only inspect ItemGroups globally, therefore setting this must be done inside a target. We wire this up to run before Build.
|
||||
-->
|
||||
<Target Name="_InitializePreserveCompilationContext">
|
||||
<PropertyGroup Condition="'$(PreserveCompilationContext)' == '' AND '$(OutputType)' == 'exe' AND '@(Content->AnyHaveMetadataValue('Extension', '.cshtml'))' == 'true'">
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Computes the applicable @(ResolvedRazorConfiguration) and @(ResolvedRazorExtension) items that match the project's
|
||||
configuration.
|
||||
|
|
@ -316,16 +347,22 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
-->
|
||||
<Target
|
||||
Name="_RazorAddBuiltProjectOutputGroupOutput"
|
||||
DependsOnTargets="ResolveRazorGenerateInputs"
|
||||
BeforeTargets="BuiltProjectOutputGroup"
|
||||
DependsOnTargets="_ResolveRazorTargetPath;ResolveRazorGenerateInputs"
|
||||
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
||||
|
||||
<PropertyGroup>
|
||||
<RazorOutputPath Condition="'$(RazorOutputPath)'==''">$([MSBuild]::EnsureTrailingSlash('$(OutDir)'))</RazorOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'@(RazorGenerate)'!= ''">
|
||||
<BuiltProjectOutputGroupOutput Include="@(RazorIntermediateAssembly)" FinalOutputPath="$(RazorOutputPath)$(RazorTargetName).dll" />
|
||||
<BuiltProjectOutputGroupOutput Include="%(RazorIntermediateAssembly.FullPath)" FinalOutputPath="$(RazorTargetPath)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_RazorAddDebugSymbolsProjectOutputGroupOutput"
|
||||
DependsOnTargets="_ResolveRazorTargetPath;ResolveRazorGenerateInputs"
|
||||
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
||||
|
||||
<ItemGroup Condition="Exists('@(_RazorDebugSymbolsIntermediatePath)')">
|
||||
<DebugSymbolsProjectOutputGroupOutput Include="%(_RazorDebugSymbolsIntermediatePath.FullPath)" FinalOutputPath="$(RazorTargetDir)$(RazorTargetName).pdb" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
|
@ -393,18 +430,14 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
-->
|
||||
<Target
|
||||
Name="_RazorCopyFilesToOutputDirectory"
|
||||
DependsOnTargets="RazorCompile"
|
||||
DependsOnTargets="_ResolveRazorTargetPath;RazorCompile"
|
||||
AfterTargets="CopyFilesToOutputDirectory"
|
||||
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
||||
|
||||
<PropertyGroup>
|
||||
<RazorOutputPath Condition="'$(RazorOutputPath)'==''">$([MSBuild]::EnsureTrailingSlash('$(OutDir)'))</RazorOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Copy the Razor dll -->
|
||||
<Copy
|
||||
SourceFiles="@(RazorIntermediateAssembly)"
|
||||
DestinationFolder="$(RazorOutputPath)"
|
||||
DestinationFiles="$(RazorTargetPath)"
|
||||
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
|
||||
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
|
||||
Retries="$(CopyRetryCount)"
|
||||
|
|
@ -478,11 +511,29 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
|
||||
</Target>
|
||||
|
||||
<Target Name="_EnsureRazorCompilerReferenced" Condition="'$(IsRazorCompilerReferenced)' != 'true'">
|
||||
<Error Text="A reference to Microsoft.AspNetCore.Razor.Design is required for this target to succeed. See todo:fwlink for instructions on how to resolve this." />
|
||||
|
||||
<Target Name="_CheckForMissingRazorCompiler" Condition="'$(IsRazorCompilerReferenced)' != 'true'">
|
||||
<Error
|
||||
Text="A PackageReference for 'Microsoft.AspNetCore.Razor.Design' was not included in your project. This package is required to compile Razor files. Typically, a
|
||||
transitive reference to 'Microsoft.AspNetCore.Razor.Design' and references required to compile Razor files are obtained by adding a PackageReference
|
||||
for 'Microsoft.AspNetCore.Mvc' in your project. For more information, see https://go.microsoft.com/fwlink/?linkid=868374." />
|
||||
</Target>
|
||||
|
||||
<Target Name="_ResolveRazorTargetPath">
|
||||
<PropertyGroup>
|
||||
<RazorOutputPath Condition="'$(RazorOutputPath)'==''">$([MSBuild]::EnsureTrailingSlash('$(OutDir)'))</RazorOutputPath>
|
||||
<RazorTargetDir>$([MSBuild]::Escape($([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetFullPath('$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(RazorOutputPath)'))'))))))</RazorTargetDir>
|
||||
<!-- Example, c:\MyProjects\MyProject\bin\debug\MyAssembly.Views.dll -->
|
||||
<RazorTargetPath Condition=" '$(RazorTargetPath)' == '' ">$(RazorTargetDir)$(RazorTargetName).dll</RazorTargetPath>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<PropertyGroup Condition="'$(RazorDesignTimeTargets)'==''">
|
||||
<RazorDesignTimeTargets>$(MSBuildExtensionsPath)\Microsoft\VisualStudio\Razor\Microsoft.NET.Sdk.Razor.DesignTime.targets</RazorDesignTimeTargets>
|
||||
<RazorDesignTimeTargets Condition="!Exists('$(RazorDesignTimeTargets)')">$(MSBuildThisFileDirectory)Microsoft.NET.Sdk.Razor.DesignTime.targets</RazorDesignTimeTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RazorDesignTimeTargets)" />
|
||||
|
||||
<!--
|
||||
This is a hook to import a set of targets after the Razor targets. By default this is unused.
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.NET.Sdk.Razor.targets
|
||||
Microsoft.Net.Sdk.Razor.props
|
||||
|
||||
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
|
||||
created a backup copy. Incorrect changes to this file will make it
|
||||
|
|
@ -10,5 +10,8 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
***********************************************************************************************
|
||||
-->
|
||||
<Project ToolsVersion="14.0">
|
||||
<Import Project="Sdk.Razor.CurrentVersion.MultiTargeting.targets" />
|
||||
<PropertyGroup>
|
||||
<RazorSdkCurrentVersionProps>$(MSBuildThisFileDirectory)..\build\netstandard2.0\Sdk.Razor.CurrentVersion.props</RazorSdkCurrentVersionProps>
|
||||
<RazorSdkCurrentVersionTargets>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.MultiTargeting.targets</RazorSdkCurrentVersTargetsFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
|
@ -18,10 +19,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private MainThreadState _main;
|
||||
private BackgroundThread _bg;
|
||||
|
||||
public BackgroundParser(RazorTemplateEngine templateEngine, string filePath)
|
||||
public BackgroundParser(RazorProjectEngine projectEngine, string filePath, string projectDirectory)
|
||||
{
|
||||
_main = new MainThreadState(filePath);
|
||||
_bg = new BackgroundThread(_main, templateEngine, filePath);
|
||||
_bg = new BackgroundThread(_main, projectEngine, filePath, projectDirectory);
|
||||
|
||||
_main.ResultsReady += (sender, args) => OnResultsReady(args);
|
||||
}
|
||||
|
|
@ -233,22 +234,25 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
private class BackgroundThread : ThreadStateBase
|
||||
{
|
||||
private readonly string _filePath;
|
||||
private readonly string _relativeFilePath;
|
||||
private readonly string _projectDirectory;
|
||||
private MainThreadState _main;
|
||||
private Thread _backgroundThread;
|
||||
private CancellationToken _shutdownToken;
|
||||
private RazorTemplateEngine _templateEngine;
|
||||
private string _filePath;
|
||||
private RazorProjectEngine _projectEngine;
|
||||
private RazorSyntaxTree _currentSyntaxTree;
|
||||
private IList<Edit> _previouslyDiscarded = new List<Edit>();
|
||||
|
||||
public BackgroundThread(MainThreadState main, RazorTemplateEngine templateEngine, string fileName)
|
||||
public BackgroundThread(MainThreadState main, RazorProjectEngine projectEngine, string filePath, string projectDirectory)
|
||||
{
|
||||
// Run on MAIN thread!
|
||||
_main = main;
|
||||
_shutdownToken = _main.CancelToken;
|
||||
_templateEngine = templateEngine;
|
||||
_filePath = fileName;
|
||||
|
||||
_projectEngine = projectEngine;
|
||||
_filePath = filePath;
|
||||
_relativeFilePath = GetNormalizedRelativeFilePath(filePath, projectDirectory);
|
||||
_projectDirectory = projectDirectory;
|
||||
_backgroundThread = new Thread(WorkerLoop);
|
||||
SetThreadId(_backgroundThread.ManagedThreadId);
|
||||
}
|
||||
|
|
@ -262,8 +266,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
// **** BACKGROUND THREAD ****
|
||||
private void WorkerLoop()
|
||||
{
|
||||
var fileNameOnly = Path.GetFileName(_filePath);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureOnThread();
|
||||
|
|
@ -347,14 +349,31 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
EnsureOnThread();
|
||||
|
||||
var sourceDocument = new TextSnapshotSourceDocument(snapshot, _filePath);
|
||||
var imports = _templateEngine.GetImports(_filePath);
|
||||
var projectItem = new TextSnapshotProjectItem(snapshot, _projectDirectory, _relativeFilePath, _filePath);
|
||||
var codeDocument = _projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, imports);
|
||||
|
||||
_templateEngine.GenerateCode(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
private string GetNormalizedRelativeFilePath(string filePath, string projectDirectory)
|
||||
{
|
||||
if (filePath.StartsWith(projectDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filePath = filePath.Substring(projectDirectory.Length);
|
||||
}
|
||||
|
||||
if (filePath.Length > 1)
|
||||
{
|
||||
filePath = filePath.Replace('\\', '/');
|
||||
|
||||
if (filePath[0] != '/')
|
||||
{
|
||||
filePath = "/" + filePath;
|
||||
}
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
|
||||
private class WorkParcel
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private readonly FileChangeTrackerFactory _fileChangeTrackerFactory;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ErrorReporter _errorReporter;
|
||||
private readonly RazorTemplateEngineFactoryService _templateEngineFactoryService;
|
||||
private readonly RazorProjectEngineFactoryService _projectEngineFactoryService;
|
||||
private readonly Dictionary<string, ImportTracker> _importTrackerCache;
|
||||
|
||||
public override event EventHandler<ImportChangedEventArgs> Changed;
|
||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
ForegroundDispatcher foregroundDispatcher,
|
||||
ErrorReporter errorReporter,
|
||||
FileChangeTrackerFactory fileChangeTrackerFactory,
|
||||
RazorTemplateEngineFactoryService templateEngineFactoryService)
|
||||
RazorProjectEngineFactoryService projectEngineFactoryService)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
|
|
@ -41,15 +42,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
||||
}
|
||||
|
||||
if (templateEngineFactoryService == null)
|
||||
if (projectEngineFactoryService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(templateEngineFactoryService));
|
||||
throw new ArgumentNullException(nameof(projectEngineFactoryService));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_errorReporter = errorReporter;
|
||||
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
||||
_templateEngineFactoryService = templateEngineFactoryService;
|
||||
_projectEngineFactoryService = projectEngineFactoryService;
|
||||
_importTrackerCache = new Dictionary<string, ImportTracker>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
|
@ -115,10 +116,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private IEnumerable<RazorProjectItem> GetImportItems(VisualStudioDocumentTracker tracker)
|
||||
{
|
||||
var projectDirectory = Path.GetDirectoryName(tracker.ProjectPath);
|
||||
var templateEngine = _templateEngineFactoryService.Create(projectDirectory, _ => { });
|
||||
var imports = templateEngine.GetImportItems(tracker.FilePath);
|
||||
var projectEngine = _projectEngineFactoryService.Create(projectDirectory, _ => { });
|
||||
var trackerItem = projectEngine.FileSystem.GetItem(tracker.FilePath);
|
||||
var importFeature = projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||
|
||||
return imports;
|
||||
// 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 importItems = importFeature?.GetImports(trackerItem) ?? Enumerable.Empty<RazorProjectItem>();
|
||||
var physicalImports = importItems.Where(import => import.FilePath != null);
|
||||
|
||||
return physicalImports;
|
||||
}
|
||||
|
||||
private void OnChanged(ImportTracker importTracker, FileChangeKind changeKind)
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
var errorReporter = languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>();
|
||||
var fileChangeTrackerFactory = languageServices.GetRequiredService<FileChangeTrackerFactory>();
|
||||
var templateEngineFactoryService = languageServices.GetRequiredService<RazorTemplateEngineFactoryService>();
|
||||
var projectEngineFactoryService = languageServices.GetRequiredService<RazorProjectEngineFactoryService>();
|
||||
|
||||
return new DefaultImportDocumentManager(
|
||||
_foregroundDispatcher,
|
||||
errorReporter,
|
||||
fileChangeTrackerFactory,
|
||||
templateEngineFactoryService);
|
||||
projectEngineFactoryService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue