GenerateStaticWebAssetsDevelopmentManifestTest.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using Microsoft.AspNetCore.StaticWebAssets.Tasks;
  4. using Microsoft.Build.Framework;
  5. using Moq;
  6. using static Microsoft.AspNetCore.StaticWebAssets.Tasks.GenerateStaticWebAssetsDevelopmentManifest;
  7. namespace Microsoft.NET.Sdk.Razor.Tests
  8. {
  9. public class GenerateStaticWebAssetsDevelopmentManifestTest
  10. {
  11. [Fact]
  12. public void SkipsManifestGenerationWhen_ThereAreNoAssetsNorDiscoveryPatterns()
  13. {
  14. // Arrange
  15. var messages = new List<string>();
  16. var buildEngine = new Mock<IBuildEngine>();
  17. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  18. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  19. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  20. {
  21. BuildEngine = buildEngine.Object,
  22. Assets = Array.Empty<ITaskItem>(),
  23. DiscoveryPatterns = Array.Empty<ITaskItem>()
  24. };
  25. // Act
  26. var result = task.Execute();
  27. // Assert
  28. result.Should().BeTrue();
  29. messages.Should().HaveCount(1);
  30. }
  31. [Fact]
  32. public void ComputeDevelopmentManifest_IncludesBuildAssets()
  33. {
  34. // Arrange
  35. var messages = new List<string>();
  36. var buildEngine = new Mock<IBuildEngine>();
  37. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  38. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  39. var expectedManifest = CreateExpectedManifest(
  40. CreateIntermediateNode(
  41. ("index.html", CreateMatchNode(0, "index.html"))),
  42. Environment.CurrentDirectory);
  43. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  44. {
  45. BuildEngine = buildEngine.Object,
  46. };
  47. var assets = new[] { CreateAsset("index.html", "index.html", assetKind: StaticWebAsset.AssetKinds.Build) };
  48. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  49. // Act
  50. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  51. // Assert
  52. manifest.Should().BeEquivalentTo(expectedManifest);
  53. }
  54. [Fact]
  55. public void ComputeDevelopmentManifest_IncludesAllAssets()
  56. {
  57. // Arrange
  58. var messages = new List<string>();
  59. var buildEngine = new Mock<IBuildEngine>();
  60. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  61. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  62. var expectedManifest = CreateExpectedManifest(
  63. CreateIntermediateNode(
  64. ("index.html", CreateMatchNode(0, "index.html"))),
  65. Environment.CurrentDirectory);
  66. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  67. {
  68. BuildEngine = buildEngine.Object,
  69. };
  70. var assets = new[] { CreateAsset("index.html", "index.html", assetKind: StaticWebAsset.AssetKinds.All) };
  71. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  72. // Act
  73. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  74. // Assert
  75. manifest.Should().BeEquivalentTo(expectedManifest);
  76. }
  77. [Fact]
  78. public void ComputeDevelopmentManifest_ExcludesPublishAssets()
  79. {
  80. // Arrange
  81. var messages = new List<string>();
  82. var buildEngine = new Mock<IBuildEngine>();
  83. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  84. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  85. var expectedManifest = CreateExpectedManifest(
  86. CreateIntermediateNode());
  87. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  88. {
  89. BuildEngine = buildEngine.Object,
  90. };
  91. var assets = new[] { CreateAsset("index.html", "index.html", assetKind: StaticWebAsset.AssetKinds.Publish) };
  92. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  93. // Act
  94. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  95. // Assert
  96. manifest.Should().BeEquivalentTo(expectedManifest);
  97. }
  98. [Fact]
  99. public void ComputeDevelopmentManifest_ExcludesReferenceAssets()
  100. {
  101. // Arrange
  102. var messages = new List<string>();
  103. var buildEngine = new Mock<IBuildEngine>();
  104. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  105. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  106. var expectedManifest = CreateExpectedManifest(
  107. CreateIntermediateNode());
  108. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  109. {
  110. BuildEngine = buildEngine.Object,
  111. Source = "CurrentProjectId"
  112. };
  113. var assets = new[] { CreateAsset("index.html", "index.html", assetMode: StaticWebAsset.AssetModes.Reference) };
  114. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  115. // Act
  116. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  117. // Assert
  118. manifest.Should().BeEquivalentTo(expectedManifest);
  119. }
  120. [Fact]
  121. public void ComputeDevelopmentManifest_PrefersBuildAssetsOverAllAssets()
  122. {
  123. // Arrange
  124. var messages = new List<string>();
  125. var buildEngine = new Mock<IBuildEngine>();
  126. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  127. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  128. var expectedManifest = CreateExpectedManifest(
  129. CreateIntermediateNode(
  130. ("index.html", CreateMatchNode(0, "index.build.html"))),
  131. Environment.CurrentDirectory);
  132. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  133. {
  134. BuildEngine = buildEngine.Object,
  135. Source = "CurrentProjectId"
  136. };
  137. var assets = new[] {
  138. CreateAsset("index.build.html", "index.html", assetKind: StaticWebAsset.AssetKinds.Build),
  139. CreateAsset("index.html", "index.html", assetKind: StaticWebAsset.AssetKinds.All)
  140. };
  141. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  142. // Act
  143. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  144. // Assert
  145. manifest.Should().BeEquivalentTo(expectedManifest);
  146. }
  147. [Fact]
  148. public void ComputeDevelopmentManifest_UsesIdentityWhenContentRootStartsByIdentity()
  149. {
  150. // Arrange
  151. var messages = new List<string>();
  152. var buildEngine = new Mock<IBuildEngine>();
  153. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  154. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  155. var expectedManifest = CreateExpectedManifest(
  156. CreateIntermediateNode(
  157. ("index.html", CreateMatchNode(0, StaticWebAsset.Normalize(Path.Combine("some", "subfolder", "index.build.html"))))),
  158. Environment.CurrentDirectory);
  159. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  160. {
  161. BuildEngine = buildEngine.Object,
  162. Source = "CurrentProjectId"
  163. };
  164. var assets = new[] {
  165. CreateAsset(Path.Combine("some", "subfolder", "index.build.html"), "index.html"),
  166. };
  167. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  168. // Act
  169. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  170. // Assert
  171. manifest.Should().BeEquivalentTo(expectedManifest);
  172. }
  173. [Fact]
  174. public void ComputeDevelopmentManifest_UsesRelativePathContentRootDoesNotStartByIdentity()
  175. {
  176. // Arrange
  177. var messages = new List<string>();
  178. var buildEngine = new Mock<IBuildEngine>();
  179. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  180. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  181. var expectedManifest = CreateExpectedManifest(
  182. CreateIntermediateNode(
  183. ("index.html", CreateMatchNode(0, "index.html"))),
  184. Path.GetFullPath(Path.Combine("bin", "debug", "wwwroot")));
  185. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  186. {
  187. BuildEngine = buildEngine.Object,
  188. Source = "CurrentProjectId"
  189. };
  190. var assets = new[] {
  191. CreateAsset(Path.Combine("some", "subfolder", "index.build.html"), "index.html", contentRoot: Path.Combine("bin", "debug", "wwwroot")),
  192. };
  193. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  194. // Act
  195. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  196. // Assert
  197. manifest.Should().BeEquivalentTo(expectedManifest);
  198. }
  199. [Fact]
  200. public void ComputeDevelopmentManifest_MapsPatternsFromCurrentProject()
  201. {
  202. // Arrange
  203. var messages = new List<string>();
  204. var buildEngine = new Mock<IBuildEngine>();
  205. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  206. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  207. var expectedManifest = CreateExpectedManifest(
  208. CreateIntermediateNode()
  209. .AddPatterns((0, "**", 0)),
  210. Path.GetFullPath("wwwroot"));
  211. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  212. {
  213. BuildEngine = buildEngine.Object,
  214. Source = "CurrentProjectId"
  215. };
  216. var assets = Array.Empty<StaticWebAsset>();
  217. var patterns = new[] { CreatePattern() };
  218. // Act
  219. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  220. // Assert
  221. manifest.Should().BeEquivalentTo(expectedManifest);
  222. }
  223. [Fact]
  224. public void ComputeDevelopmentManifest_MapsPatternsFromOtherProjects()
  225. {
  226. // Arrange
  227. var messages = new List<string>();
  228. var buildEngine = new Mock<IBuildEngine>();
  229. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  230. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  231. var expectedManifest = CreateExpectedManifest(
  232. CreateIntermediateNode(
  233. ("_other", CreateIntermediateNode(
  234. ("_project", CreateIntermediateNode().AddPatterns((0, "**", 2)))))),
  235. Path.GetFullPath("wwwroot"));
  236. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  237. {
  238. BuildEngine = buildEngine.Object,
  239. Source = "CurrentProjectId"
  240. };
  241. var assets = Array.Empty<StaticWebAsset>();
  242. var patterns = new[] { CreatePattern(basePath: "_other/_project", source: "OtherProject") };
  243. // Act
  244. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  245. // Assert
  246. manifest.Should().BeEquivalentTo(expectedManifest);
  247. }
  248. [Fact]
  249. public void ComputeDevelopmentManifest_CanMapMultiplePatternsOnSameNode()
  250. {
  251. // Arrange
  252. var messages = new List<string>();
  253. var buildEngine = new Mock<IBuildEngine>();
  254. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  255. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  256. var expectedManifest = CreateExpectedManifest(
  257. CreateIntermediateNode(
  258. ("_other", CreateIntermediateNode(
  259. ("_project", CreateIntermediateNode().AddPatterns(
  260. (0, "*.js", 2),
  261. (0, "*.css", 2)))))),
  262. Path.GetFullPath("wwwroot"));
  263. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  264. {
  265. BuildEngine = buildEngine.Object,
  266. Source = "CurrentProjectId"
  267. };
  268. var assets = Array.Empty<StaticWebAsset>();
  269. var patterns = new[]
  270. {
  271. CreatePattern(basePath: "_other/_project", source: "OtherProject", pattern: "*.js"),
  272. CreatePattern(basePath: "_other/_project", source: "OtherProject", pattern: "*.css")
  273. };
  274. // Act
  275. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  276. // Assert
  277. manifest.Should().BeEquivalentTo(expectedManifest);
  278. }
  279. [Fact]
  280. public void ComputeDevelopmentManifest_CanMapMultiplePatternsOnSameNodeWithDifferentContentRoots()
  281. {
  282. // Arrange
  283. var messages = new List<string>();
  284. var buildEngine = new Mock<IBuildEngine>();
  285. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  286. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  287. var expectedManifest = CreateExpectedManifest(
  288. CreateIntermediateNode(
  289. ("_other", CreateIntermediateNode(
  290. ("_project", CreateIntermediateNode().AddPatterns(
  291. (0, "*.js", 2),
  292. (1, "*.css", 2)))))),
  293. Path.GetFullPath("wwwroot"),
  294. Path.GetFullPath("styles"));
  295. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  296. {
  297. BuildEngine = buildEngine.Object,
  298. Source = "CurrentProjectId"
  299. };
  300. var assets = Array.Empty<StaticWebAsset>();
  301. var patterns = new[]
  302. {
  303. CreatePattern(basePath: "_other/_project", source: "OtherProject", pattern: "*.js"),
  304. CreatePattern(basePath: "_other/_project", source: "OtherProject", pattern: "*.css", contentRoot: Path.GetFullPath("styles"))
  305. };
  306. // Act
  307. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  308. // Assert
  309. manifest.Should().BeEquivalentTo(expectedManifest);
  310. }
  311. [Fact]
  312. public void ComputeDevelopmentManifest_MultipleAssetsSameContentRoot()
  313. {
  314. // Arrange
  315. var messages = new List<string>();
  316. var buildEngine = new Mock<IBuildEngine>();
  317. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  318. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  319. var expectedManifest = CreateExpectedManifest(
  320. CreateIntermediateNode(
  321. ("css", CreateIntermediateNode(("site.css", CreateMatchNode(0, "css/site.css")))),
  322. ("js", CreateIntermediateNode(("index.js", CreateMatchNode(0, "js/index.js"))))),
  323. Environment.CurrentDirectory);
  324. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  325. {
  326. BuildEngine = buildEngine.Object,
  327. Source = "CurrentProjectId"
  328. };
  329. var assets = new[]
  330. {
  331. CreateAsset(Path.Combine(Environment.CurrentDirectory, "css", "site.css"), "css/site.css"),
  332. CreateAsset(Path.Combine(Environment.CurrentDirectory, "js", "index.js"), "js/index.js")
  333. };
  334. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  335. // Act
  336. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  337. // Assert
  338. manifest.Should().BeEquivalentTo(expectedManifest);
  339. }
  340. [Fact]
  341. public void ComputeDevelopmentManifest_DifferentCasingEndUpInDifferentNodes()
  342. {
  343. // Arrange
  344. var messages = new List<string>();
  345. var buildEngine = new Mock<IBuildEngine>();
  346. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  347. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  348. var expectedManifest = CreateExpectedManifest(
  349. CreateIntermediateNode(
  350. ("css", CreateIntermediateNode(("site.css", CreateMatchNode(0, "css/site.css")))),
  351. ("CSS", CreateIntermediateNode(("site.css", CreateMatchNode(0, "CSS/site.css"))))),
  352. Environment.CurrentDirectory);
  353. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  354. {
  355. BuildEngine = buildEngine.Object,
  356. Source = "CurrentProjectId"
  357. };
  358. var assets = new[]
  359. {
  360. CreateAsset(Path.Combine(Environment.CurrentDirectory, "css", "site.css"), "css/site.css"),
  361. CreateAsset(Path.Combine(Environment.CurrentDirectory, "CSS", "site.css"), "CSS/site.css"),
  362. };
  363. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  364. // Act
  365. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  366. // Assert
  367. manifest.Should().BeEquivalentTo(expectedManifest);
  368. }
  369. [Fact]
  370. public void ComputeDevelopmentManifest_UsesBasePathForAssetsFromDifferentProjects()
  371. {
  372. // Arrange
  373. var messages = new List<string>();
  374. var buildEngine = new Mock<IBuildEngine>();
  375. buildEngine.Setup(e => e.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
  376. .Callback<BuildMessageEventArgs>(args => messages.Add(args.Message));
  377. var expectedManifest = CreateExpectedManifest(
  378. CreateIntermediateNode(
  379. ("css", CreateIntermediateNode(("site.css", CreateMatchNode(0, "css/site.css")))),
  380. ("_content", CreateIntermediateNode(
  381. ("OtherProject", CreateIntermediateNode(
  382. ("CSS", CreateIntermediateNode(("site.css", CreateMatchNode(1, "CSS/site.css"))))))))),
  383. Environment.CurrentDirectory,
  384. Path.GetFullPath("otherProject"));
  385. var task = new GenerateStaticWebAssetsDevelopmentManifest()
  386. {
  387. BuildEngine = buildEngine.Object,
  388. Source = "CurrentProjectId"
  389. };
  390. var assets = new[]
  391. {
  392. CreateAsset(Path.Combine(Environment.CurrentDirectory, "css", "site.css"), "css/site.css"),
  393. CreateAsset(
  394. Path.Combine(Environment.CurrentDirectory, "CSS", "site.css"),
  395. "CSS/site.css",
  396. basePath: "_content/OtherProject",
  397. sourceType: "Project",
  398. contentRoot: Path.GetFullPath("otherProject")),
  399. };
  400. var patterns = Array.Empty<StaticWebAssetsDiscoveryPattern>();
  401. // Act
  402. var manifest = task.ComputeDevelopmentManifest(assets, patterns);
  403. // Assert
  404. manifest.Should().BeEquivalentTo(expectedManifest);
  405. }
  406. private static StaticWebAssetsDiscoveryPattern CreatePattern(
  407. string name = null,
  408. string contentRoot = null,
  409. string pattern = null,
  410. string basePath = null,
  411. string source = null) =>
  412. new()
  413. {
  414. Name = name ?? "CurrentProjectId\\wwwroot",
  415. Pattern = pattern ?? "**",
  416. BasePath = basePath ?? "_content/CurrentProjectId",
  417. Source = source ?? "CurrentProjectId",
  418. ContentRoot = StaticWebAsset.NormalizeContentRootPath(contentRoot ?? Path.Combine(Environment.CurrentDirectory, "wwwroot"))
  419. };
  420. private static StaticWebAssetsDevelopmentManifest CreateExpectedManifest(StaticWebAssetNode root, params string[] contentRoots)
  421. {
  422. return new StaticWebAssetsDevelopmentManifest()
  423. {
  424. ContentRoots = contentRoots.Select(cr => StaticWebAsset.NormalizeContentRootPath(cr)).ToArray(),
  425. Root = root
  426. };
  427. }
  428. private static StaticWebAssetNode CreateIntermediateNode(params (string key, StaticWebAssetNode node)[] children) => new()
  429. {
  430. Children = children.Length == 0 ? null : children.ToDictionary(pair => pair.key, pair => pair.node)
  431. };
  432. private static StaticWebAssetNode CreateMatchNode(int index, string subpath) => new()
  433. {
  434. Asset = new StaticWebAssetMatch { ContentRootIndex = index, SubPath = subpath }
  435. };
  436. private StaticWebAsset CreateAsset(
  437. string identity,
  438. string relativePath,
  439. string assetKind = default,
  440. string assetMode = default,
  441. string sourceId = default,
  442. string sourceType = default,
  443. string basePath = default,
  444. string contentRoot = default)
  445. {
  446. return new StaticWebAsset()
  447. {
  448. Identity = Path.GetFullPath(identity),
  449. SourceId = sourceId ?? "CurrentProjectId",
  450. SourceType = sourceType ?? StaticWebAsset.SourceTypes.Discovered,
  451. BasePath = basePath ?? "_content/Base",
  452. RelativePath = relativePath,
  453. AssetKind = assetKind ?? StaticWebAsset.AssetKinds.All,
  454. AssetMode = assetMode ?? StaticWebAsset.AssetModes.All,
  455. AssetRole = StaticWebAsset.AssetRoles.Primary,
  456. ContentRoot = StaticWebAsset.NormalizeContentRootPath(contentRoot ?? Environment.CurrentDirectory),
  457. OriginalItemSpec = identity
  458. };
  459. }
  460. }
  461. internal static class StaticWebAssetNodeTestExtensions
  462. {
  463. public static StaticWebAssetNode AddPatterns(this StaticWebAssetNode node, params (int contentRoot, string pattern, int depth)[] patterns)
  464. {
  465. node.Patterns = patterns.Select(p => new StaticWebAssetPattern { ContentRootIndex = p.contentRoot, Pattern = p.pattern, Depth = p.depth }).ToArray();
  466. return node;
  467. }
  468. }
  469. }