Razor 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. commit 5b28c06d6453696e98fb7bf32abe3aef09a5c26b
  2. Author: Ryan Nowak <[email protected]>
  3. Date: Fri Feb 2 17:41:14 2018 -0800
  4. Add prelimianry support for extensions to Razor (#2012)
  5. * Add prelimianry support for extensions to Razor
  6. This PR adds MSBuild insfrastructure to the SDK that can understand
  7. concepts we need to expose to the project, code generator and runtime
  8. like:
  9. - Language version
  10. - Configuration
  11. - Extensions (plugins)
  12. As an example of how this works, I've done the wireup for MVC. This will
  13. now generate assembly attributes in your application that can act as a
  14. source-of-truth for what should be included in runtime compilation, and
  15. it's all based on the project-file. This means that it can be delivered
  16. and configured by packages.
  17. The next step here is to implement a loader for RazorProjectEngine based
  18. on these primitives, and then use it in our CLI tools and MVC.
  19. The next step after that is to expose it in VS and VS4Mac through the
  20. project system.
  21. diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj
  22. index 37eafe0684a..401d07c0308 100644
  23. --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj
  24. +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj
  25. @@ -12,6 +12,11 @@
  26. </Compile>
  27. </ItemGroup>
  28. + <ItemGroup>
  29. + <Content Include="build\**\*.props" PackagePath="build\" />
  30. + <Content Include="build\**\*.targets" PackagePath="build\" />
  31. + </ItemGroup>
  32. +
  33. <ItemGroup>
  34. <ProjectReference Include="../Microsoft.AspNetCore.Razor.Language/Microsoft.AspNetCore.Razor.Language.csproj" />
  35. <ProjectReference Include="../Microsoft.CodeAnalysis.Razor/Microsoft.CodeAnalysis.Razor.csproj" />
  36. diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.props b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.props
  37. new file mode 100644
  38. index 00000000000..8d2ac3b630a
  39. --- /dev/null
  40. +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.props
  41. @@ -0,0 +1,32 @@
  42. +<Project>
  43. + <!--
  44. + MSBuild support for Razor code generation that targets ASP.NET Core MVC 2.X
  45. +
  46. + The properties and items here are designed to be read by CPS so they should be just simple evaluation-time values
  47. + and should not require targets to initialize.
  48. + -->
  49. + <PropertyGroup>
  50. + <!--
  51. + Set the primary configuration supported by this pacakge as the default configuration for Razor.
  52. + -->
  53. + <RazorDefaultConfiguration Condition="'$(RazorDefaultConfiguration)'==''">MVC-2.1</RazorDefaultConfiguration>
  54. + </PropertyGroup>
  55. +
  56. + <ItemGroup>
  57. + <!--
  58. + While technically the assembly in this package can provide support for the MVC-2.0 configuration, don't declare
  59. + it here. The IDE is hardcoded to inject 2.0 support when needed. The settings flowing through MSBuild should reflect
  60. + the project's runtime.
  61. + -->
  62. + <RazorConfiguration Include="MVC-2.1">
  63. + <Extensions>MVC-2.1;$(CustomRazorExtension)</Extensions>
  64. + </RazorConfiguration>
  65. + </ItemGroup>
  66. +
  67. + <ItemGroup>
  68. + <RazorExtension Include="MVC-2.1">
  69. + <AssemblyName>Microsoft.AspNetCore.Mvc.Razor.Extensions</AssemblyName>
  70. + <AssemblyFilePath>$(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll</AssemblyFilePath>
  71. + </RazorExtension>
  72. + </ItemGroup>
  73. +</Project>
  74. \ No newline at end of file
  75. diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.targets b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.targets
  76. new file mode 100644
  77. index 00000000000..61a0e7a8dc5
  78. --- /dev/null
  79. +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.targets
  80. @@ -0,0 +1,9 @@
  81. +<Project>
  82. + <PropertyGroup>
  83. +
  84. + <!--
  85. + MVC will generally want to add support for runtime compilation, but only for applications.
  86. + -->
  87. + <GenerateRazorAssemblyInfo Condition="'$(GenerateRazorAssemblyInfo)'=='' and '$(OutputType)'=='Exe'">true</GenerateRazorAssemblyInfo>
  88. + </PropertyGroup>
  89. +</Project>
  90. \ No newline at end of file
  91. diff --git a/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.props b/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.props
  92. index 6549731539e..c25a2ab1748 100644
  93. --- a/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.props
  94. +++ b/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.props
  95. @@ -1,15 +1,29 @@
  96. <Project ToolsVersion="14.0" TreatAsLocalProperty="_RazorTaskFolder;_RazorTaskAssembly">
  97. <PropertyGroup>
  98. <!--
  99. - Used by the Web SDK if the Razor SDK can be used for compilation. This needs to live in a nuget package (not in the SDK)
  100. - so that it only shows up in supported versions.
  101. + Used by the Web SDK if the Razor SDK can be used for compilation. This needs to live in a nuget package (not in the SDK)
  102. + so that it only shows up in supported versions.
  103. -->
  104. <IsRazorCompilerReferenced>true</IsRazorCompilerReferenced>
  105. <!--
  106. - Location of the CodeGeneration targets. The SDK uses this to import the file ensuring deterministic import order.
  107. + Location of the CodeGeneration targets. The SDK uses this to import the file ensuring deterministic import order.
  108. -->
  109. <RazorCodeGenerationTargetsPath>$(MSBuildThisFileDirectory)Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets</RazorCodeGenerationTargetsPath>
  110. +
  111. + <!--
  112. + Configures the language version of Razor. Supported and default values differ depending on the version of
  113. + the packages in use.
  114. +
  115. + Supported:
  116. + 2.0
  117. + 2.1
  118. + Latest = 2.1
  119. +
  120. + Default:
  121. + 2.1
  122. + -->
  123. + <RazorLangVersion Condition="'$(RazorLangVersion)'==''">2.1</RazorLangVersion>
  124. </PropertyGroup>
  125. <PropertyGroup>
  126. diff --git a/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs
  127. index 6193fcb066e..13c56c698e4 100644
  128. --- a/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs
  129. +++ b/src/Microsoft.AspNetCore.Razor.Language/Properties/Resources.Designer.cs
  130. @@ -1856,6 +1856,20 @@ namespace Microsoft.AspNetCore.Razor.Language
  131. internal static string FormatRazorProjectEngineMissingFeatureDependency(object p0, object p1)
  132. => string.Format(CultureInfo.CurrentCulture, GetString("RazorProjectEngineMissingFeatureDependency"), p0, p1);
  133. + /// <summary>
  134. + /// The Razor language version '{0}' is unrecognized or not supported by this version of Razor.
  135. + /// </summary>
  136. + internal static string RazorLanguageVersion_InvalidVersion
  137. + {
  138. + get => GetString("RazorLanguageVersion_InvalidVersion");
  139. + }
  140. +
  141. + /// <summary>
  142. + /// The Razor language version '{0}' is unrecognized or not supported by this version of Razor.
  143. + /// </summary>
  144. + internal static string FormatRazorLanguageVersion_InvalidVersion(object p0)
  145. + => string.Format(CultureInfo.CurrentCulture, GetString("RazorLanguageVersion_InvalidVersion"), p0);
  146. +
  147. private static string GetString(string name, params string[] formatterNames)
  148. {
  149. var value = _resourceManager.GetString(name);
  150. diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs
  151. index 103c3fbec51..0f00751497e 100644
  152. --- a/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs
  153. +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorConfiguration.cs
  154. @@ -2,24 +2,58 @@
  155. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  156. using System;
  157. +using System.Collections.Generic;
  158. +using System.Linq;
  159. namespace Microsoft.AspNetCore.Razor.Language
  160. {
  161. public sealed class RazorConfiguration
  162. {
  163. - public static readonly RazorConfiguration Default = new RazorConfiguration(RazorLanguageVersion.Latest, designTime: false);
  164. + public static readonly RazorConfiguration Default = new RazorConfiguration(
  165. + RazorLanguageVersion.Latest,
  166. + "unnamed",
  167. + Array.Empty<RazorExtension>(),
  168. + designTime: false);
  169. - public RazorConfiguration(RazorLanguageVersion languageVersion, bool designTime)
  170. + // This is used only in some back-compat scenarios. We don't expose it because there's no
  171. + // use case for anyone else to use it.
  172. + internal static readonly RazorConfiguration DefaultDesignTime = new RazorConfiguration(
  173. + RazorLanguageVersion.Latest,
  174. + "unnamed",
  175. + Array.Empty<RazorExtension>(),
  176. + designTime: true);
  177. +
  178. + public RazorConfiguration(
  179. + RazorLanguageVersion languageVersion,
  180. + string configurationName,
  181. + IEnumerable<RazorExtension> extensions,
  182. + bool designTime)
  183. {
  184. if (languageVersion == null)
  185. {
  186. throw new ArgumentNullException(nameof(languageVersion));
  187. }
  188. + if (configurationName == null)
  189. + {
  190. + throw new ArgumentNullException(nameof(configurationName));
  191. + }
  192. +
  193. + if (extensions == null)
  194. + {
  195. + throw new ArgumentNullException(nameof(extensions));
  196. + }
  197. +
  198. LanguageVersion = languageVersion;
  199. + ConfigurationName = configurationName;
  200. + Extensions = extensions.ToArray();
  201. DesignTime = designTime;
  202. }
  203. + public string ConfigurationName { get; }
  204. +
  205. + public IReadOnlyList<RazorExtension> Extensions { get; }
  206. +
  207. public RazorLanguageVersion LanguageVersion { get; }
  208. public bool DesignTime { get; }
  209. diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorEngine.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorEngine.cs
  210. index 184afd70ace..a48c31893a0 100644
  211. --- a/src/Microsoft.AspNetCore.Razor.Language/RazorEngine.cs
  212. +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorEngine.cs
  213. @@ -24,8 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Language
  214. return CreateDesignTime(configure: null);
  215. }
  216. - public static RazorEngine CreateDesignTime(Action<IRazorEngineBuilder> configure)
  217. - => CreateCore(new RazorConfiguration(RazorLanguageVersion.Latest, designTime: true), configure);
  218. + public static RazorEngine CreateDesignTime(Action<IRazorEngineBuilder> configure) => CreateCore(RazorConfiguration.DefaultDesignTime, configure);
  219. // Internal since RazorEngine APIs are going to be obsolete.
  220. internal static RazorEngine CreateCore(RazorConfiguration configuration, Action<IRazorEngineBuilder> configure)
  221. diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorExtension.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorExtension.cs
  222. new file mode 100644
  223. index 00000000000..feb94b0edef
  224. --- /dev/null
  225. +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorExtension.cs
  226. @@ -0,0 +1,10 @@
  227. +// Copyright(c) .NET Foundation.All rights reserved.
  228. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  229. +
  230. +namespace Microsoft.AspNetCore.Razor.Language
  231. +{
  232. + public abstract class RazorExtension
  233. + {
  234. + public abstract string ExtensionName { get; }
  235. + }
  236. +}
  237. diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorLanguageVersion.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorLanguageVersion.cs
  238. index 721c84ff00e..c2002908782 100644
  239. --- a/src/Microsoft.AspNetCore.Razor.Language/RazorLanguageVersion.cs
  240. +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorLanguageVersion.cs
  241. @@ -2,10 +2,12 @@
  242. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  243. using System;
  244. +using System.Diagnostics;
  245. namespace Microsoft.AspNetCore.Razor.Language
  246. {
  247. - public sealed class RazorLanguageVersion : IEquatable<RazorLanguageVersion>
  248. + [DebuggerDisplay("{" + nameof(DebuggerToString) + "(),nq}")]
  249. + public sealed class RazorLanguageVersion : IEquatable<RazorLanguageVersion>, IComparable<RazorLanguageVersion>
  250. {
  251. public static readonly RazorLanguageVersion Version_1_0 = new RazorLanguageVersion(1, 0);
  252. @@ -17,6 +19,60 @@ namespace Microsoft.AspNetCore.Razor.Language
  253. public static readonly RazorLanguageVersion Latest = Version_2_1;
  254. + public static bool TryParse(string languageVersion, out RazorLanguageVersion version)
  255. + {
  256. + if (languageVersion == null)
  257. + {
  258. + throw new ArgumentNullException(nameof(languageVersion));
  259. + }
  260. +
  261. + if (string.Equals(languageVersion, "latest", StringComparison.OrdinalIgnoreCase))
  262. + {
  263. + version = Version_2_1;
  264. + return true;
  265. + }
  266. + else if (languageVersion == "2.1")
  267. + {
  268. + version = Version_2_1;
  269. + return true;
  270. + }
  271. + else if (languageVersion == "2.0")
  272. + {
  273. + version = Version_2_0;
  274. + return true;
  275. + }
  276. + else if (languageVersion == "1.1")
  277. + {
  278. + version = Version_1_1;
  279. + return true;
  280. + }
  281. + else if (languageVersion == "1.0")
  282. + {
  283. + version = Version_1_0;
  284. + return true;
  285. + }
  286. +
  287. + version = null;
  288. + return false;
  289. + }
  290. +
  291. + public static RazorLanguageVersion Parse(string languageVersion)
  292. + {
  293. + if (languageVersion == null)
  294. + {
  295. + throw new ArgumentNullException(nameof(languageVersion));
  296. + }
  297. +
  298. + if (TryParse(languageVersion, out var parsed))
  299. + {
  300. + return parsed;
  301. + }
  302. +
  303. + throw new ArgumentException(
  304. + Resources.FormatRazorLanguageVersion_InvalidVersion(languageVersion),
  305. + nameof(languageVersion));
  306. + }
  307. +
  308. // Don't want anyone else constructing language versions.
  309. private RazorLanguageVersion(int major, int minor)
  310. {
  311. @@ -28,6 +84,22 @@ namespace Microsoft.AspNetCore.Razor.Language
  312. public int Minor { get; }
  313. + public int CompareTo(RazorLanguageVersion other)
  314. + {
  315. + if (other == null)
  316. + {
  317. + throw new ArgumentNullException(nameof(other));
  318. + }
  319. +
  320. + var result = Major.CompareTo(other.Major);
  321. + if (result != 0)
  322. + {
  323. + return result;
  324. + }
  325. +
  326. + return Minor.CompareTo(other.Minor);
  327. + }
  328. +
  329. public bool Equals(RazorLanguageVersion other)
  330. {
  331. if (other == null)
  332. @@ -44,7 +116,9 @@ namespace Microsoft.AspNetCore.Razor.Language
  333. // We don't need to do anything special for our hash code since reference equality is what we're going for.
  334. return base.GetHashCode();
  335. }
  336. +
  337. + public override string ToString() => $"{Major}.{Minor}";
  338. - public override string ToString() => $"Razor '{Major}.{Minor}'";
  339. + private string DebuggerToString() => $"Razor '{Major}.{Minor}'";
  340. }
  341. }
  342. diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs
  343. index 19b1d54e80b..0629eb9af86 100644
  344. --- a/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs
  345. +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs
  346. @@ -9,8 +9,9 @@ namespace Microsoft.AspNetCore.Razor.Language
  347. {
  348. var allowMinimizedBooleanTagHelperAttributes = false;
  349. - if (version == RazorLanguageVersion.Version_2_1)
  350. + if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0)
  351. {
  352. + // Added in 2.1
  353. allowMinimizedBooleanTagHelperAttributes = true;
  354. }
  355. diff --git a/src/Microsoft.AspNetCore.Razor.Language/Resources.resx b/src/Microsoft.AspNetCore.Razor.Language/Resources.resx
  356. index d2810420469..6413f1038b9 100644
  357. --- a/src/Microsoft.AspNetCore.Razor.Language/Resources.resx
  358. +++ b/src/Microsoft.AspNetCore.Razor.Language/Resources.resx
  359. @@ -533,4 +533,7 @@ Instead, wrap the contents of the block in "{{}}":
  360. <data name="RazorProjectEngineMissingFeatureDependency" xml:space="preserve">
  361. <value>The '{0}' is missing feature '{1}'.</value>
  362. </data>
  363. + <data name="RazorLanguageVersion_InvalidVersion" xml:space="preserve">
  364. + <value>The Razor language version '{0}' is unrecognized or not supported by this version of Razor.</value>
  365. + </data>
  366. </root>
  367. \ No newline at end of file
  368. diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorConfigurationNameAttribute.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorConfigurationNameAttribute.cs
  369. new file mode 100644
  370. index 00000000000..034e64c3093
  371. --- /dev/null
  372. +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorConfigurationNameAttribute.cs
  373. @@ -0,0 +1,38 @@
  374. +// Copyright (c) .NET Foundation. All rights reserved.
  375. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  376. +
  377. +using System;
  378. +
  379. +namespace Microsoft.AspNetCore.Razor.Hosting
  380. +{
  381. + /// <summary>
  382. + /// Specifies the name of a Razor configuration as defined by the Razor SDK.
  383. + /// </summary>
  384. + /// <remarks>
  385. + /// This attribute is applied to an application's entry point assembly by the Razor SDK during the build,
  386. + /// so that the Razor configuration can be loaded at runtime based on the settings provided by the project
  387. + /// file.
  388. + /// </remarks>
  389. + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
  390. + public sealed class RazorConfigurationNameAttribute : Attribute
  391. + {
  392. + /// <summary>
  393. + /// Creates a new instance of <see cref="RazorConfigurationNameAttribute"/>.
  394. + /// </summary>
  395. + /// <param name="configurationName">The name of the Razor configuration.</param>
  396. + public RazorConfigurationNameAttribute(string configurationName)
  397. + {
  398. + if (configurationName == null)
  399. + {
  400. + throw new ArgumentNullException(nameof(configurationName));
  401. + }
  402. +
  403. + ConfigurationName = configurationName;
  404. + }
  405. +
  406. + /// <summary>
  407. + /// Gets the name of the Razor configuration.
  408. + /// </summary>
  409. + public string ConfigurationName { get; }
  410. + }
  411. +}
  412. \ No newline at end of file
  413. diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorExtensionAssemblyNameAttribute.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorExtensionAssemblyNameAttribute.cs
  414. new file mode 100644
  415. index 00000000000..92a9d1c6ec7
  416. --- /dev/null
  417. +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorExtensionAssemblyNameAttribute.cs
  418. @@ -0,0 +1,50 @@
  419. +// Copyright (c) .NET Foundation. All rights reserved.
  420. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  421. +
  422. +using System;
  423. +
  424. +namespace Microsoft.AspNetCore.Razor.Hosting
  425. +{
  426. + /// <summary>
  427. + /// Specifies the name of a Razor extension as defined by the Razor SDK.
  428. + /// </summary>
  429. + /// <remarks>
  430. + /// This attribute is applied to an application's entry point assembly by the Razor SDK during the build,
  431. + /// so that the Razor configuration can be loaded at runtime based on the settings provided by the project
  432. + /// file.
  433. + /// </remarks>
  434. + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
  435. + public sealed class RazorExtensionAssemblyNameAttribute : Attribute
  436. + {
  437. + /// <summary>
  438. + /// Creates a new instance of <see cref="RazorExtensionAssemblyNameAttribute"/>.
  439. + /// </summary>
  440. + /// <param name="extensionName">The name of the extension.</param>
  441. + /// <param name="assemblyName">The assembly name of the extension.</param>
  442. + public RazorExtensionAssemblyNameAttribute(string extensionName, string assemblyName)
  443. + {
  444. + if (extensionName == null)
  445. + {
  446. + throw new ArgumentNullException(nameof(extensionName));
  447. + }
  448. +
  449. + if (assemblyName == null)
  450. + {
  451. + throw new ArgumentNullException(nameof(assemblyName));
  452. + }
  453. +
  454. + ExtensionName = extensionName;
  455. + AssemblyName = assemblyName;
  456. + }
  457. +
  458. + /// <summary>
  459. + /// Gets the assembly name of the extension.
  460. + /// </summary>
  461. + public string AssemblyName { get; }
  462. +
  463. + /// <summary>
  464. + /// Gets the name of the extension.
  465. + /// </summary>
  466. + public string ExtensionName { get; }
  467. + }
  468. +}
  469. \ No newline at end of file
  470. diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorLanguageVersionAttribute.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorLanguageVersionAttribute.cs
  471. new file mode 100644
  472. index 00000000000..8f261143d04
  473. --- /dev/null
  474. +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Hosting/RazorLanguageVersionAttribute.cs
  475. @@ -0,0 +1,38 @@
  476. +// Copyright (c) .NET Foundation. All rights reserved.
  477. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  478. +
  479. +using System;
  480. +
  481. +namespace Microsoft.AspNetCore.Razor.Hosting
  482. +{
  483. + /// <summary>
  484. + /// Specifies the name of a Razor configuration as defined by the Razor SDK.
  485. + /// </summary>
  486. + /// <remarks>
  487. + /// This attribute is part of a set of metadata attributes that can be applied to an assembly at build
  488. + /// time by the Razor SDK. These attributes allow the Razor configuration to be loaded at runtime based
  489. + /// on the settings originally provided by the project file.
  490. + /// </remarks>
  491. + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
  492. + public sealed class RazorLanguageVersionAttribute : Attribute
  493. + {
  494. + /// <summary>
  495. + /// Creates a new instance of <see cref="RazorLanguageVersionAttribute"/>.
  496. + /// </summary>
  497. + /// <param name="languageVersion">The language version of Razor</param>
  498. + public RazorLanguageVersionAttribute(string languageVersion)
  499. + {
  500. + if (languageVersion == null)
  501. + {
  502. + throw new ArgumentNullException(nameof(languageVersion));
  503. + }
  504. +
  505. + LanguageVersion = languageVersion;
  506. + }
  507. +
  508. + /// <summary>
  509. + /// Gets the Razor language version.
  510. + /// </summary>
  511. + public string LanguageVersion { get; }
  512. + }
  513. +}
  514. \ No newline at end of file
  515. diff --git a/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets b/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
  516. index a081f46aaf8..99109a78d4f 100644
  517. --- a/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
  518. +++ b/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
  519. @@ -161,6 +161,46 @@ Copyright (c) .NET Foundation. All rights reserved.
  520. <Target Name="RazorCompile" DependsOnTargets="$(RazorCompileDependsOn)">
  521. </Target>
  522. + <!--
  523. + Generates assembly attributes in support for Razor runtime code generation. This is a set of standard
  524. + metadata attributes (defined in Microsoft.AspNetCore.Razor.Runtime) that capture the build-time
  525. + Razor configuration of an application to be used at runtime.
  526. +
  527. + This allows the project file to act as the source of truth for the applicable Razor configuration regardless
  528. + of how Razor is used.
  529. +
  530. + The SDK expects configurations that use runtime compilation to set $(GenerateRazorAssemblyInfo) to true,
  531. + it will be unset by default.
  532. + -->
  533. + <Target
  534. + Name="RazorGetAssemblyAttributes"
  535. + AfterTargets="GetAssemblyAttributes"
  536. + Condition="'$(GenerateRazorAssemblyInfo)'=='true' and '$(RazorDefaultConfiguration)'!=''">
  537. +
  538. + <ItemGroup>
  539. + <_ResolvedRazorConfiguration Include="@(RazorConfiguration)" Condition="'%(RazorConfiguration.Identity)'=='$(RazorDefaultConfiguration)'" />
  540. + <_ResolvedRazorExtensionName Include="%(_ResolvedRazorConfiguration.Extensions)"/>
  541. + </ItemGroup>
  542. +
  543. + <FindInList List="@(RazorExtension)" ItemSpecToFind="%(_ResolvedRazorExtensionName.Identity)">
  544. + <Output TaskParameter="ItemFound" ItemName="_ResolvedRazorExtension" />
  545. + </FindInList>
  546. +
  547. + <ItemGroup>
  548. + <AssemblyAttribute Include="Microsoft.AspNetCore.Razor.Hosting.RazorLanguageVersionAttribute">
  549. + <_Parameter1>$(RazorLangVersion)</_Parameter1>
  550. + </AssemblyAttribute>
  551. + <AssemblyAttribute Include="Microsoft.AspNetCore.Razor.Hosting.RazorConfigurationNameAttribute">
  552. + <_Parameter1>$(RazorDefaultConfiguration)</_Parameter1>
  553. + </AssemblyAttribute>
  554. + <AssemblyAttribute Include="Microsoft.AspNetCore.Razor.Hosting.RazorExtensionAssemblyNameAttribute" Condition="'%(_ResolvedRazorExtension.AssemblyName)'!=''">
  555. + <_Parameter1>%(_ResolvedRazorExtension.Identity)</_Parameter1>
  556. + <_Parameter2>%(_ResolvedRazorExtension.AssemblyName)</_Parameter2>
  557. + </AssemblyAttribute>
  558. + </ItemGroup>
  559. +
  560. + </Target>
  561. +
  562. <!--
  563. Gathers input source files for code generation. This is a separate target so that we can avoid
  564. lots of work when there are no inputs for code generation.
  565. diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTemplateEngineFactoryService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTemplateEngineFactoryService.cs
  566. index a82d1b8e94c..ab5518bb463 100644
  567. --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTemplateEngineFactoryService.cs
  568. +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTemplateEngineFactoryService.cs
  569. @@ -43,7 +43,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
  570. var project = FindProject(projectPath);
  571. var configuration = (project?.Configuration as MvcExtensibilityConfiguration) ?? DefaultConfiguration;
  572. var razorLanguageVersion = configuration.LanguageVersion;
  573. - var razorConfiguration = new RazorConfiguration(razorLanguageVersion, designTime: true);
  574. + var razorConfiguration = new RazorConfiguration(razorLanguageVersion, "unnamed", Array.Empty<RazorExtension>(), designTime: true);
  575. RazorEngine engine;
  576. if (razorLanguageVersion.Major == 1)
  577. diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs
  578. index 35cb880c1af..1cf20d0e817 100644
  579. --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs
  580. +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs
  581. @@ -100,6 +100,50 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
  582. throw new BuildOutputMissingException(result, match);
  583. }
  584. + public static void FileContainsLine(MSBuildResult result, string filePath, string match)
  585. + {
  586. + if (result == null)
  587. + {
  588. + throw new ArgumentNullException(nameof(result));
  589. + }
  590. +
  591. + filePath = Path.Combine(result.Project.DirectoryPath, filePath);
  592. + FileExists(result, filePath);
  593. +
  594. + var lines = File.ReadAllLines(filePath);
  595. + for (var i = 0; i < lines.Length; i++)
  596. + {
  597. + var line = lines[i].Trim();
  598. + if (line == match)
  599. + {
  600. + return;
  601. + }
  602. + }
  603. +
  604. + throw new FileContentMissingException(result, filePath, File.ReadAllText(filePath), match);
  605. + }
  606. +
  607. + public static void FileDoesNotContainLine(MSBuildResult result, string filePath, string match)
  608. + {
  609. + if (result == null)
  610. + {
  611. + throw new ArgumentNullException(nameof(result));
  612. + }
  613. +
  614. + filePath = Path.Combine(result.Project.DirectoryPath, filePath);
  615. + FileExists(result, filePath);
  616. +
  617. + var lines = File.ReadAllLines(filePath);
  618. + for (var i = 0; i < lines.Length; i++)
  619. + {
  620. + var line = lines[i].Trim();
  621. + if (line == match)
  622. + {
  623. + throw new FileContentFoundException(result, filePath, File.ReadAllText(filePath), match);
  624. + }
  625. + }
  626. + }
  627. +
  628. public static void FileExists(MSBuildResult result, params string[] paths)
  629. {
  630. if (result == null)
  631. @@ -304,6 +348,66 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
  632. protected override string Heading => $"Build did not contain the line: '{Match}'.";
  633. }
  634. + private class FileContentFoundException : MSBuildXunitException
  635. + {
  636. + public FileContentFoundException(MSBuildResult result, string filePath, string content, string match)
  637. + : base(result)
  638. + {
  639. + FilePath = filePath;
  640. + Content = content;
  641. + Match = match;
  642. + }
  643. +
  644. + public string Content { get; }
  645. +
  646. + public string FilePath { get; }
  647. +
  648. + public string Match { get; }
  649. +
  650. + protected override string Heading
  651. + {
  652. + get
  653. + {
  654. + var builder = new StringBuilder();
  655. + builder.AppendFormat("File content of '{0}' should not contain line: '{1}'.", FilePath, Match);
  656. + builder.AppendLine();
  657. + builder.AppendLine();
  658. + builder.AppendLine(Content);
  659. + return builder.ToString();
  660. + }
  661. + }
  662. + }
  663. +
  664. + private class FileContentMissingException : MSBuildXunitException
  665. + {
  666. + public FileContentMissingException(MSBuildResult result, string filePath, string content, string match)
  667. + : base(result)
  668. + {
  669. + FilePath = filePath;
  670. + Content = content;
  671. + Match = match;
  672. + }
  673. +
  674. + public string Content { get; }
  675. +
  676. + public string FilePath { get; }
  677. +
  678. + public string Match { get; }
  679. +
  680. + protected override string Heading
  681. + {
  682. + get
  683. + {
  684. + var builder = new StringBuilder();
  685. + builder.AppendFormat("File content of '{0}' did not contain the line: '{1}'.", FilePath, Match);
  686. + builder.AppendLine();
  687. + builder.AppendLine();
  688. + builder.AppendLine(Content);
  689. + return builder.ToString();
  690. + }
  691. + }
  692. + }
  693. +
  694. private class FileMissingException : MSBuildXunitException
  695. {
  696. public FileMissingException(MSBuildResult result, string filePath)
  697. diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ConfigurationMetadataIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ConfigurationMetadataIntegrationTest.cs
  698. new file mode 100644
  699. index 00000000000..f527157a89a
  700. --- /dev/null
  701. +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ConfigurationMetadataIntegrationTest.cs
  702. @@ -0,0 +1,90 @@
  703. +// Copyright (c) .NET Foundation. All rights reserved.
  704. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  705. +
  706. +using System.IO;
  707. +using System.Threading.Tasks;
  708. +using Xunit;
  709. +
  710. +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
  711. +{
  712. + public class ConfigurationMetadataIntegrationTest : MSBuildIntegrationTestBase
  713. + {
  714. + [Fact]
  715. + [InitializeTestProject("SimpleMvc")]
  716. + public async Task Build_WithMvc_AddsConfigurationMetadata()
  717. + {
  718. + var result = await DotnetMSBuild("Build", $"/p:RazorCompileOnBuild=true");
  719. +
  720. + Assert.BuildPassed(result);
  721. +
  722. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.PrecompiledViews.dll");
  723. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.PrecompiledViews.pdb");
  724. +
  725. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs");
  726. + Assert.FileContainsLine(
  727. + result,
  728. + Path.Combine(IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs"),
  729. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorLanguageVersionAttribute(\"2.1\")]");
  730. + Assert.FileContainsLine(
  731. + result,
  732. + Path.Combine(IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs"),
  733. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorConfigurationNameAttribute(\"MVC-2.1\")]");
  734. + Assert.FileContainsLine(
  735. + result,
  736. + Path.Combine(IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs"),
  737. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorExtensionAssemblyNameAttribute(\"MVC-2.1\", \"Microsoft.AspNetCore.Mvc.Razor.Extensions\")]");
  738. + }
  739. +
  740. + [Fact]
  741. + [InitializeTestProject("SimpleMvc")]
  742. + public async Task Build_WithGenerateRazorAssemblyInfo_False_SuppressesConfigurationMetadata()
  743. + {
  744. + var result = await DotnetMSBuild("Build", $"/p:RazorCompileOnBuild=true /p:GenerateRazorAssemblyInfo=false");
  745. +
  746. + Assert.BuildPassed(result);
  747. +
  748. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.PrecompiledViews.dll");
  749. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.PrecompiledViews.pdb");
  750. +
  751. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs");
  752. + Assert.FileDoesNotContainLine(
  753. + result,
  754. + Path.Combine(IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs"),
  755. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorLanguageVersionAttribute(\"2.1\")]");
  756. + Assert.FileDoesNotContainLine(
  757. + result,
  758. + Path.Combine(IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs"),
  759. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorConfigurationNameAttribute(\"MVC-2.1\")]");
  760. + Assert.FileDoesNotContainLine(
  761. + result,
  762. + Path.Combine(IntermediateOutputPath, "SimpleMvc.AssemblyInfo.cs"),
  763. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorExtensionAssemblyNameAttribute(\"MVC-2.1\", \"Microsoft.AspNetCore.Razor.Extensions\")]");
  764. + }
  765. +
  766. + [Fact]
  767. + [InitializeTestProject("ClassLibrary")]
  768. + public async Task Build_ForClassLibrary_SuppressesConfigurationMetadata()
  769. + {
  770. + var result = await DotnetMSBuild("Build", $"/p:RazorCompileOnBuild=true");
  771. +
  772. + Assert.BuildPassed(result);
  773. +
  774. + Assert.FileExists(result, IntermediateOutputPath, "ClassLibrary.PrecompiledViews.dll");
  775. + Assert.FileExists(result, IntermediateOutputPath, "ClassLibrary.PrecompiledViews.pdb");
  776. +
  777. + Assert.FileExists(result, IntermediateOutputPath, "ClassLibrary.AssemblyInfo.cs");
  778. + Assert.FileDoesNotContainLine(
  779. + result,
  780. + Path.Combine(IntermediateOutputPath, "ClassLibrary.AssemblyInfo.cs"),
  781. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorLanguageVersionAttribute(\"2.1\")]");
  782. + Assert.FileDoesNotContainLine(
  783. + result,
  784. + Path.Combine(IntermediateOutputPath, "ClassLibrary.AssemblyInfo.cs"),
  785. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorConfigurationNameAttribute(\"MVC-2.1\")]");
  786. + Assert.FileDoesNotContainLine(
  787. + result,
  788. + Path.Combine(IntermediateOutputPath, "ClassLibrary.AssemblyInfo.cs"),
  789. + "[assembly: Microsoft.AspNetCore.Razor.Hosting.RazorExtensionAssemblyNameAttribute(\"MVC-2.1\", \"Microsoft.AspNetCore.Razor.Extensions\")]");
  790. + }
  791. + }
  792. +}
  793. diff --git a/test/testapps/AppWithP2PReference/AppWithP2PReference.csproj b/test/testapps/AppWithP2PReference/AppWithP2PReference.csproj
  794. index ac5bb850141..2dc2432d05c 100644
  795. --- a/test/testapps/AppWithP2PReference/AppWithP2PReference.csproj
  796. +++ b/test/testapps/AppWithP2PReference/AppWithP2PReference.csproj
  797. @@ -1,10 +1,13 @@
  798. -<Project Sdk="Microsoft.NET.Sdk.Web">
  799. +<Project>
  800. +
  801. + <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.Web" />
  802. +
  803. <PropertyGroup>
  804. <_RazorMSBuildRoot>$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\bin\$(Configuration)\netstandard2.0\</_RazorMSBuildRoot>
  805. </PropertyGroup>
  806. -
  807. <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.props" />
  808. <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\build\netstandard2.0\Microsoft.AspNetCore.Razor.Design.props" />
  809. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.props" />
  810. <PropertyGroup>
  811. <TargetFramework>netcoreapp2.0</TargetFramework>
  812. @@ -18,4 +21,9 @@
  813. <ItemGroup>
  814. <ProjectReference Include="..\ClassLibrary\ClassLibrary.csproj"/>
  815. </ItemGroup>
  816. +
  817. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.targets" />
  818. +
  819. + <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.Web" />
  820. + <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.targets" />
  821. </Project>
  822. diff --git a/test/testapps/ClassLibrary/ClassLibrary.csproj b/test/testapps/ClassLibrary/ClassLibrary.csproj
  823. index 86cb453382a..f4e970b76f5 100644
  824. --- a/test/testapps/ClassLibrary/ClassLibrary.csproj
  825. +++ b/test/testapps/ClassLibrary/ClassLibrary.csproj
  826. @@ -1,10 +1,14 @@
  827. -<Project Sdk="Microsoft.NET.Sdk">
  828. +<Project>
  829. +
  830. + <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
  831. +
  832. <PropertyGroup>
  833. <_RazorMSBuildRoot>$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\bin\$(Configuration)\netstandard2.0\</_RazorMSBuildRoot>
  834. </PropertyGroup>
  835. <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.props" />
  836. <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\build\netstandard2.0\Microsoft.AspNetCore.Razor.Design.props" />
  837. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.props" />
  838. <PropertyGroup>
  839. <TargetFramework>netcoreapp2.0</TargetFramework>
  840. @@ -20,4 +24,10 @@
  841. <Pack>false</Pack>
  842. </Content>
  843. </ItemGroup>
  844. +
  845. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.targets" />
  846. +
  847. + <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
  848. + <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.targets" />
  849. +
  850. </Project>
  851. diff --git a/test/testapps/Directory.Build.targets b/test/testapps/Directory.Build.targets
  852. index 4f2e91835cc..8c119d5413b 100644
  853. --- a/test/testapps/Directory.Build.targets
  854. +++ b/test/testapps/Directory.Build.targets
  855. @@ -1,3 +1,2 @@
  856. <Project>
  857. - <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.targets" />
  858. </Project>
  859. diff --git a/test/testapps/SimpleMvc/SimpleMvc.csproj b/test/testapps/SimpleMvc/SimpleMvc.csproj
  860. index 4525726119d..d2f6e05d851 100644
  861. --- a/test/testapps/SimpleMvc/SimpleMvc.csproj
  862. +++ b/test/testapps/SimpleMvc/SimpleMvc.csproj
  863. @@ -1,10 +1,14 @@
  864. -<Project Sdk="Microsoft.NET.Sdk.Web">
  865. +<Project>
  866. +
  867. + <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.Web" />
  868. +
  869. <PropertyGroup>
  870. <_RazorMSBuildRoot>$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\bin\$(Configuration)\netstandard2.0\</_RazorMSBuildRoot>
  871. </PropertyGroup>
  872. <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.props" />
  873. <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\build\netstandard2.0\Microsoft.AspNetCore.Razor.Design.props" />
  874. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.props" />
  875. <PropertyGroup>
  876. <TargetFramework>netcoreapp2.0</TargetFramework>
  877. @@ -15,4 +19,9 @@
  878. <!-- Test Placeholder -->
  879. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.targets" />
  880. +
  881. + <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.Web" />
  882. + <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.targets" />
  883. +
  884. </Project>
  885. diff --git a/test/testapps/SimplePages/SimplePages.csproj b/test/testapps/SimplePages/SimplePages.csproj
  886. index 4525726119d..12865d018c4 100644
  887. --- a/test/testapps/SimplePages/SimplePages.csproj
  888. +++ b/test/testapps/SimplePages/SimplePages.csproj
  889. @@ -1,10 +1,14 @@
  890. -<Project Sdk="Microsoft.NET.Sdk.Web">
  891. +<Project>
  892. +
  893. + <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.Web" />
  894. +
  895. <PropertyGroup>
  896. <_RazorMSBuildRoot>$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\bin\$(Configuration)\netstandard2.0\</_RazorMSBuildRoot>
  897. </PropertyGroup>
  898. <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.props" />
  899. <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Razor.Design\build\netstandard2.0\Microsoft.AspNetCore.Razor.Design.props" />
  900. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.props" />
  901. <PropertyGroup>
  902. <TargetFramework>netcoreapp2.0</TargetFramework>
  903. @@ -15,4 +19,9 @@
  904. <!-- Test Placeholder -->
  905. + <Import Project="$(SolutionRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.targets" />
  906. +
  907. + <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.Web" />
  908. + <Import Project="$(SolutionRoot)src\Microsoft.NET.Sdk.Razor\SDK\Sdk.targets" />
  909. +
  910. </Project>