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
|
Razor
|
||||||
=====
|
=====
|
||||||
|
|
||||||
AppVeyor: [](https://ci.appveyor.com/project/aspnetci/razor/branch/dev)
|
|
||||||
|
|
||||||
Travis: [](https://travis-ci.org/aspnet/Razor)
|
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.
|
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
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.27107.3000
|
VisualStudioVersion = 15.0.27130.2036
|
||||||
MinimumVisualStudioVersion = 15.0.26730.03
|
MinimumVisualStudioVersion = 15.0.26730.03
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C0D6505-79B3-49D0-B4C3-176F0F1836ED}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C0D6505-79B3-49D0-B4C3-176F0F1836ED}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
|
@ -92,9 +93,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Razo
|
||||||
EndProject
|
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}"
|
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
|
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
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{6EA56B2B-89EC-4C38-A384-97D203375B06}.ReleaseNoVSIX|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -440,6 +451,7 @@ Global
|
||||||
{3E7F2D49-3B45-45A8-9893-F73EC1EEBAAB} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
{3E7F2D49-3B45-45A8-9893-F73EC1EEBAAB} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||||
{7D9ECCEE-71D1-4A42-ABEE-876AFA1B4FC9} = {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}
|
{6EA56B2B-89EC-4C38-A384-97D203375B06} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||||
|
{72E89155-86C7-454E-BDD9-39F497F2F61B} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||||
|
|
@ -21,33 +20,22 @@ namespace Microsoft.AspNetCore.Razor.Performance
|
||||||
}
|
}
|
||||||
|
|
||||||
var root = current;
|
var root = current;
|
||||||
|
|
||||||
var engine = RazorEngine.Create(b => { RazorExtensions.Register(b); });
|
|
||||||
|
|
||||||
var fileSystem = RazorProjectFileSystem.Create(root.FullName);
|
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);
|
MSN = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RazorTemplateEngine DesignTimeTemplateEngine { get; }
|
public RazorProjectEngine ProjectEngine { get; }
|
||||||
|
|
||||||
public RazorTemplateEngine RuntimeTemplateEngine { get; }
|
public RazorProjectItem MSN { get; }
|
||||||
|
|
||||||
public IReadOnlyList<RazorSourceDocument> Imports { get; }
|
|
||||||
|
|
||||||
public RazorSourceDocument MSN { get; }
|
|
||||||
|
|
||||||
[Benchmark(Description = "Razor Design Time Code Generation of MSN.com")]
|
[Benchmark(Description = "Razor Design Time Code Generation of MSN.com")]
|
||||||
public void CodeGeneration_DesignTime_LargeStaticFile()
|
public void CodeGeneration_DesignTime_LargeStaticFile()
|
||||||
{
|
{
|
||||||
var codeDocument = RazorCodeDocument.Create(MSN, Imports);
|
var codeDocument = ProjectEngine.ProcessDesignTime(MSN);
|
||||||
var generated = DesignTimeTemplateEngine.GenerateCode(codeDocument);
|
var generated = codeDocument.GetCSharpDocument();
|
||||||
|
|
||||||
if (generated.Diagnostics.Count != 0)
|
if (generated.Diagnostics.Count != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -58,8 +46,8 @@ namespace Microsoft.AspNetCore.Razor.Performance
|
||||||
[Benchmark(Description = "Razor Runtime Code Generation of MSN.com")]
|
[Benchmark(Description = "Razor Runtime Code Generation of MSN.com")]
|
||||||
public void CodeGeneration_Runtime_LargeStaticFile()
|
public void CodeGeneration_Runtime_LargeStaticFile()
|
||||||
{
|
{
|
||||||
var codeDocument = RazorCodeDocument.Create(MSN, Imports);
|
var codeDocument = ProjectEngine.Process(MSN);
|
||||||
var generated = RuntimeTemplateEngine.GenerateCode(codeDocument);
|
var generated = codeDocument.GetCSharpDocument();
|
||||||
|
|
||||||
if (generated.Diagnostics.Count != 0)
|
if (generated.Diagnostics.Count != 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,23 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Package Versions">
|
<PropertyGroup Label="Package Versions">
|
||||||
<BenchmarkDotNetPackageVersion>0.10.11</BenchmarkDotNetPackageVersion>
|
<BenchmarkDotNetPackageVersion>0.10.11</BenchmarkDotNetPackageVersion>
|
||||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15698</InternalAspNetCoreSdkPackageVersion>
|
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15726</InternalAspNetCoreSdkPackageVersion>
|
||||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30230</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview2-30230</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreTestingPackageVersion>
|
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30230</MicrosoftAspNetCoreTestingPackageVersion>
|
||||||
<MicrosoftBuildFrameworkPackageVersion>15.3.409</MicrosoftBuildFrameworkPackageVersion>
|
<MicrosoftBuildFrameworkPackageVersion>15.6.82</MicrosoftBuildFrameworkPackageVersion>
|
||||||
<MicrosoftBuildPackageVersion>15.3.409</MicrosoftBuildPackageVersion>
|
<MicrosoftBuildPackageVersion>15.6.82</MicrosoftBuildPackageVersion>
|
||||||
<MicrosoftBuildUtilitiesCorePackageVersion>15.3.409</MicrosoftBuildUtilitiesCorePackageVersion>
|
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
|
||||||
<MicrosoftCodeAnalysisCommonPackageVersion>2.6.1</MicrosoftCodeAnalysisCommonPackageVersion>
|
<MicrosoftCodeAnalysisCommonPackageVersion>2.6.1</MicrosoftCodeAnalysisCommonPackageVersion>
|
||||||
<MicrosoftCodeAnalysisCSharpPackageVersion>2.6.1</MicrosoftCodeAnalysisCSharpPackageVersion>
|
<MicrosoftCodeAnalysisCSharpPackageVersion>2.6.1</MicrosoftCodeAnalysisCSharpPackageVersion>
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-25711-01</MicrosoftExtensionsDependencyModelPackageVersion>
|
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-25711-01</MicrosoftExtensionsDependencyModelPackageVersion>
|
||||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview2-30077</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview2-30230</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26130-04</MicrosoftNETCoreApp21PackageVersion>
|
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26225-03</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.3.0</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.6.0</MicrosoftNETTestSdkPackageVersion>
|
||||||
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
|
<MicrosoftVisualStudioComponentModelHostPackageVersion>15.0.26606</MicrosoftVisualStudioComponentModelHostPackageVersion>
|
||||||
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
|
<MicrosoftVisualStudioEditorPackageVersion>15.6.161-preview</MicrosoftVisualStudioEditorPackageVersion>
|
||||||
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>15.6.161-preview</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
<MicrosoftVisualStudioLanguageIntellisensePackageVersion>15.6.161-preview</MicrosoftVisualStudioLanguageIntellisensePackageVersion>
|
||||||
|
|
@ -42,8 +42,8 @@
|
||||||
<NETStandardLibrary20PackageVersion>2.0.1</NETStandardLibrary20PackageVersion>
|
<NETStandardLibrary20PackageVersion>2.0.1</NETStandardLibrary20PackageVersion>
|
||||||
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
||||||
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
|
<StreamJsonRpcPackageVersion>1.1.92</StreamJsonRpcPackageVersion>
|
||||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview2-26130-01</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview2-26224-02</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||||
<SystemValueTuplePackageVersion>4.5.0-preview2-26130-01</SystemValueTuplePackageVersion>
|
<SystemValueTuplePackageVersion>4.5.0-preview2-26224-02</SystemValueTuplePackageVersion>
|
||||||
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
|
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
|
||||||
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
|
<VSIX_MicrosoftCodeAnalysisCommonPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisCommonPackageVersion>
|
||||||
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
|
<VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
<Project>
|
<Project>
|
||||||
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
||||||
|
|
||||||
<PropertyGroup Label="RestoreSources">
|
<PropertyGroup Label="RestoreSources">
|
||||||
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
||||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
||||||
$(RestoreSources);
|
$(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-dev/api/v3/index.json;
|
||||||
https://dotnet.myget.org/F/aspnetcore-tools/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;
|
https://dotnet.myget.org/F/msbuild/api/v3/index.json;
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
version:2.1.0-preview2-15698
|
version:2.1.0-preview2-15726
|
||||||
commithash:7216e5068cb1957e09d45fcbe58a744dd5c2de73
|
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.AddDirective(Directive);
|
||||||
builder.Features.Add(new Pass(builder.Configuration.DesignTime));
|
builder.Features.Add(new Pass());
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,13 +64,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||||
|
|
||||||
internal class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
internal class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||||
{
|
{
|
||||||
private readonly bool _designTime;
|
|
||||||
|
|
||||||
public Pass(bool designTime)
|
|
||||||
{
|
|
||||||
_designTime = designTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs after the @inherits directive
|
// Runs after the @inherits directive
|
||||||
public override int Order => 5;
|
public override int Order => 5;
|
||||||
|
|
||||||
|
|
@ -79,7 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||||
var visitor = new Visitor();
|
var visitor = new Visitor();
|
||||||
var modelType = GetModelType(documentNode, visitor);
|
var modelType = GetModelType(documentNode, visitor);
|
||||||
|
|
||||||
if (_designTime)
|
if (documentNode.Options.DesignTime)
|
||||||
{
|
{
|
||||||
// Alias the TModel token to a known type.
|
// Alias the TModel token to a known type.
|
||||||
// This allows design time compilation to succeed for Razor files where the token isn't replaced.
|
// 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.AddDirective(Directive);
|
||||||
builder.Features.Add(new Pass(builder.DesignTime));
|
builder.Features.Add(new Pass());
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
#endregion
|
#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));
|
throw new ArgumentNullException(nameof(builder));
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureDesignTime(builder);
|
|
||||||
|
|
||||||
InjectDirective.Register(builder);
|
InjectDirective.Register(builder);
|
||||||
ModelDirective.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 ModelExpressionPass());
|
||||||
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
||||||
|
|
||||||
builder.SetImportFeature(new DefaultMvcImportFeature());
|
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisterViewComponentTagHelpers(RazorProjectEngineBuilder builder)
|
public static void RegisterViewComponentTagHelpers(RazorProjectEngineBuilder builder)
|
||||||
|
|
@ -47,22 +45,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||||
throw new ArgumentNullException(nameof(builder));
|
throw new ArgumentNullException(nameof(builder));
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureDesignTime(builder);
|
|
||||||
|
|
||||||
builder.Features.Add(new ViewComponentTagHelperPass());
|
builder.Features.Add(new ViewComponentTagHelperPass());
|
||||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EnsureDesignTime(RazorProjectEngineBuilder builder)
|
|
||||||
{
|
|
||||||
if (builder.Configuration.DesignTime)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException(Resources.RuntimeCodeGenerationNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Obsolete
|
#region Obsolete
|
||||||
public static void Register(IRazorEngineBuilder builder)
|
public static void Register(IRazorEngineBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||||
|
|
||||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||||
{
|
{
|
||||||
|
if (documentNode.Options.DesignTime)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var @namespace = documentNode.FindPrimaryNamespace();
|
var @namespace = documentNode.FindPrimaryNamespace();
|
||||||
if (@namespace == null || string.IsNullOrEmpty(@namespace.Content))
|
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)
|
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||||
{
|
{
|
||||||
|
if (documentNode.Options.DesignTime)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var walker = new Visitor();
|
var walker = new Visitor();
|
||||||
walker.VisitDocument(documentNode);
|
walker.VisitDocument(documentNode);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddDirective(Directive);
|
builder.AddDirective(Directive);
|
||||||
builder.Features.Add(new Pass(builder.Configuration.DesignTime));
|
builder.Features.Add(new Pass());
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,13 +71,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||||
|
|
||||||
internal class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
internal class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||||
{
|
{
|
||||||
private readonly bool _designTime;
|
|
||||||
|
|
||||||
public Pass(bool designTime)
|
|
||||||
{
|
|
||||||
_designTime = designTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs after the @inherits directive
|
// Runs after the @inherits directive
|
||||||
public override int Order => 5;
|
public override int Order => 5;
|
||||||
|
|
||||||
|
|
@ -86,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||||
var visitor = new Visitor();
|
var visitor = new Visitor();
|
||||||
var modelType = GetModelType(documentNode, visitor);
|
var modelType = GetModelType(documentNode, visitor);
|
||||||
|
|
||||||
if (_designTime)
|
if (documentNode.Options.DesignTime)
|
||||||
{
|
{
|
||||||
// Alias the TModel token to a known type.
|
// Alias the TModel token to a known type.
|
||||||
// This allows design time compilation to succeed for Razor files where the token isn't replaced.
|
// 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.AddDirective(Directive);
|
||||||
builder.Features.Add(new Pass(builder.DesignTime));
|
builder.Features.Add(new Pass());
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
#endregion
|
#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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
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.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||||
InheritsDirective.Register(builder);
|
InheritsDirective.Register(builder);
|
||||||
SectionDirective.Register(builder);
|
SectionDirective.Register(builder);
|
||||||
|
|
||||||
|
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||||
|
|
||||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||||
builder.AddTargetExtension(new TemplateTargetExtension()
|
builder.AddTargetExtension(new TemplateTargetExtension()
|
||||||
{
|
{
|
||||||
|
|
@ -36,14 +38,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||||
builder.Features.Add(new ViewComponentTagHelperPass());
|
builder.Features.Add(new ViewComponentTagHelperPass());
|
||||||
builder.Features.Add(new RazorPageDocumentClassifierPass());
|
builder.Features.Add(new RazorPageDocumentClassifierPass());
|
||||||
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
||||||
|
builder.Features.Add(new AssemblyAttributeInjectionPass());
|
||||||
|
builder.Features.Add(new InstrumentationPass());
|
||||||
|
|
||||||
if (!builder.Configuration.DesignTime)
|
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||||
{
|
|
||||||
builder.Features.Add(new AssemblyAttributeInjectionPass());
|
|
||||||
builder.Features.Add(new InstrumentationPass());
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.SetImportFeature(new DefaultMvcImportFeature());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Obsolete
|
#region Obsolete
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@
|
||||||
Set the primary configuration supported by this pacakge as the default configuration for Razor.
|
Set the primary configuration supported by this pacakge as the default configuration for Razor.
|
||||||
-->
|
-->
|
||||||
<RazorDefaultConfiguration Condition="'$(RazorDefaultConfiguration)'==''">MVC-2.1</RazorDefaultConfiguration>
|
<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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -26,7 +29,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<RazorExtension Include="MVC-2.1">
|
<RazorExtension Include="MVC-2.1">
|
||||||
<AssemblyName>Microsoft.AspNetCore.Mvc.Razor.Extensions</AssemblyName>
|
<AssemblyName>Microsoft.AspNetCore.Mvc.Razor.Extensions</AssemblyName>
|
||||||
<AssemblyFilePath>$(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll</AssemblyFilePath>
|
<AssemblyFilePath>$(_MvcExtensionAssemblyPath)</AssemblyFilePath>
|
||||||
</RazorExtension>
|
</RazorExtension>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -5,5 +5,10 @@
|
||||||
MVC will generally want to add support for runtime compilation, but only for applications.
|
MVC will generally want to add support for runtime compilation, but only for applications.
|
||||||
-->
|
-->
|
||||||
<GenerateRazorAssemblyInfo Condition="'$(GenerateRazorAssemblyInfo)'=='' and '$(OutputType)'=='Exe'">true</GenerateRazorAssemblyInfo>
|
<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>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -60,11 +60,11 @@
|
||||||
-->
|
-->
|
||||||
<Touch
|
<Touch
|
||||||
Files="$(_RazorTagHelperInputCache)"
|
Files="$(_RazorTagHelperInputCache)"
|
||||||
AlwaysCreate="true">
|
AlwaysCreate="true" />
|
||||||
<Output
|
|
||||||
TaskParameter="TouchedFiles"
|
<ItemGroup>
|
||||||
ItemName="FileWrites" />
|
<FileWrites Include="$(_RazorTagHelperInputCache)" />
|
||||||
</Touch>
|
</ItemGroup>
|
||||||
|
|
||||||
<RazorTagHelper
|
<RazorTagHelper
|
||||||
Debug="$(_RazorDebugTagHelperTask)"
|
Debug="$(_RazorDebugTagHelperTask)"
|
||||||
|
|
@ -73,6 +73,9 @@
|
||||||
UseServer="$(UseRazorBuildServer)"
|
UseServer="$(UseRazorBuildServer)"
|
||||||
ForceServer="$(_RazorForceBuildServer)"
|
ForceServer="$(_RazorForceBuildServer)"
|
||||||
PipeName="$(_RazorBuildServerPipeName)"
|
PipeName="$(_RazorBuildServerPipeName)"
|
||||||
|
Version="$(RazorLangVersion)"
|
||||||
|
Configuration="@(ResolvedRazorConfiguration)"
|
||||||
|
Extensions="@(ResolvedRazorExtension)"
|
||||||
Assemblies="@(RazorReferencePath)"
|
Assemblies="@(RazorReferencePath)"
|
||||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||||
TagHelperManifest="$(_RazorTagHelperOutputCache)">
|
TagHelperManifest="$(_RazorTagHelperOutputCache)">
|
||||||
|
|
@ -121,6 +124,9 @@
|
||||||
UseServer="$(UseRazorBuildServer)"
|
UseServer="$(UseRazorBuildServer)"
|
||||||
ForceServer="$(_RazorForceBuildServer)"
|
ForceServer="$(_RazorForceBuildServer)"
|
||||||
PipeName="$(_RazorBuildServerPipeName)"
|
PipeName="$(_RazorBuildServerPipeName)"
|
||||||
|
Version="$(RazorLangVersion)"
|
||||||
|
Configuration="@(ResolvedRazorConfiguration)"
|
||||||
|
Extensions="@(ResolvedRazorExtension)"
|
||||||
Sources="@(RazorGenerateWithTargetPath)"
|
Sources="@(RazorGenerateWithTargetPath)"
|
||||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||||
TagHelperManifest="$(_RazorTagHelperOutputCache)" />
|
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)
|
public CSharpCodeWritingScope(CodeWriter writer, int tabSize = 4, bool autoSpace = true)
|
||||||
{
|
{
|
||||||
_writer = writer;
|
_writer = writer;
|
||||||
_autoSpace = true;
|
_autoSpace = autoSpace;
|
||||||
_tabSize = tabSize;
|
_tabSize = tabSize;
|
||||||
_startIndent = -1; // Set in WriteStartScope
|
_startIndent = -1; // Set in WriteStartScope
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,35 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||||
Context.CodeWriter.Write(" ");
|
Context.CodeWriter.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.CodeWriter
|
Context.CodeWriter.Write(node.ReturnType);
|
||||||
.Write(node.ReturnType)
|
Context.CodeWriter.Write(" ");
|
||||||
.Write(" ")
|
|
||||||
.Write(node.MethodName)
|
Context.CodeWriter.Write(node.MethodName);
|
||||||
.WriteLine("()");
|
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())
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||||
{
|
{
|
||||||
public abstract class DocumentWriter
|
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)
|
public DocumentWriter Create(CodeTarget codeTarget, RazorCodeGenerationOptions options)
|
||||||
{
|
{
|
||||||
if (codeTarget == null)
|
if (codeTarget == null)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
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);
|
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);
|
var cSharpDocument = writer.WriteDocument(codeDocument, documentNode);
|
||||||
codeDocument.SetCSharpDocument(cSharpDocument);
|
codeDocument.SetCSharpDocument(cSharpDocument);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,30 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
internal class DefaultRazorCodeGenerationOptionsBuilder : RazorCodeGenerationOptionsBuilder
|
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;
|
public override int IndentSize { get; set; } = 4;
|
||||||
|
|
||||||
|
|
@ -22,5 +36,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
return new DefaultRazorCodeGenerationOptions(IndentWithTabs, IndentSize, DesignTime, SuppressChecksum, SuppressMetadataAttributes);
|
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
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
internal class DefaultRazorCodeGenerationOptionsFeature : RazorEngineFeatureBase, IRazorCodeGenerationOptionsFeature
|
internal class DefaultRazorCodeGenerationOptionsFeature : RazorEngineFeatureBase, IRazorCodeGenerationOptionsFeature
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
{
|
{
|
||||||
private readonly bool _designTime;
|
private readonly bool _designTime;
|
||||||
private IConfigureRazorCodeGenerationOptionsFeature[] _configureOptions;
|
private IConfigureRazorCodeGenerationOptionsFeature[] _configureOptions;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
internal class DefaultRazorIntermediateNodeLoweringPhase : RazorEnginePhaseBase, IRazorIntermediateNodeLoweringPhase
|
internal class DefaultRazorIntermediateNodeLoweringPhase : RazorEnginePhaseBase, IRazorIntermediateNodeLoweringPhase
|
||||||
{
|
{
|
||||||
private IRazorCodeGenerationOptionsFeature _optionsFeature;
|
private IRazorCodeGenerationOptionsFeature _optionsFeature;
|
||||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
var document = new DocumentIntermediateNode();
|
var document = new DocumentIntermediateNode();
|
||||||
var builder = IntermediateNodeBuilder.Create(document);
|
var builder = IntermediateNodeBuilder.Create(document);
|
||||||
|
|
||||||
document.Options = _optionsFeature.GetOptions();
|
document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions();
|
||||||
|
|
||||||
var namespaces = new Dictionary<string, SourceSpan?>(StringComparer.Ordinal);
|
var namespaces = new Dictionary<string, SourceSpan?>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
|
@ -785,4 +786,5 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
private static bool IsMalformed(List<RazorDiagnostic> diagnostics)
|
private static bool IsMalformed(List<RazorDiagnostic> diagnostics)
|
||||||
=> diagnostics.Count > 0 && diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
=> 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
|
internal class DefaultRazorParserOptionsBuilder : RazorParserOptionsBuilder
|
||||||
{
|
{
|
||||||
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
|
private bool _designTime;
|
||||||
|
|
||||||
|
public DefaultRazorParserOptionsBuilder(RazorConfiguration configuration)
|
||||||
{
|
{
|
||||||
DesignTime = designTime;
|
if (configuration == null)
|
||||||
Version = version;
|
{
|
||||||
|
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 ICollection<DirectiveDescriptor> Directives { get; } = new List<DirectiveDescriptor>();
|
||||||
|
|
||||||
public override bool ParseLeadingDirectives { get; set; }
|
public override bool ParseLeadingDirectives { get; set; }
|
||||||
|
|
||||||
public override RazorLanguageVersion Version { get; }
|
public override RazorLanguageVersion LanguageVersion { get; }
|
||||||
|
|
||||||
public override RazorParserOptions Build()
|
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
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
internal class DefaultRazorParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
|
internal class DefaultRazorParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
{
|
{
|
||||||
private readonly bool _designTime;
|
private readonly bool _designTime;
|
||||||
private readonly RazorLanguageVersion _version;
|
private readonly RazorLanguageVersion _version;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
internal class DefaultRazorParsingPhase : RazorEnginePhaseBase, IRazorParsingPhase
|
internal class DefaultRazorParsingPhase : RazorEnginePhaseBase, IRazorParsingPhase
|
||||||
{
|
{
|
||||||
private IRazorParserOptionsFeature _optionsFeature;
|
private IRazorParserOptionsFeature _optionsFeature;
|
||||||
|
|
@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
|
|
||||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||||
{
|
{
|
||||||
var options = _optionsFeature.GetOptions();
|
var options = codeDocument.GetParserOptions() ??_optionsFeature.GetOptions();
|
||||||
var syntaxTree = RazorSyntaxTree.Parse(codeDocument.Source, options);
|
var syntaxTree = RazorSyntaxTree.Parse(codeDocument.Source, options);
|
||||||
codeDocument.SetSyntaxTree(syntaxTree);
|
codeDocument.SetSyntaxTree(syntaxTree);
|
||||||
|
|
||||||
|
|
@ -26,4 +27,5 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
codeDocument.SetImportSyntaxTrees(importSyntaxTrees);
|
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
|
internal class DefaultRazorProjectEngine : RazorProjectEngine
|
||||||
{
|
{
|
||||||
public DefaultRazorProjectEngine(
|
public DefaultRazorProjectEngine(
|
||||||
|
RazorConfiguration configuration,
|
||||||
RazorEngine engine,
|
RazorEngine engine,
|
||||||
RazorProjectFileSystem fileSystem,
|
RazorProjectFileSystem fileSystem,
|
||||||
IReadOnlyList<IRazorProjectEngineFeature> features)
|
IReadOnlyList<IRazorProjectEngineFeature> projectFeatures)
|
||||||
{
|
{
|
||||||
|
if (configuration == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configuration));
|
||||||
|
}
|
||||||
|
|
||||||
if (engine == null)
|
if (engine == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(engine));
|
throw new ArgumentNullException(nameof(engine));
|
||||||
|
|
@ -24,48 +30,81 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
throw new ArgumentNullException(nameof(fileSystem));
|
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;
|
Engine = engine;
|
||||||
FileSystem = fileSystem;
|
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 RazorProjectFileSystem FileSystem { get; }
|
||||||
|
|
||||||
public override RazorEngine Engine { 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)
|
if (projectItem == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(projectItem));
|
throw new ArgumentNullException(nameof(projectItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
var importFeature = GetRequiredFeature<IRazorImportFeature>();
|
|
||||||
var imports = importFeature.GetImports(projectItem);
|
|
||||||
var sourceDocument = RazorSourceDocument.ReadFrom(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);
|
Engine.Process(codeDocument);
|
||||||
|
|
||||||
return codeDocument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TFeature GetRequiredFeature<TFeature>() where TFeature : IRazorProjectEngineFeature
|
private TFeature GetRequiredFeature<TFeature>() where TFeature : IRazorProjectEngineFeature
|
||||||
{
|
{
|
||||||
var feature = Features.OfType<TFeature>().FirstOrDefault();
|
var feature = ProjectFeatures.OfType<TFeature>().FirstOrDefault();
|
||||||
if (feature == null)
|
if (feature == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
|
|
@ -76,5 +115,43 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
|
|
||||||
return feature;
|
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>();
|
Phases = new List<IRazorEnginePhase>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override RazorConfiguration Configuration { get; }
|
||||||
|
|
||||||
public override RazorProjectFileSystem FileSystem { get; }
|
public override RazorProjectFileSystem FileSystem { get; }
|
||||||
|
|
||||||
public override ICollection<IRazorFeature> Features { get; }
|
public override ICollection<IRazorFeature> Features { get; }
|
||||||
|
|
||||||
public override IList<IRazorEnginePhase> Phases { get; }
|
public override IList<IRazorEnginePhase> Phases { get; }
|
||||||
|
|
||||||
public override RazorConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
public override RazorProjectEngine Build()
|
public override RazorProjectEngine Build()
|
||||||
{
|
{
|
||||||
RazorEngine engine = null;
|
var engine = RazorEngine.CreateEmpty(ConfigureRazorEngine);
|
||||||
|
var projectFeatures = Features.OfType<IRazorProjectEngineFeature>().ToArray();
|
||||||
if (Configuration.DesignTime)
|
var projectEngine = new DefaultRazorProjectEngine(Configuration, engine, FileSystem, projectFeatures);
|
||||||
{
|
|
||||||
engine = RazorEngine.CreateDesignTimeEmpty(ConfigureRazorEngine);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
engine = RazorEngine.CreateEmpty(ConfigureRazorEngine);
|
|
||||||
}
|
|
||||||
|
|
||||||
var projectEngineFeatures = Features.OfType<IRazorProjectEngineFeature>().ToArray();
|
|
||||||
var projectEngine = new DefaultRazorProjectEngine(engine, FileSystem, projectEngineFeatures);
|
|
||||||
|
|
||||||
return projectEngine;
|
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" };
|
private static readonly string[] PrivateModifiers = new string[] { "private" };
|
||||||
|
|
||||||
public bool DesignTime { get; set; }
|
|
||||||
|
|
||||||
public string RunnerVariableName { get; set; } = "__tagHelperRunner";
|
public string RunnerVariableName { get; set; } = "__tagHelperRunner";
|
||||||
|
|
||||||
public string StringValueBufferVariableName { get; set; } = "__tagHelperStringValueBuffer";
|
public string StringValueBufferVariableName { get; set; } = "__tagHelperStringValueBuffer";
|
||||||
|
|
@ -82,7 +80,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DesignTime)
|
if (context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
context.RenderChildren(node);
|
context.RenderChildren(node);
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +134,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
.Write(CreateTagHelperMethodName)
|
.Write(CreateTagHelperMethodName)
|
||||||
.WriteLine("<global::" + node.TypeName + ">();");
|
.WriteLine("<global::" + node.TypeName + ">();");
|
||||||
|
|
||||||
if (!DesignTime)
|
if (!context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
context.CodeWriter.WriteInstanceMethodInvocation(
|
context.CodeWriter.WriteInstanceMethodInvocation(
|
||||||
ExecutionContextVariableName,
|
ExecutionContextVariableName,
|
||||||
|
|
@ -153,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DesignTime)
|
if (!context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.Write("await ")
|
.Write("await ")
|
||||||
|
|
@ -200,7 +198,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DesignTime)
|
if (context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
context.RenderChildren(node);
|
context.RenderChildren(node);
|
||||||
}
|
}
|
||||||
|
|
@ -284,7 +282,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
throw new InvalidOperationException(message);
|
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.
|
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
|
||||||
if (node.IsIndexerNameMatch &&
|
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 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 (node.BoundAttribute.ExpectsStringValue(node.AttributeName))
|
||||||
{
|
{
|
||||||
if (DesignTime)
|
if (context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
context.RenderChildren(node);
|
context.RenderChildren(node);
|
||||||
|
|
||||||
|
|
@ -370,7 +368,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (DesignTime)
|
if (context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null) as IntermediateNode;
|
var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null) as IntermediateNode;
|
||||||
var valueStart = firstMappedChild?.Source;
|
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.
|
// We need to inform the context of the attribute value.
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
|
|
@ -474,7 +472,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
|
|
||||||
public void WriteTagHelperRuntime(CodeRenderingContext context, DefaultTagHelperRuntimeIntermediateNode node)
|
public void WriteTagHelperRuntime(CodeRenderingContext context, DefaultTagHelperRuntimeIntermediateNode node)
|
||||||
{
|
{
|
||||||
if (!DesignTime)
|
if (!context.Options.DesignTime)
|
||||||
{
|
{
|
||||||
context.CodeWriter.WriteLine("#line hidden");
|
context.CodeWriter.WriteLine("#line hidden");
|
||||||
|
|
||||||
|
|
@ -566,7 +564,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
}
|
}
|
||||||
else if (node is IntermediateToken token)
|
else if (node is IntermediateToken token)
|
||||||
{
|
{
|
||||||
if (DesignTime && node.Source != null)
|
if (context.Options.DesignTime && node.Source != null)
|
||||||
{
|
{
|
||||||
context.AddSourceMappingFor(node);
|
context.AddSourceMappingFor(node);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
|
|
||||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
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();
|
var walker = new DesignTimeHelperWalker();
|
||||||
walker.VisitDocument(documentNode);
|
walker.VisitDocument(documentNode);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||||
|
|
||||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
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();
|
var walker = new PreallocatedTagHelperWalker();
|
||||||
walker.VisitDocument(documentNode);
|
walker.VisitDocument(documentNode);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
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.
|
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
|
[Obsolete("In Razor 2.1 and newer, use RazorCodeDocument.GetCodeGenerationOptions().")]
|
||||||
public interface IRazorCodeGenerationOptionsFeature : IRazorEngineFeature
|
public interface IRazorCodeGenerationOptionsFeature : IRazorEngineFeature
|
||||||
{
|
{
|
||||||
RazorCodeGenerationOptions GetOptions();
|
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.
|
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
|
[Obsolete("In Razor 2.1 and newer, use RazorCodeDocument.GetParserOptions().")]
|
||||||
public interface IRazorParserOptionsFeature : IRazorEngineFeature
|
public interface IRazorParserOptionsFeature : IRazorEngineFeature
|
||||||
{
|
{
|
||||||
RazorParserOptions GetOptions();
|
RazorParserOptions GetOptions();
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||||
|
|
||||||
public string MethodName { get; set; }
|
public string MethodName { get; set; }
|
||||||
|
|
||||||
|
public IList<MethodParameter> Parameters { get; } = new List<MethodParameter>();
|
||||||
|
|
||||||
public string ReturnType { get; set; }
|
public string ReturnType { get; set; }
|
||||||
|
|
||||||
public override void Accept(IntermediateNodeVisitor visitor)
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
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.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.GenerateTool, 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);
|
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 IReadOnlyList<RazorSourceDocument> Imports { get; }
|
||||||
|
|
||||||
public abstract ItemCollection Items { get; }
|
public abstract ItemCollection Items { get; }
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,46 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
document.Items[typeof(RazorCSharpDocument)] = csharp;
|
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
|
private class ImportSyntaxTreesHolder
|
||||||
{
|
{
|
||||||
public ImportSyntaxTreesHolder(IReadOnlyList<RazorSyntaxTree> syntaxTrees)
|
public ImportSyntaxTreesHolder(IReadOnlyList<RazorSyntaxTree> syntaxTrees)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
public abstract class RazorCodeGenerationOptionsBuilder
|
public abstract class RazorCodeGenerationOptionsBuilder
|
||||||
{
|
{
|
||||||
|
public virtual RazorConfiguration Configuration => null;
|
||||||
|
|
||||||
public abstract bool DesignTime { get; }
|
public abstract bool DesignTime { get; }
|
||||||
|
|
||||||
public abstract int IndentSize { get; set; }
|
public abstract int IndentSize { get; set; }
|
||||||
|
|
@ -41,5 +43,9 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
public virtual bool SuppressMetadataAttributes { get; set; }
|
public virtual bool SuppressMetadataAttributes { get; set; }
|
||||||
|
|
||||||
public abstract RazorCodeGenerationOptions Build();
|
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(
|
public static readonly RazorConfiguration Default = new RazorConfiguration(
|
||||||
RazorLanguageVersion.Latest,
|
RazorLanguageVersion.Latest,
|
||||||
"unnamed",
|
"unnamed",
|
||||||
Array.Empty<RazorExtension>(),
|
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);
|
|
||||||
|
|
||||||
public RazorConfiguration(
|
public RazorConfiguration(
|
||||||
RazorLanguageVersion languageVersion,
|
RazorLanguageVersion languageVersion,
|
||||||
string configurationName,
|
string configurationName,
|
||||||
IEnumerable<RazorExtension> extensions,
|
IEnumerable<RazorExtension> extensions)
|
||||||
bool designTime)
|
|
||||||
{
|
{
|
||||||
if (languageVersion == null)
|
if (languageVersion == null)
|
||||||
{
|
{
|
||||||
|
|
@ -47,7 +37,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
LanguageVersion = languageVersion;
|
LanguageVersion = languageVersion;
|
||||||
ConfigurationName = configurationName;
|
ConfigurationName = configurationName;
|
||||||
Extensions = extensions.ToArray();
|
Extensions = extensions.ToArray();
|
||||||
DesignTime = designTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ConfigurationName { get; }
|
public string ConfigurationName { get; }
|
||||||
|
|
@ -55,7 +44,5 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
public IReadOnlyList<RazorExtension> Extensions { get; }
|
public IReadOnlyList<RazorExtension> Extensions { get; }
|
||||||
|
|
||||||
public RazorLanguageVersion LanguageVersion { get; }
|
public RazorLanguageVersion LanguageVersion { get; }
|
||||||
|
|
||||||
public bool DesignTime { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,27 +17,27 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
return Create(configure: null);
|
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()
|
public static RazorEngine CreateDesignTime()
|
||||||
{
|
{
|
||||||
return CreateDesignTime(configure: null);
|
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 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)
|
if (configuration == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(configuration));
|
throw new ArgumentNullException(nameof(configuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new DefaultRazorEngineBuilder(configuration.DesignTime);
|
var builder = new DefaultRazorEngineBuilder(designTime);
|
||||||
AddDefaults(builder);
|
AddDefaults(builder);
|
||||||
|
|
||||||
if (configuration.DesignTime)
|
if (designTime)
|
||||||
{
|
{
|
||||||
AddDefaultDesignTimeFeatures(configuration, builder.Features);
|
AddDefaultDesignTimeFeatures(configuration, builder.Features);
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
var targetExtension = features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
var targetExtension = features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
||||||
Debug.Assert(targetExtension != null);
|
Debug.Assert(targetExtension != null);
|
||||||
|
|
||||||
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension() { DesignTime = false });
|
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension());
|
||||||
targetExtension.TargetExtensions.Add(new PreallocatedAttributeTargetExtension());
|
targetExtension.TargetExtensions.Add(new PreallocatedAttributeTargetExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
var targetExtension = features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
var targetExtension = features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
||||||
Debug.Assert(targetExtension != null);
|
Debug.Assert(targetExtension != null);
|
||||||
|
|
||||||
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension() { DesignTime = true });
|
targetExtension.TargetExtensions.Add(new DefaultTagHelperTargetExtension());
|
||||||
targetExtension.TargetExtensions.Add(new DesignTimeDirectiveTargetExtension());
|
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.
|
// 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.
|
// 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.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
public abstract class RazorParserOptionsBuilder
|
public abstract class RazorParserOptionsBuilder
|
||||||
{
|
{
|
||||||
|
public virtual RazorConfiguration Configuration => null;
|
||||||
|
|
||||||
public abstract bool DesignTime { get; }
|
public abstract bool DesignTime { get; }
|
||||||
|
|
||||||
public abstract ICollection<DirectiveDescriptor> Directives { get; }
|
public abstract ICollection<DirectiveDescriptor> Directives { get; }
|
||||||
|
|
||||||
public abstract bool ParseLeadingDirectives { get; set; }
|
public abstract bool ParseLeadingDirectives { get; set; }
|
||||||
|
|
||||||
public virtual RazorLanguageVersion Version { get; }
|
public virtual RazorLanguageVersion LanguageVersion { get; }
|
||||||
|
|
||||||
public abstract RazorParserOptions Build();
|
public abstract RazorParserOptions Build();
|
||||||
|
|
||||||
|
public virtual void SetDesignTime(bool designTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,61 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
public abstract class RazorProjectEngine
|
public abstract class RazorProjectEngine
|
||||||
{
|
{
|
||||||
|
public abstract RazorConfiguration Configuration { get; }
|
||||||
|
|
||||||
public abstract RazorProjectFileSystem FileSystem { get; }
|
public abstract RazorProjectFileSystem FileSystem { get; }
|
||||||
|
|
||||||
public abstract RazorEngine Engine { 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(
|
public static RazorProjectEngine Create(
|
||||||
RazorProjectFileSystem fileSystem,
|
|
||||||
RazorConfiguration configuration,
|
RazorConfiguration configuration,
|
||||||
|
RazorProjectFileSystem fileSystem,
|
||||||
Action<RazorProjectEngineBuilder> configure)
|
Action<RazorProjectEngineBuilder> configure)
|
||||||
{
|
{
|
||||||
if (fileSystem == null)
|
if (fileSystem == null)
|
||||||
|
|
@ -37,54 +72,96 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
|
|
||||||
var builder = new DefaultRazorProjectEngineBuilder(configuration, fileSystem);
|
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)
|
LoadExtensions(builder, configuration.Extensions);
|
||||||
{
|
|
||||||
AddDesignTimeDefaults(builder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddRuntimeDefaults(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
configure?.Invoke(builder);
|
configure?.Invoke(builder);
|
||||||
|
|
||||||
return builder.Build();
|
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)
|
// General extensibility
|
||||||
{
|
features.Add(new DefaultRazorDirectiveFeature());
|
||||||
var engineFeatures = new List<IRazorEngineFeature>();
|
features.Add(new DefaultMetadataIdentifierFeature());
|
||||||
RazorEngine.AddDefaultFeatures(engineFeatures);
|
|
||||||
RazorEngine.AddDefaultDesignTimeFeatures(builder.Configuration, engineFeatures);
|
|
||||||
|
|
||||||
AddEngineFeaturesAndPhases(builder, engineFeatures);
|
// Options features
|
||||||
}
|
features.Add(new DefaultRazorParserOptionsFactoryProjectFeature());
|
||||||
|
features.Add(new DefaultRazorCodeGenerationOptionsFactoryProjectFeature());
|
||||||
|
|
||||||
private static void AddRuntimeDefaults(RazorProjectEngineBuilder builder)
|
// Legacy options features
|
||||||
{
|
//
|
||||||
var engineFeatures = new List<IRazorEngineFeature>();
|
// These features are obsolete as of 2.1. Our code will resolve this but not invoke them.
|
||||||
RazorEngine.AddDefaultFeatures(engineFeatures);
|
features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Version_2_0));
|
||||||
RazorEngine.AddDefaultRuntimeFeatures(builder.Configuration, engineFeatures);
|
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)
|
// Intermediate Node Passes
|
||||||
{
|
features.Add(new DefaultDocumentClassifierPass());
|
||||||
for (var i = 0; i < engineFeatures.Count; i++)
|
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];
|
@class.ClassName = "Template";
|
||||||
builder.Features.Add(engineFeature);
|
@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 class RazorProjectEngineBuilder
|
||||||
{
|
{
|
||||||
|
public abstract RazorConfiguration Configuration { get; }
|
||||||
|
|
||||||
public abstract RazorProjectFileSystem FileSystem { get; }
|
public abstract RazorProjectFileSystem FileSystem { get; }
|
||||||
|
|
||||||
public abstract ICollection<IRazorFeature> Features { get; }
|
public abstract ICollection<IRazorFeature> Features { get; }
|
||||||
|
|
||||||
public abstract IList<IRazorEnginePhase> Phases { get; }
|
public abstract IList<IRazorEnginePhase> Phases { get; }
|
||||||
|
|
||||||
public abstract RazorConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
public abstract RazorProjectEngine Build();
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
public static class RazorProjectEngineBuilderExtensions
|
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)
|
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.
|
// 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)
|
foreach (var existingFeature in existingFeatures)
|
||||||
{
|
{
|
||||||
builder.Features.Remove(existingFeature);
|
builder.Features.Remove(existingFeature);
|
||||||
|
|
@ -79,6 +145,27 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
return builder;
|
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)
|
private static IRazorDirectiveFeature GetDirectiveFeature(RazorProjectEngineBuilder builder)
|
||||||
{
|
{
|
||||||
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||||
|
|
@ -102,5 +189,77 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
|
|
||||||
return targetExtensionFeature;
|
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
|
public abstract class RazorProjectFileSystem : RazorProject
|
||||||
{
|
{
|
||||||
|
internal static readonly RazorProjectFileSystem Empty = new EmptyProjectFileSystem();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a Razor project file system based off of a root directory.
|
/// Create a Razor project file system based off of a root directory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,28 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
{
|
{
|
||||||
public class RazorGenerate : DotNetToolTask
|
public class RazorGenerate : DotNetToolTask
|
||||||
{
|
{
|
||||||
|
private static readonly string[] SourceRequiredMetadata = new string[]
|
||||||
|
{
|
||||||
|
FullPath,
|
||||||
|
GeneratedOutput,
|
||||||
|
TargetPath,
|
||||||
|
};
|
||||||
|
|
||||||
private const string GeneratedOutput = "GeneratedOutput";
|
private const string GeneratedOutput = "GeneratedOutput";
|
||||||
private const string TargetPath = "TargetPath";
|
private const string TargetPath = "TargetPath";
|
||||||
private const string FullPath = "FullPath";
|
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]
|
[Required]
|
||||||
public ITaskItem[] Sources { get; set; }
|
public ITaskItem[] Sources { get; set; }
|
||||||
|
|
@ -26,11 +45,28 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
|
|
||||||
protected override bool ValidateParameters()
|
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++)
|
for (var i = 0; i < Sources.Length; i++)
|
||||||
{
|
{
|
||||||
if (!EnsureRequiredMetadata(Sources[i], FullPath) ||
|
if (!EnsureRequiredMetadata(Sources[i], FullPath) ||
|
||||||
!EnsureRequiredMetadata(Sources[i], GeneratedOutput) ||
|
!EnsureRequiredMetadata(Sources[i], GeneratedOutput) ||
|
||||||
!EnsureRequiredMetadata(Sources[i], TargetPath))
|
!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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +101,21 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
builder.AppendLine("-t");
|
builder.AppendLine("-t");
|
||||||
builder.AppendLine(TagHelperManifest);
|
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();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,19 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
{
|
{
|
||||||
public class RazorTagHelper : DotNetToolTask
|
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]
|
[Required]
|
||||||
public string[] Assemblies { get; set; }
|
public string[] Assemblies { get; set; }
|
||||||
|
|
||||||
|
|
@ -51,6 +64,21 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
builder.AppendLine("-p");
|
builder.AppendLine("-p");
|
||||||
builder.AppendLine(ProjectRoot);
|
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();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,19 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.Extensions.CommandLineUtils;
|
using Microsoft.Extensions.CommandLineUtils;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Tools
|
namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
{
|
{
|
||||||
internal class Application : CommandLineApplication
|
internal class Application : CommandLineApplication
|
||||||
{
|
{
|
||||||
public Application(CancellationToken cancellationToken)
|
public Application(CancellationToken cancellationToken, ExtensionAssemblyLoader loader, ExtensionDependencyChecker checker, Func<string, MetadataReferenceProperties, PortableExecutableReference> assemblyReferenceProvider)
|
||||||
{
|
{
|
||||||
CancellationToken = cancellationToken;
|
CancellationToken = cancellationToken;
|
||||||
|
Checker = checker;
|
||||||
|
Loader = loader;
|
||||||
|
AssemblyReferenceProvider = assemblyReferenceProvider;
|
||||||
|
|
||||||
Name = "rzc";
|
Name = "rzc";
|
||||||
FullName = "Microsoft ASP.NET Core Razor CLI tool";
|
FullName = "Microsoft ASP.NET Core Razor CLI tool";
|
||||||
|
|
@ -31,6 +35,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
public CancellationToken CancellationToken { get; }
|
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)
|
public new int Execute(params string[] args)
|
||||||
{
|
{
|
||||||
try
|
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.
|
// 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.
|
// 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.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Tools
|
namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
{
|
{
|
||||||
|
|
@ -19,6 +21,25 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
private class DefaultCompilerHost : CompilerHost
|
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)
|
public override ServerResponse Execute(ServerRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!TryParseArguments(request, out var parsed))
|
if (!TryParseArguments(request, out var parsed))
|
||||||
|
|
@ -28,28 +49,23 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
var exitCode = 0;
|
var exitCode = 0;
|
||||||
var output = string.Empty;
|
var output = string.Empty;
|
||||||
var app = new Application(cancellationToken);
|
|
||||||
var commandArgs = parsed.args.ToArray();
|
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)
|
if (ServerLogger.IsLoggingEnabled)
|
||||||
{
|
{
|
||||||
using (var writer = new StringWriter())
|
output = writer.ToString();
|
||||||
{
|
ServerLogger.Log(output);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompletedServerResponse(exitCode, utf8output: false, output: string.Empty);
|
return new CompletedServerResponse(exitCode, utf8output: false, output: string.Empty);
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,25 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Tools
|
namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
{
|
{
|
||||||
internal class CompositeRazorProjectFileSystem : RazorProjectFileSystem
|
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)
|
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;
|
yield return result;
|
||||||
}
|
}
|
||||||
|
|
@ -30,9 +31,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public override RazorProjectItem GetItem(string path)
|
public override RazorProjectItem GetItem(string path)
|
||||||
{
|
{
|
||||||
RazorProjectItem razorProjectItem = null;
|
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)
|
if (razorProjectItem != null && razorProjectItem.Exists)
|
||||||
{
|
{
|
||||||
return razorProjectItem;
|
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.");
|
ServerLogger.Log("End writing response.");
|
||||||
|
|
||||||
reason = ConnectionResult.Reason.CompilationCompleted;
|
reason = ConnectionResult.Reason.CompilationCompleted;
|
||||||
|
|
||||||
|
_eventBus.CompilationCompleted();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
@ -26,6 +25,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
Assemblies = Argument("assemblies", "assemblies to search for tag helpers", multipleValues: true);
|
Assemblies = Argument("assemblies", "assemblies to search for tag helpers", multipleValues: true);
|
||||||
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
||||||
ProjectDirectory = Option("-p", "project root directory", 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; }
|
public CommandArgument Assemblies { get; }
|
||||||
|
|
@ -34,6 +37,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
public CommandOption ProjectDirectory { get; }
|
public CommandOption ProjectDirectory { get; }
|
||||||
|
|
||||||
|
public CommandOption Version { get; }
|
||||||
|
|
||||||
|
public CommandOption Configuration { get; }
|
||||||
|
|
||||||
|
public CommandOption ExtensionNames { get; }
|
||||||
|
|
||||||
|
public CommandOption ExtensionFilePaths { get; }
|
||||||
|
|
||||||
protected override bool ValidateArguments()
|
protected override bool ValidateArguments()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(TagHelperManifest.Value()))
|
if (string.IsNullOrEmpty(TagHelperManifest.Value()))
|
||||||
|
|
@ -53,12 +64,61 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
ProjectDirectory.Values.Add(Environment.CurrentDirectory);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<int> ExecuteCoreAsync()
|
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(
|
var result = ExecuteCore(
|
||||||
|
configuration: configuration,
|
||||||
projectDirectory: ProjectDirectory.Value(),
|
projectDirectory: ProjectDirectory.Value(),
|
||||||
outputFilePath: TagHelperManifest.Value(),
|
outputFilePath: TagHelperManifest.Value(),
|
||||||
assemblies: Assemblies.Values.ToArray());
|
assemblies: Assemblies.Values.ToArray());
|
||||||
|
|
@ -66,36 +126,31 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
return Task.FromResult(result);
|
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);
|
outputFilePath = Path.Combine(projectDirectory, outputFilePath);
|
||||||
|
|
||||||
var metadataReferences = new MetadataReference[assemblies.Length];
|
var metadataReferences = new MetadataReference[assemblies.Length];
|
||||||
for (var i = 0; i < assemblies.Length; i++)
|
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 DefaultMetadataReferenceFeature() { References = metadataReferences });
|
||||||
b.Features.Add(new CompilationTagHelperFeature());
|
b.Features.Add(new CompilationTagHelperFeature());
|
||||||
|
|
||||||
// TagHelperDescriptorProviders (actually do tag helper discovery)
|
|
||||||
b.Features.Add(new DefaultTagHelperDescriptorProvider());
|
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();
|
var tagHelpers = feature.GetDescriptors();
|
||||||
|
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
{
|
{
|
||||||
Serialize(stream, tagHelpers);
|
Serialize(stream, tagHelpers);
|
||||||
|
|
||||||
stream.Position = 0L;
|
stream.Position = 0;
|
||||||
|
|
||||||
var newHash = Hash(stream);
|
var newHash = Hash(stream);
|
||||||
var existingHash = Hash(outputFilePath);
|
var existingHash = Hash(outputFilePath);
|
||||||
|
|
@ -103,7 +158,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
if (!HashesEqual(newHash, existingHash))
|
if (!HashesEqual(newHash, existingHash))
|
||||||
{
|
{
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
using (var output = File.OpenWrite(outputFilePath))
|
using (var output = File.Open(outputFilePath, FileMode.Create))
|
||||||
{
|
{
|
||||||
stream.CopyTo(output);
|
stream.CopyTo(output);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public virtual void ConnectionCompleted(int count)
|
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>
|
/// <summary>
|
||||||
/// Called when a bad client connection was detected and the server will be shutting down as a
|
/// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.Extensions.CommandLineUtils;
|
using Microsoft.Extensions.CommandLineUtils;
|
||||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||||
|
|
@ -13,13 +13,6 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Tools
|
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
|
internal class GenerateCommand : CommandBase
|
||||||
{
|
{
|
||||||
public GenerateCommand(Application parent)
|
public GenerateCommand(Application parent)
|
||||||
|
|
@ -30,6 +23,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
RelativePaths = Option("-r", "Relative path", CommandOptionType.MultipleValue);
|
RelativePaths = Option("-r", "Relative path", CommandOptionType.MultipleValue);
|
||||||
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
||||||
TagHelperManifest = Option("-t", "tag helper manifest file", 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; }
|
public CommandOption Sources { get; }
|
||||||
|
|
@ -42,9 +39,29 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
public CommandOption TagHelperManifest { get; }
|
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()
|
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(
|
var result = ExecuteCore(
|
||||||
|
configuration: configuration,
|
||||||
projectDirectory: ProjectDirectory.Value(),
|
projectDirectory: ProjectDirectory.Value(),
|
||||||
tagHelperManifest: TagHelperManifest.Value(),
|
tagHelperManifest: TagHelperManifest.Value(),
|
||||||
sources: Sources.Values,
|
sources: Sources.Values,
|
||||||
|
|
@ -77,10 +94,48 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
ProjectDirectory.Values.Add(Environment.CurrentDirectory);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ExecuteCore(
|
private int ExecuteCore(
|
||||||
|
RazorConfiguration configuration,
|
||||||
string projectDirectory,
|
string projectDirectory,
|
||||||
string tagHelperManifest,
|
string tagHelperManifest,
|
||||||
List<string> sources,
|
List<string> sources,
|
||||||
|
|
@ -90,26 +145,19 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
tagHelperManifest = Path.Combine(projectDirectory, tagHelperManifest);
|
tagHelperManifest = Path.Combine(projectDirectory, tagHelperManifest);
|
||||||
|
|
||||||
var tagHelpers = GetTagHelpers(tagHelperManifest);
|
var tagHelpers = GetTagHelpers(tagHelperManifest);
|
||||||
|
var inputItems = GetInputItems(projectDirectory, sources, outputs, relativePaths);
|
||||||
var engine = RazorEngine.Create(b =>
|
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, });
|
b.Features.Add(new StaticTagHelperFeature() { TagHelpers = tagHelpers, });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var results = GenerateCode(engine, inputItems);
|
||||||
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 success = true;
|
var success = true;
|
||||||
|
|
||||||
|
|
@ -180,14 +228,15 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OutputItem[] GenerateCode(RazorTemplateEngine templateEngine, SourceItem[] inputs)
|
private OutputItem[] GenerateCode(RazorProjectEngine engine, SourceItem[] inputs)
|
||||||
{
|
{
|
||||||
var outputs = new OutputItem[inputs.Length];
|
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 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);
|
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>
|
||||||
|
|
||||||
<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>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- This makes it so that the runtimeconfig.json is included as part of the build output of the project that references this project. -->
|
<!-- 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;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Tools
|
namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
{
|
{
|
||||||
|
|
@ -15,7 +16,16 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
var cancel = new CancellationTokenSource();
|
var cancel = new CancellationTokenSource();
|
||||||
Console.CancelKeyPress += (sender, e) => { cancel.Cancel(); };
|
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);
|
return application.Execute(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = ConnectionHost.Create(Pipe.Value());
|
var host = ConnectionHost.Create(Pipe.Value());
|
||||||
|
|
||||||
var compilerHost = CompilerHost.Create();
|
var compilerHost = CompilerHost.Create();
|
||||||
ExecuteServerCore(host, compilerHost, Cancelled, eventBus: null, keepAlive: keepAlive);
|
ExecuteServerCore(host, compilerHost, Cancelled, eventBus: null, keepAlive: keepAlive);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a Request to the stream.
|
/// Write a Request to the stream.
|
||||||
/// </summary>
|
/// </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 memoryStream = new MemoryStream())
|
||||||
using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode))
|
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);
|
var process = Process.GetProcessById(response.ServerProcessId);
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// There is an inherent race here with the server process. If it has already shutdown
|
// 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);
|
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.
|
// Ignore an exception that occurred while the server was shutting down.
|
||||||
|
Error.Write(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
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">
|
<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>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,13 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
-->
|
-->
|
||||||
<Project ToolsVersion="14.0">
|
<Project ToolsVersion="14.0">
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(RazorSdkTargetsPath)' == ''">
|
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" Condition="'$(_RazorSdkImportsMicrosoftNetSdk)' == 'true'" />
|
||||||
<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>
|
<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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="$(RazorSdkTargetsPath)" />
|
<Import Project="$(RazorSdkCurrentVersionTargets)" />
|
||||||
|
|
||||||
</Project>
|
</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">
|
<Project ToolsVersion="14.0">
|
||||||
<Import Project="Sdk.Razor.CurrentVersion.props" />
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Redirect the SDK to use the targets from the current package -->
|
<RazorSdkCurrentVersionProps>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.props</RazorSdkCurrentVersionProps>
|
||||||
<RazorSdkTargetsPath>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.targets</RazorSdkTargetsPath>
|
<RazorSdkCurrentVersionTargets>$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.targets</RazorSdkCurrentVersionTargets>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</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.
|
Default properties for common Razor SDK behavior.
|
||||||
-->
|
-->
|
||||||
<PropertyGroup>
|
<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).
|
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>
|
<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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(_WebSdkHeartsRazorSdk)' == 'true' AND '$(EnableDefaultItems)' == 'true' And '$(EnableDefaultContentItems)' == 'true'">
|
<ItemGroup Condition="'$(EnableDefaultItems)' == 'true' And '$(EnableDefaultContentItems)' == 'true'">
|
||||||
<Content Include="**\*.cshtml" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(DefaultWebContentItemExcludes)" />
|
<Content Include="**\*.cshtml" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(DefaultWebContentItemExcludes)">
|
||||||
|
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||||
|
</Content>
|
||||||
|
|
||||||
<None Remove="**\*.cshtml" />
|
<None Remove="**\*.cshtml" />
|
||||||
</ItemGroup>
|
</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>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,13 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
ResolveRazorGenerateInputs;
|
ResolveRazorGenerateInputs;
|
||||||
AssignRazorGenerateTargetPaths;
|
AssignRazorGenerateTargetPaths;
|
||||||
ResolveAssemblyReferenceRazorGenerateInputs;
|
ResolveAssemblyReferenceRazorGenerateInputs;
|
||||||
_EnsureRazorCompilerReferenced;
|
_CheckForMissingRazorCompiler;
|
||||||
ResolveTagHelperRazorGenerateInputs
|
ResolveTagHelperRazorGenerateInputs
|
||||||
</PrepareForRazorGenerateDependsOn>
|
</PrepareForRazorGenerateDependsOn>
|
||||||
|
|
||||||
<RazorGenerateDependsOn>
|
<RazorGenerateDependsOn>
|
||||||
PrepareForRazorGenerate;
|
PrepareForRazorGenerate;
|
||||||
_EnsureRazorCompilerReferenced;
|
_CheckForMissingRazorCompiler;
|
||||||
RazorCoreGenerate
|
RazorCoreGenerate
|
||||||
</RazorGenerateDependsOn>
|
</RazorGenerateDependsOn>
|
||||||
|
|
||||||
|
|
@ -50,20 +50,36 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
</PrepareForRazorCompileDependsOn>
|
</PrepareForRazorCompileDependsOn>
|
||||||
|
|
||||||
<ResolveRazorCompileInputsDependsOn>
|
<ResolveRazorCompileInputsDependsOn>
|
||||||
ResolveRazorEmbeddedResources
|
ResolveRazorEmbeddedResources
|
||||||
</ResolveRazorCompileInputsDependsOn>
|
</ResolveRazorCompileInputsDependsOn>
|
||||||
|
|
||||||
<RazorCompileDependsOn>
|
<RazorCompileDependsOn>
|
||||||
PrepareForRazorCompile;
|
PrepareForRazorCompile;
|
||||||
RazorCoreCompile
|
RazorCoreCompile
|
||||||
</RazorCompileDependsOn>
|
</RazorCompileDependsOn>
|
||||||
|
|
||||||
|
<BuiltProjectOutputGroupDependsOn>
|
||||||
|
$(BuiltProjectOutputGroupDependsOn);
|
||||||
|
_RazorAddBuiltProjectOutputGroupOutput
|
||||||
|
</BuiltProjectOutputGroupDependsOn>
|
||||||
|
|
||||||
|
<DebugSymbolsProjectOutputGroupDependsOn>
|
||||||
|
$(DebugSymbolsProjectOutputGroupDependsOn);
|
||||||
|
_RazorAddDebugSymbolsProjectOutputGroupOutput
|
||||||
|
</DebugSymbolsProjectOutputGroupDependsOn>
|
||||||
|
|
||||||
|
<PrepareForBuildDependsOn>
|
||||||
|
_InitializePreserveCompilationContext;
|
||||||
|
$(PrepareForBuildDependsOn)
|
||||||
|
</PrepareForBuildDependsOn>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Default values for properties that affect Razor targets to the standard build lifecycle.
|
Default values for properties that affect Razor targets to the standard build lifecycle.
|
||||||
-->
|
-->
|
||||||
<PropertyGroup Condition="'$(RazorCompileOnBuild)'==''">
|
<PropertyGroup Condition="'$(RazorCompileOnBuild)'==''">
|
||||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
<RazorCompileOnBuild>true</RazorCompileOnBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(RazorCompileOnPublish)'==''">
|
<PropertyGroup Condition="'$(RazorCompileOnPublish)'==''">
|
||||||
|
|
@ -83,9 +99,12 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Output directory used for generated files -->
|
<!-- Output directory used for generated files -->
|
||||||
<RazorGenerateIntermediateOutputPath Condition="'$(RazorGenerateIntermediateOutputPath)'==''">$(IntermediateOutputPath)Razor\</RazorGenerateIntermediateOutputPath>
|
<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 -->
|
<!-- 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
|
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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Resolve the toolset to use -->
|
<!-- 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. -->
|
<!-- Default value for the property 'MvcRazorCompileOnPublish' is empty. If it has been explicitly enabled, continue using precompilation. -->
|
||||||
<ResolvedRazorCompileToolset Condition="'$(MvcRazorCompileOnPublish)' == 'true'">PrecompilationTool</ResolvedRazorCompileToolset>
|
<ResolvedRazorCompileToolset Condition="'$(MvcRazorCompileOnPublish)' == 'true'">PrecompilationTool</ResolvedRazorCompileToolset>
|
||||||
|
|
||||||
|
|
@ -154,6 +173,8 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<RazorIntermediateAssembly Condition="'$(RazorIntermediateAssembly)'==''" Include="$(IntermediateOutputPath)$(RazorTargetName).dll" />
|
<RazorIntermediateAssembly Condition="'$(RazorIntermediateAssembly)'==''" Include="$(IntermediateOutputPath)$(RazorTargetName).dll" />
|
||||||
<!-- Used in Compilation.targets -->
|
<!-- Used in Compilation.targets -->
|
||||||
<_RazorDebugSymbolsIntermediatePath Condition="'$(_RazorDebugSymbolsProduced)'=='true'" Include="$(IntermediateOutputPath)$(RazorTargetName).pdb" />
|
<_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>
|
</ItemGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
@ -182,6 +203,16 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<Target Name="RazorCompile" DependsOnTargets="$(RazorCompileDependsOn)">
|
<Target Name="RazorCompile" DependsOnTargets="$(RazorCompileDependsOn)">
|
||||||
</Target>
|
</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
|
Computes the applicable @(ResolvedRazorConfiguration) and @(ResolvedRazorExtension) items that match the project's
|
||||||
configuration.
|
configuration.
|
||||||
|
|
@ -316,16 +347,22 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
-->
|
-->
|
||||||
<Target
|
<Target
|
||||||
Name="_RazorAddBuiltProjectOutputGroupOutput"
|
Name="_RazorAddBuiltProjectOutputGroupOutput"
|
||||||
DependsOnTargets="ResolveRazorGenerateInputs"
|
DependsOnTargets="_ResolveRazorTargetPath;ResolveRazorGenerateInputs"
|
||||||
BeforeTargets="BuiltProjectOutputGroup"
|
|
||||||
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<RazorOutputPath Condition="'$(RazorOutputPath)'==''">$([MSBuild]::EnsureTrailingSlash('$(OutDir)'))</RazorOutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'@(RazorGenerate)'!= ''">
|
<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>
|
</ItemGroup>
|
||||||
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|
@ -393,18 +430,14 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
-->
|
-->
|
||||||
<Target
|
<Target
|
||||||
Name="_RazorCopyFilesToOutputDirectory"
|
Name="_RazorCopyFilesToOutputDirectory"
|
||||||
DependsOnTargets="RazorCompile"
|
DependsOnTargets="_ResolveRazorTargetPath;RazorCompile"
|
||||||
AfterTargets="CopyFilesToOutputDirectory"
|
AfterTargets="CopyFilesToOutputDirectory"
|
||||||
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
Condition="'$(ResolvedRazorCompileToolset)'=='RazorSdk' and '$(RazorCompileOnBuild)'=='true'">
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<RazorOutputPath Condition="'$(RazorOutputPath)'==''">$([MSBuild]::EnsureTrailingSlash('$(OutDir)'))</RazorOutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<!-- Copy the Razor dll -->
|
<!-- Copy the Razor dll -->
|
||||||
<Copy
|
<Copy
|
||||||
SourceFiles="@(RazorIntermediateAssembly)"
|
SourceFiles="@(RazorIntermediateAssembly)"
|
||||||
DestinationFolder="$(RazorOutputPath)"
|
DestinationFiles="$(RazorTargetPath)"
|
||||||
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
|
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
|
||||||
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
|
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
|
||||||
Retries="$(CopyRetryCount)"
|
Retries="$(CopyRetryCount)"
|
||||||
|
|
@ -478,11 +511,29 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="_EnsureRazorCompilerReferenced" Condition="'$(IsRazorCompilerReferenced)' != 'true'">
|
<Target Name="_CheckForMissingRazorCompiler" 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." />
|
<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>
|
||||||
|
|
||||||
|
<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.
|
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
|
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
|
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">
|
<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>
|
</Project>
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||||
|
|
@ -18,10 +19,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
private MainThreadState _main;
|
private MainThreadState _main;
|
||||||
private BackgroundThread _bg;
|
private BackgroundThread _bg;
|
||||||
|
|
||||||
public BackgroundParser(RazorTemplateEngine templateEngine, string filePath)
|
public BackgroundParser(RazorProjectEngine projectEngine, string filePath, string projectDirectory)
|
||||||
{
|
{
|
||||||
_main = new MainThreadState(filePath);
|
_main = new MainThreadState(filePath);
|
||||||
_bg = new BackgroundThread(_main, templateEngine, filePath);
|
_bg = new BackgroundThread(_main, projectEngine, filePath, projectDirectory);
|
||||||
|
|
||||||
_main.ResultsReady += (sender, args) => OnResultsReady(args);
|
_main.ResultsReady += (sender, args) => OnResultsReady(args);
|
||||||
}
|
}
|
||||||
|
|
@ -233,22 +234,25 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
private class BackgroundThread : ThreadStateBase
|
private class BackgroundThread : ThreadStateBase
|
||||||
{
|
{
|
||||||
|
private readonly string _filePath;
|
||||||
|
private readonly string _relativeFilePath;
|
||||||
|
private readonly string _projectDirectory;
|
||||||
private MainThreadState _main;
|
private MainThreadState _main;
|
||||||
private Thread _backgroundThread;
|
private Thread _backgroundThread;
|
||||||
private CancellationToken _shutdownToken;
|
private CancellationToken _shutdownToken;
|
||||||
private RazorTemplateEngine _templateEngine;
|
private RazorProjectEngine _projectEngine;
|
||||||
private string _filePath;
|
|
||||||
private RazorSyntaxTree _currentSyntaxTree;
|
private RazorSyntaxTree _currentSyntaxTree;
|
||||||
private IList<Edit> _previouslyDiscarded = new List<Edit>();
|
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!
|
// Run on MAIN thread!
|
||||||
_main = main;
|
_main = main;
|
||||||
_shutdownToken = _main.CancelToken;
|
_shutdownToken = _main.CancelToken;
|
||||||
_templateEngine = templateEngine;
|
_projectEngine = projectEngine;
|
||||||
_filePath = fileName;
|
_filePath = filePath;
|
||||||
|
_relativeFilePath = GetNormalizedRelativeFilePath(filePath, projectDirectory);
|
||||||
|
_projectDirectory = projectDirectory;
|
||||||
_backgroundThread = new Thread(WorkerLoop);
|
_backgroundThread = new Thread(WorkerLoop);
|
||||||
SetThreadId(_backgroundThread.ManagedThreadId);
|
SetThreadId(_backgroundThread.ManagedThreadId);
|
||||||
}
|
}
|
||||||
|
|
@ -262,8 +266,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
// **** BACKGROUND THREAD ****
|
// **** BACKGROUND THREAD ****
|
||||||
private void WorkerLoop()
|
private void WorkerLoop()
|
||||||
{
|
{
|
||||||
var fileNameOnly = Path.GetFileName(_filePath);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EnsureOnThread();
|
EnsureOnThread();
|
||||||
|
|
@ -347,14 +349,31 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
EnsureOnThread();
|
EnsureOnThread();
|
||||||
|
|
||||||
var sourceDocument = new TextSnapshotSourceDocument(snapshot, _filePath);
|
var projectItem = new TextSnapshotProjectItem(snapshot, _projectDirectory, _relativeFilePath, _filePath);
|
||||||
var imports = _templateEngine.GetImports(_filePath);
|
var codeDocument = _projectEngine.ProcessDesignTime(projectItem);
|
||||||
|
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, imports);
|
|
||||||
|
|
||||||
_templateEngine.GenerateCode(codeDocument);
|
|
||||||
return 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
|
private class WorkParcel
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
||||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
private readonly FileChangeTrackerFactory _fileChangeTrackerFactory;
|
private readonly FileChangeTrackerFactory _fileChangeTrackerFactory;
|
||||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||||
private readonly ErrorReporter _errorReporter;
|
private readonly ErrorReporter _errorReporter;
|
||||||
private readonly RazorTemplateEngineFactoryService _templateEngineFactoryService;
|
private readonly RazorProjectEngineFactoryService _projectEngineFactoryService;
|
||||||
private readonly Dictionary<string, ImportTracker> _importTrackerCache;
|
private readonly Dictionary<string, ImportTracker> _importTrackerCache;
|
||||||
|
|
||||||
public override event EventHandler<ImportChangedEventArgs> Changed;
|
public override event EventHandler<ImportChangedEventArgs> Changed;
|
||||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
ForegroundDispatcher foregroundDispatcher,
|
ForegroundDispatcher foregroundDispatcher,
|
||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
FileChangeTrackerFactory fileChangeTrackerFactory,
|
FileChangeTrackerFactory fileChangeTrackerFactory,
|
||||||
RazorTemplateEngineFactoryService templateEngineFactoryService)
|
RazorProjectEngineFactoryService projectEngineFactoryService)
|
||||||
{
|
{
|
||||||
if (foregroundDispatcher == null)
|
if (foregroundDispatcher == null)
|
||||||
{
|
{
|
||||||
|
|
@ -41,15 +42,15 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
throw new ArgumentNullException(nameof(fileChangeTrackerFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (templateEngineFactoryService == null)
|
if (projectEngineFactoryService == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(templateEngineFactoryService));
|
throw new ArgumentNullException(nameof(projectEngineFactoryService));
|
||||||
}
|
}
|
||||||
|
|
||||||
_foregroundDispatcher = foregroundDispatcher;
|
_foregroundDispatcher = foregroundDispatcher;
|
||||||
_errorReporter = errorReporter;
|
_errorReporter = errorReporter;
|
||||||
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
_fileChangeTrackerFactory = fileChangeTrackerFactory;
|
||||||
_templateEngineFactoryService = templateEngineFactoryService;
|
_projectEngineFactoryService = projectEngineFactoryService;
|
||||||
_importTrackerCache = new Dictionary<string, ImportTracker>(StringComparer.OrdinalIgnoreCase);
|
_importTrackerCache = new Dictionary<string, ImportTracker>(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,10 +116,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
private IEnumerable<RazorProjectItem> GetImportItems(VisualStudioDocumentTracker tracker)
|
private IEnumerable<RazorProjectItem> GetImportItems(VisualStudioDocumentTracker tracker)
|
||||||
{
|
{
|
||||||
var projectDirectory = Path.GetDirectoryName(tracker.ProjectPath);
|
var projectDirectory = Path.GetDirectoryName(tracker.ProjectPath);
|
||||||
var templateEngine = _templateEngineFactoryService.Create(projectDirectory, _ => { });
|
var projectEngine = _projectEngineFactoryService.Create(projectDirectory, _ => { });
|
||||||
var imports = templateEngine.GetImportItems(tracker.FilePath);
|
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)
|
private void OnChanged(ImportTracker importTracker, FileChangeKind changeKind)
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
|
|
||||||
var errorReporter = languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>();
|
var errorReporter = languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>();
|
||||||
var fileChangeTrackerFactory = languageServices.GetRequiredService<FileChangeTrackerFactory>();
|
var fileChangeTrackerFactory = languageServices.GetRequiredService<FileChangeTrackerFactory>();
|
||||||
var templateEngineFactoryService = languageServices.GetRequiredService<RazorTemplateEngineFactoryService>();
|
var projectEngineFactoryService = languageServices.GetRequiredService<RazorProjectEngineFactoryService>();
|
||||||
|
|
||||||
return new DefaultImportDocumentManager(
|
return new DefaultImportDocumentManager(
|
||||||
_foregroundDispatcher,
|
_foregroundDispatcher,
|
||||||
errorReporter,
|
errorReporter,
|
||||||
fileChangeTrackerFactory,
|
fileChangeTrackerFactory,
|
||||||
templateEngineFactoryService);
|
projectEngineFactoryService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue