Add support for relative paths
* Move path munging in to Razor SDK * Use AssignTargetPath to determine the target path for outputs and embedded resources Fixes #1829 Fixes #1847 Fixes #1999
This commit is contained in:
parent
5a93f68ccc
commit
84beb5985f
|
|
@ -36,3 +36,5 @@ global.json
|
|||
BenchmarkDotNet.Artifacts/
|
||||
Microsoft.VisualStudio.RazorExtension.nuget.props
|
||||
Microsoft.VisualStudio.RazorExtension.nuget.targets
|
||||
msbuild.binlog
|
||||
msbuild.log
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@
|
|||
-->
|
||||
<Target
|
||||
Name="_HashRazorGenerateInputs"
|
||||
Condition="'@(RazorGenerate)'!=''">
|
||||
Condition="'@(RazorGenerateWithTargetPath)' != ''">
|
||||
|
||||
<Hash ItemsToHash="@(RazorGenerate)">
|
||||
<Hash ItemsToHash="@(RazorGenerateWithTargetPath)">
|
||||
<Output TaskParameter="HashResult" PropertyName="_RazorGenerateInputsHash" />
|
||||
</Hash>
|
||||
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
DependsOnTargets="Compile"
|
||||
Inputs="$(MSBuildAllProjects);@(RazorReferencePath)"
|
||||
Outputs="$(_RazorTagHelperInputCache)"
|
||||
Condition="'@(RazorGenerate)'!=''">
|
||||
Condition="'@(RazorGenerateWithTargetPath)' != ''">
|
||||
|
||||
<!--
|
||||
We're manipulating our output directly here because we want to separate the actual up-to-date check
|
||||
|
|
@ -81,9 +81,13 @@
|
|||
</RazorTagHelper>
|
||||
</Target>
|
||||
|
||||
<Target Name="_ResolveRazorGenerateOutputs">
|
||||
<Target Name="_ResolveRazorGenerateOutputs" Condition="'@(RazorGenerateWithTargetPath)' != ''">
|
||||
<Error
|
||||
Text="RazorGenerateWithTargetPath item '%(RazorGenerateWithTargetPath.Identity)' does not specify required metadata 'GeneratedOutput'."
|
||||
Condition="'%(RazorGenerateWithTargetPath.GeneratedOutput)' == ''" />
|
||||
|
||||
<ItemGroup>
|
||||
<_RazorGenerateOutput Include="%(RazorGenerate.GeneratedOutput)" Condition="'%(RazorGenerate.GeneratedOutput)'!=''"/>
|
||||
<_RazorGenerateOutput Include="%(RazorGenerateWithTargetPath.GeneratedOutput)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
|
@ -97,9 +101,9 @@
|
|||
<Target
|
||||
Name="RazorCoreGenerate"
|
||||
DependsOnTargets="$(RazorCoreGenerateDependsOn)"
|
||||
Inputs="$(MSBuildAllProjects);$(_RazorGenerateInputsHashFile);$(_RazorTagHelperOutputCache);@(RazorGenerate)"
|
||||
Inputs="$(MSBuildAllProjects);$(_RazorGenerateInputsHashFile);$(_RazorTagHelperOutputCache);@(RazorGenerateWithTargetPath)"
|
||||
Outputs="@(_RazorGenerateOutput)"
|
||||
Condition="'@(RazorGenerate)'!= ''">
|
||||
Condition="'@(RazorGenerateWithTargetPath)'!= ''">
|
||||
|
||||
<RemoveDir
|
||||
Directories="$(RazorGenerateIntermediateOutputPath)"
|
||||
|
|
@ -115,10 +119,9 @@
|
|||
ToolAssembly="$(_RazorToolAssembly)"
|
||||
UseServer="$(UseRazorBuildServer)"
|
||||
PipeName="$(_RazorBuildServerPipeName)"
|
||||
Sources="@(RazorGenerate)"
|
||||
Sources="@(RazorGenerateWithTargetPath)"
|
||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||
TagHelperManifest="$(_RazorTagHelperOutputCache)"
|
||||
OutputPath="$(RazorGenerateIntermediateOutputPath)" />
|
||||
TagHelperManifest="$(_RazorTagHelperOutputCache)" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="@(_RazorGenerateOutput)" />
|
||||
|
|
@ -132,18 +135,6 @@
|
|||
<Target Name="_ResolveGeneratedRazorCompileInputs">
|
||||
<ItemGroup>
|
||||
<RazorCompile Include="@(_RazorGenerateOutput)" />
|
||||
<RazorEmbeddedResource Include="@(RazorGenerate)" Condition="$(EmbedRazorGenerateSources)">
|
||||
<LogicalName>/$([System.String]::Copy('%(Identity)').Replace('\','/'))</LogicalName>
|
||||
<Type>Non-Resx</Type>
|
||||
<WithCulture>false</WithCulture>
|
||||
</RazorEmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Similar to _GenerateCompileInputs -->
|
||||
<ItemGroup>
|
||||
<_RazorCoreCompileResourceInputs
|
||||
Include="@(RazorEmbeddedResource)"
|
||||
Condition="'%(RazorEmbeddedResource.WithCulture)'=='false' and '%(RazorEmbeddedResource.Type)'=='Non-Resx' " />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using System.Linq;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class FileSystemRazorProject : RazorProject
|
||||
internal class FileSystemRazorProject : RazorProjectFileSystem
|
||||
{
|
||||
public FileSystemRazorProject(string root)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ using System.Runtime.CompilerServices;
|
|||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Language.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.TagHelperTool, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Test.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Tools.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("rzc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -1870,6 +1870,34 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
internal static string FormatRazorLanguageVersion_InvalidVersion(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("RazorLanguageVersion_InvalidVersion"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// File path '{0}' does not belong to the directory '{1}'.
|
||||
/// </summary>
|
||||
internal static string VirtualFileSystem_FileDoesNotBelongToDirectory
|
||||
{
|
||||
get => GetString("VirtualFileSystem_FileDoesNotBelongToDirectory");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File path '{0}' does not belong to the directory '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatVirtualFileSystem_FileDoesNotBelongToDirectory(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("VirtualFileSystem_FileDoesNotBelongToDirectory"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// The file path '{0}' is invalid. File path is the root relative path of the file starting with '/' and should not contain any '\' characters.
|
||||
/// </summary>
|
||||
internal static string VirtualFileSystem_InvalidRelativePath
|
||||
{
|
||||
get => GetString("VirtualFileSystem_InvalidRelativePath");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file path '{0}' is invalid. File path is the root relative path of the file starting with '/' and should not contain any '\' characters.
|
||||
/// </summary>
|
||||
internal static string FormatVirtualFileSystem_InvalidRelativePath(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("VirtualFileSystem_InvalidRelativePath"), p0);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -5,5 +5,14 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
public abstract class RazorProjectFileSystem : RazorProject
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a Razor project based on a physical file system.
|
||||
/// </summary>
|
||||
/// <param name="rootDirectoryPath">The directory to root the file system at.</param>
|
||||
/// <returns>A <see cref="RazorProject"/></returns>
|
||||
public static new RazorProjectFileSystem Create(string rootDirectoryPath)
|
||||
{
|
||||
return new FileSystemRazorProject(rootDirectoryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -536,4 +536,10 @@ Instead, wrap the contents of the block in "{{}}":
|
|||
<data name="RazorLanguageVersion_InvalidVersion" xml:space="preserve">
|
||||
<value>The Razor language version '{0}' is unrecognized or not supported by this version of Razor.</value>
|
||||
</data>
|
||||
<data name="VirtualFileSystem_FileDoesNotBelongToDirectory" xml:space="preserve">
|
||||
<value>File path '{0}' does not belong to the directory '{1}'.</value>
|
||||
</data>
|
||||
<data name="VirtualFileSystem_InvalidRelativePath" xml:space="preserve">
|
||||
<value>The file path '{0}' is invalid. File path is the root relative path of the file starting with '/' and should not contain any '\' characters.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal class VirtualRazorProjectFileSystem : RazorProjectFileSystem
|
||||
{
|
||||
private readonly DirectoryNode Root = new DirectoryNode("/");
|
||||
|
||||
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
|
||||
{
|
||||
|
||||
basePath = NormalizeAndEnsureValidPath(basePath);
|
||||
var directory = Root.GetDirectory(basePath);
|
||||
return directory?.EnumerateItems() ?? Enumerable.Empty<RazorProjectItem>();
|
||||
}
|
||||
|
||||
public override RazorProjectItem GetItem(string path)
|
||||
{
|
||||
path = NormalizeAndEnsureValidPath(path);
|
||||
return Root.GetItem(path) ?? new NotFoundProjectItem(string.Empty, path);
|
||||
}
|
||||
|
||||
public void Add(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var filePath = NormalizeAndEnsureValidPath(projectItem.FilePath);
|
||||
Root.AddFile(new FileNode(filePath, projectItem));
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
[DebuggerDisplay("{Path}")]
|
||||
internal class DirectoryNode
|
||||
{
|
||||
public DirectoryNode(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public List<DirectoryNode> Directories { get; } = new List<DirectoryNode>();
|
||||
|
||||
public List<FileNode> Files { get; } = new List<FileNode>();
|
||||
|
||||
public void AddFile(FileNode fileNode)
|
||||
{
|
||||
var filePath = fileNode.Path;
|
||||
if (!filePath.StartsWith(Path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var message = Resources.FormatVirtualFileSystem_FileDoesNotBelongToDirectory(fileNode.Path, Path);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
// Look for the first / that appears in the path after the current directory path.
|
||||
var directoryPath = GetDirectoryPath(filePath);
|
||||
var directory = GetOrAddDirectory(this, directoryPath, createIfNotExists: true);
|
||||
Debug.Assert(directory != null);
|
||||
directory.Files.Add(fileNode);
|
||||
}
|
||||
|
||||
public DirectoryNode GetDirectory(string path)
|
||||
{
|
||||
if (!path.StartsWith(Path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var message = Resources.FormatVirtualFileSystem_FileDoesNotBelongToDirectory(path, Path);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
return GetOrAddDirectory(this, path);
|
||||
}
|
||||
|
||||
public IEnumerable<RazorProjectItem> EnumerateItems()
|
||||
{
|
||||
foreach (var file in Files)
|
||||
{
|
||||
yield return file.ProjectItem;
|
||||
}
|
||||
|
||||
foreach (var directory in Directories)
|
||||
{
|
||||
foreach (var file in directory.EnumerateItems())
|
||||
{
|
||||
yield return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RazorProjectItem GetItem(string path)
|
||||
{
|
||||
if (!path.StartsWith(Path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatVirtualFileSystem_FileDoesNotBelongToDirectory(path, Path));
|
||||
}
|
||||
|
||||
var directoryPath = GetDirectoryPath(path);
|
||||
var directory = GetOrAddDirectory(this, directoryPath);
|
||||
if (directory == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var file in directory.Files)
|
||||
{
|
||||
var filePath = file.Path;
|
||||
var directoryLength = directory.Path.Length;
|
||||
|
||||
// path, filePath -> /Views/Home/Index.cshtml
|
||||
// directory.Path -> /Views/Home/
|
||||
// We only need to match the file name portion since we've already matched the directory segment.
|
||||
if (string.Compare(path, directoryLength, filePath, directoryLength, path.Length - directoryLength, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
return file.ProjectItem;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetDirectoryPath(string path)
|
||||
{
|
||||
// /dir1/dir2/file.cshtml -> /dir1/dir2/
|
||||
var fileNameIndex = path.LastIndexOf('/');
|
||||
if (fileNameIndex == -1)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.Substring(0, fileNameIndex + 1);
|
||||
}
|
||||
|
||||
private static DirectoryNode GetOrAddDirectory(
|
||||
DirectoryNode directory,
|
||||
string path,
|
||||
bool createIfNotExists = false)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(path));
|
||||
if (path[path.Length - 1] != '/')
|
||||
{
|
||||
path += '/';
|
||||
}
|
||||
|
||||
int index;
|
||||
while ((index = path.IndexOf('/', directory.Path.Length)) != -1 && index != path.Length)
|
||||
{
|
||||
var subDirectory = FindSubDirectory(directory, path);
|
||||
|
||||
if (subDirectory == null)
|
||||
{
|
||||
if (createIfNotExists)
|
||||
{
|
||||
var directoryPath = path.Substring(0, index + 1); // + 1 to include trailing slash
|
||||
subDirectory = new DirectoryNode(directoryPath);
|
||||
directory.Directories.Add(subDirectory);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
directory = subDirectory;
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
private static DirectoryNode FindSubDirectory(DirectoryNode parentDirectory, string path)
|
||||
{
|
||||
for (var i = 0; i < parentDirectory.Directories.Count; i++)
|
||||
{
|
||||
// ParentDirectory.Path -> /Views/Home/
|
||||
// CurrentDirectory.Path -> /Views/Home/SubDir/
|
||||
// Path -> /Views/Home/SubDir/MorePath/File.cshtml
|
||||
// Each invocation of FindSubDirectory returns the immediate subdirectory along the path to the file.
|
||||
|
||||
var currentDirectory = parentDirectory.Directories[i];
|
||||
var directoryPath = currentDirectory.Path;
|
||||
var startIndex = parentDirectory.Path.Length;
|
||||
var directoryNameLength = directoryPath.Length - startIndex;
|
||||
|
||||
if (string.Compare(path, startIndex, directoryPath, startIndex, directoryPath.Length - startIndex, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
return currentDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
[DebuggerDisplay("{Path}")]
|
||||
internal struct FileNode
|
||||
{
|
||||
public FileNode(string path, RazorProjectItem projectItem)
|
||||
{
|
||||
Path = path;
|
||||
ProjectItem = projectItem;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public RazorProjectItem ProjectItem { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,9 +69,9 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
{
|
||||
if (Debug)
|
||||
{
|
||||
Log.LogMessage(MessageImportance.High, "Waiting for debugger in pid: {0}", Process.GetCurrentProcess().Id);
|
||||
while (!Debugger.IsAttached)
|
||||
{
|
||||
Log.LogMessage(MessageImportance.High, "Waiting for debugger in pid: {0}", Process.GetCurrentProcess().Id);
|
||||
Thread.Sleep(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
}
|
||||
|
|
@ -174,5 +174,18 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
CommandLineUtilities.SplitCommandLineIntoArguments(responseFileCommands, removeHashComments: true);
|
||||
return responseFileArguments.ToList();
|
||||
}
|
||||
|
||||
protected override bool HandleTaskExecutionErrors()
|
||||
{
|
||||
if (!HasLoggedErrors)
|
||||
{
|
||||
var toolCommand = Path.GetFileNameWithoutExtension(ToolAssembly) + " " + Command;
|
||||
// Show a slightly better error than the standard ToolTask message that says "dotnet" failed.
|
||||
Log.LogError($"{toolCommand} exited with code {ExitCode}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.HandleTaskExecutionErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,44 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.CodeAnalysis.CommandLine;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
public class RazorGenerate : DotNetToolTask
|
||||
{
|
||||
private const string GeneratedOutput = "GeneratedOutput";
|
||||
private const string TargetPath = "TargetPath";
|
||||
private const string FullPath = "FullPath";
|
||||
|
||||
[Required]
|
||||
public string[] Sources { get; set; }
|
||||
public ITaskItem[] Sources { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ProjectRoot { get; set; }
|
||||
|
||||
[Required]
|
||||
public string OutputPath { get; set; }
|
||||
|
||||
[Required]
|
||||
public string TagHelperManifest { get; set; }
|
||||
|
||||
internal override string Command => "generate";
|
||||
|
||||
protected override bool ValidateParameters()
|
||||
{
|
||||
for (var i = 0; i < Sources.Length; i++)
|
||||
{
|
||||
if (!EnsureRequiredMetadata(Sources[i], FullPath) ||
|
||||
!EnsureRequiredMetadata(Sources[i], GeneratedOutput) ||
|
||||
!EnsureRequiredMetadata(Sources[i], TargetPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return base.ValidateParameters();
|
||||
}
|
||||
|
||||
protected override string GenerateResponseFileCommands()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
|
@ -31,19 +47,37 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
|
||||
for (var i = 0; i < Sources.Length; i++)
|
||||
{
|
||||
builder.AppendLine(Sources[i]);
|
||||
var input = Sources[i];
|
||||
builder.AppendLine("-s");
|
||||
builder.AppendLine(input.GetMetadata(FullPath));
|
||||
|
||||
builder.AppendLine("-r");
|
||||
builder.AppendLine(input.GetMetadata(TargetPath));
|
||||
|
||||
builder.AppendLine("-o");
|
||||
var outputPath = Path.Combine(ProjectRoot, input.GetMetadata(GeneratedOutput));
|
||||
builder.AppendLine(outputPath);
|
||||
}
|
||||
|
||||
builder.AppendLine("-p");
|
||||
builder.AppendLine(ProjectRoot);
|
||||
|
||||
builder.AppendLine("-o");
|
||||
builder.AppendLine(OutputPath);
|
||||
|
||||
builder.AppendLine("-t");
|
||||
builder.AppendLine(TagHelperManifest);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private bool EnsureRequiredMetadata(ITaskItem item, string metadataName)
|
||||
{
|
||||
var value = item.GetMetadata(metadataName);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Log.LogError($"Missing required metadata '{metadataName}' for '{item.ItemSpec}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class CompositeRazorProjectFileSystem : RazorProjectFileSystem
|
||||
{
|
||||
public CompositeRazorProjectFileSystem(IReadOnlyList<RazorProjectFileSystem> projects)
|
||||
{
|
||||
Projects = projects ?? throw new ArgumentNullException(nameof(projects));
|
||||
}
|
||||
|
||||
public IReadOnlyList<RazorProjectFileSystem> Projects { get; }
|
||||
|
||||
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
|
||||
{
|
||||
foreach (var project in Projects)
|
||||
{
|
||||
foreach (var result in project.EnumerateItems(basePath))
|
||||
{
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override RazorProjectItem GetItem(string path)
|
||||
{
|
||||
RazorProjectItem razorProjectItem = null;
|
||||
foreach (var project in Projects)
|
||||
{
|
||||
razorProjectItem = project.GetItem(path);
|
||||
if (razorProjectItem != null && razorProjectItem.Exists)
|
||||
{
|
||||
return razorProjectItem;
|
||||
}
|
||||
}
|
||||
|
||||
return razorProjectItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
{
|
||||
args = args.Skip(1).ToArray();
|
||||
|
||||
Console.WriteLine("Waiting for debugger in pid: {0}", Process.GetCurrentProcess().Id);
|
||||
while (!Debugger.IsAttached)
|
||||
{
|
||||
Console.WriteLine("Waiting for debugger in pid: {0}", Process.GetCurrentProcess().Id);
|
||||
Thread.Sleep(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,30 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class Builder<T>
|
||||
{
|
||||
public static Builder<T> Make(CommandBase result) => null;
|
||||
|
||||
public static Builder<T> Make(T result) => null;
|
||||
}
|
||||
|
||||
internal class GenerateCommand : CommandBase
|
||||
{
|
||||
public GenerateCommand(Application parent)
|
||||
: base(parent, "generate")
|
||||
{
|
||||
Sources = Argument("sources", ".cshtml files to compile", multipleValues: true);
|
||||
Sources = Option("-s", ".cshtml files to compile", CommandOptionType.MultipleValue);
|
||||
Outputs = Option("-o", "Generated output file path", CommandOptionType.MultipleValue);
|
||||
RelativePaths = Option("-r", "Relative path", CommandOptionType.MultipleValue);
|
||||
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
||||
OutputDirectory = Option("-o", "output directory", CommandOptionType.SingleValue);
|
||||
TagHelperManifest = Option("-t", "tag helper manifest file", CommandOptionType.SingleValue);
|
||||
}
|
||||
|
||||
public CommandArgument Sources { get; }
|
||||
public CommandOption Sources { get; }
|
||||
|
||||
public CommandOption OutputDirectory { get; }
|
||||
public CommandOption Outputs { get; }
|
||||
|
||||
public CommandOption RelativePaths { get; }
|
||||
|
||||
public CommandOption ProjectDirectory { get; }
|
||||
|
||||
|
|
@ -36,25 +46,30 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
{
|
||||
var result = ExecuteCore(
|
||||
projectDirectory: ProjectDirectory.Value(),
|
||||
outputDirectory: OutputDirectory.Value(),
|
||||
tagHelperManifest: TagHelperManifest.Value(),
|
||||
sources: Sources.Values.ToArray());
|
||||
sources: Sources.Values,
|
||||
outputs: Outputs.Values,
|
||||
relativePaths: RelativePaths.Values);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
protected override bool ValidateArguments()
|
||||
{
|
||||
if (string.IsNullOrEmpty(OutputDirectory.Value()))
|
||||
if (Sources.Values.Count == 0)
|
||||
{
|
||||
Error.WriteLine($"{OutputDirectory.ValueName} not specified.");
|
||||
Error.WriteLine($"{Sources.ValueName} should have at least one value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Sources.Values.Count == 0)
|
||||
if (Outputs.Values.Count != Sources.Values.Count)
|
||||
{
|
||||
Error.WriteLine($"{Sources.Name} should have at least one value.");
|
||||
return false;
|
||||
Error.WriteLine($"{Sources.ValueName} has {Sources.Values.Count}, but {Outputs.ValueName} has {Outputs.Values.Count}.");
|
||||
}
|
||||
|
||||
if (RelativePaths.Values.Count != Sources.Values.Count)
|
||||
{
|
||||
Error.WriteLine($"{Sources.ValueName} has {Sources.Values.Count}, but {RelativePaths.ValueName} has {RelativePaths.Values.Count}.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ProjectDirectory.Value()))
|
||||
|
|
@ -65,10 +80,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return true;
|
||||
}
|
||||
|
||||
private int ExecuteCore(string projectDirectory, string outputDirectory, string tagHelperManifest, string[] sources)
|
||||
private int ExecuteCore(
|
||||
string projectDirectory,
|
||||
string tagHelperManifest,
|
||||
List<string> sources,
|
||||
List<string> outputs,
|
||||
List<string> relativePaths)
|
||||
{
|
||||
tagHelperManifest = Path.Combine(projectDirectory, tagHelperManifest);
|
||||
outputDirectory = Path.Combine(projectDirectory, outputDirectory);
|
||||
|
||||
var tagHelpers = GetTagHelpers(tagHelperManifest);
|
||||
|
||||
|
|
@ -79,10 +98,18 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
b.Features.Add(new StaticTagHelperFeature() { TagHelpers = tagHelpers, });
|
||||
});
|
||||
|
||||
var templateEngine = new MvcRazorTemplateEngine(engine, RazorProject.Create(projectDirectory));
|
||||
|
||||
var sourceItems = GetRazorFiles(projectDirectory, sources);
|
||||
var results = GenerateCode(templateEngine, sourceItems);
|
||||
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;
|
||||
|
||||
|
|
@ -97,16 +124,30 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
}
|
||||
|
||||
var viewFile = result.ViewFileInfo.ViewEnginePath.Substring(1);
|
||||
var outputFileName = Path.ChangeExtension(viewFile, ".cs");
|
||||
|
||||
var outputFilePath = Path.Combine(outputDirectory, outputFileName);
|
||||
var outputFilePath = result.InputItem.OutputPath;
|
||||
File.WriteAllText(outputFilePath, result.CSharpDocument.GeneratedCode);
|
||||
}
|
||||
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
||||
private VirtualRazorProjectFileSystem GetVirtualRazorProjectSystem(SourceItem[] inputItems)
|
||||
{
|
||||
var project = new VirtualRazorProjectFileSystem();
|
||||
foreach (var item in inputItems)
|
||||
{
|
||||
var projectItem = new FileSystemRazorProjectItem(
|
||||
basePath: "/",
|
||||
filePath: item.FilePath,
|
||||
relativePhysicalPath: item.RelativePhysicalPath,
|
||||
file: new FileInfo(item.SourcePath));
|
||||
|
||||
project.Add(projectItem);
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
private IReadOnlyList<TagHelperDescriptor> GetTagHelpers(string tagHelperManifest)
|
||||
{
|
||||
if (!File.Exists(tagHelperManifest))
|
||||
|
|
@ -127,33 +168,27 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
}
|
||||
|
||||
private List<SourceItem> GetRazorFiles(string projectDirectory, string[] sources)
|
||||
private SourceItem[] GetInputItems(string projectDirectory, List<string> sources, List<string> outputs, List<string> relativePath)
|
||||
{
|
||||
var trimLength = projectDirectory.EndsWith("/") ? projectDirectory.Length - 1 : projectDirectory.Length;
|
||||
|
||||
var items = new List<SourceItem>(sources.Length);
|
||||
for (var i = 0; i < sources.Length; i++)
|
||||
var items = new SourceItem[sources.Count];
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
var fullPath = Path.Combine(projectDirectory, sources[i]);
|
||||
if (fullPath.StartsWith(projectDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var viewEnginePath = fullPath.Substring(trimLength).Replace('\\', '/');
|
||||
items.Add(new SourceItem(fullPath, viewEnginePath));
|
||||
}
|
||||
var outputPath = Path.Combine(projectDirectory, outputs[i]);
|
||||
items[i] = new SourceItem(sources[i], outputs[i], relativePath[i]);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private OutputItem[] GenerateCode(RazorTemplateEngine templateEngine, IReadOnlyList<SourceItem> sources)
|
||||
private OutputItem[] GenerateCode(RazorTemplateEngine templateEngine, SourceItem[] inputs)
|
||||
{
|
||||
var outputs = new OutputItem[sources.Count];
|
||||
var outputs = new OutputItem[inputs.Length];
|
||||
Parallel.For(0, outputs.Length, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, i =>
|
||||
{
|
||||
var source = sources[i];
|
||||
var inputItem = inputs[i];
|
||||
|
||||
var csharpDocument = templateEngine.GenerateCode(source.ViewEnginePath);
|
||||
outputs[i] = new OutputItem(source, csharpDocument);
|
||||
var csharpDocument = templateEngine.GenerateCode(inputItem.FilePath);
|
||||
outputs[i] = new OutputItem(inputItem, csharpDocument);
|
||||
});
|
||||
|
||||
return outputs;
|
||||
|
|
@ -162,43 +197,37 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
private struct OutputItem
|
||||
{
|
||||
public OutputItem(
|
||||
SourceItem viewFileInfo,
|
||||
SourceItem inputItem,
|
||||
RazorCSharpDocument cSharpDocument)
|
||||
{
|
||||
ViewFileInfo = viewFileInfo;
|
||||
InputItem = inputItem;
|
||||
CSharpDocument = cSharpDocument;
|
||||
}
|
||||
|
||||
public SourceItem ViewFileInfo { get; }
|
||||
public SourceItem InputItem { get; }
|
||||
|
||||
public RazorCSharpDocument CSharpDocument { get; }
|
||||
}
|
||||
|
||||
private struct SourceItem
|
||||
{
|
||||
public SourceItem(string fullPath, string viewEnginePath)
|
||||
public SourceItem(string sourcePath, string outputPath, string physicalRelativePath)
|
||||
{
|
||||
FullPath = fullPath;
|
||||
ViewEnginePath = viewEnginePath;
|
||||
SourcePath = sourcePath;
|
||||
OutputPath = outputPath;
|
||||
RelativePhysicalPath = physicalRelativePath;
|
||||
FilePath = '/' + physicalRelativePath
|
||||
.Replace(Path.DirectorySeparatorChar, '/')
|
||||
.Replace("//", "/");
|
||||
}
|
||||
|
||||
public string FullPath { get; }
|
||||
public string SourcePath { get; }
|
||||
|
||||
public string ViewEnginePath { get; }
|
||||
public string OutputPath { get; }
|
||||
|
||||
public Stream CreateReadStream()
|
||||
{
|
||||
// We are setting buffer size to 1 to prevent FileStream from allocating it's internal buffer
|
||||
// 0 causes constructor to throw
|
||||
var bufferSize = 1;
|
||||
return new FileStream(
|
||||
FullPath,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite,
|
||||
bufferSize,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
}
|
||||
public string RelativePhysicalPath { get; }
|
||||
|
||||
public string FilePath { get; }
|
||||
}
|
||||
|
||||
private class StaticTagHelperFeature : ITagHelperFeature
|
||||
|
|
@ -210,4 +239,4 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
public IReadOnlyList<TagHelperDescriptor> GetDescriptors() => TagHelpers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<PropertyGroup>
|
||||
<PrepareForRazorGenerateDependsOn>
|
||||
ResolveRazorGenerateInputs;
|
||||
AssignRazorGenerateTargetPaths;
|
||||
ResolveAssemblyReferenceRazorGenerateInputs;
|
||||
_EnsureRazorCompilerReferenced;
|
||||
ResolveTagHelperRazorGenerateInputs
|
||||
|
|
@ -48,6 +49,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
</PrepareForRazorCompileDependsOn>
|
||||
|
||||
<ResolveRazorCompileInputsDependsOn>
|
||||
ResolveRazorEmbeddedResources
|
||||
</ResolveRazorCompileInputsDependsOn>
|
||||
|
||||
<RazorCompileDependsOn>
|
||||
|
|
@ -221,11 +223,17 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<ItemGroup Condition="'$(EnableDefaultRazorGenerateItems)'=='true'">
|
||||
<RazorGenerate Include="@(Content)" Condition="'%(Content.Extension)'=='.cshtml'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="AssignRazorGenerateTargetPaths" Condition="'@(RazorGenerate)' != ''">
|
||||
<AssignTargetPath Files="@(RazorGenerate)" RootFolder="$(MSBuildProjectDirectory)">
|
||||
<Output TaskParameter="AssignedFiles" ItemName="RazorGenerateWithTargetPath" />
|
||||
</AssignTargetPath>
|
||||
|
||||
<ItemGroup>
|
||||
<RazorGenerate Update="@(RazorGenerate)" Condtion="'%(RazorGenerate.GeneratedOutput)'==''">
|
||||
<GeneratedOutput>$(RazorGenerateIntermediateOutputPath)%(RelativeDir)%(Filename).cs</GeneratedOutput>
|
||||
</RazorGenerate>
|
||||
<RazorGenerateWithTargetPath Condition="'%(RazorGenerateWithTargetPath.GeneratedOutput)' == ''">
|
||||
<GeneratedOutput>$(RazorGenerateIntermediateOutputPath)$([System.IO.Path]::ChangeExtension('%(RazorGenerateWithTargetPath.TargetPath)', '.cs'))</GeneratedOutput>
|
||||
</RazorGenerateWithTargetPath>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
|
@ -249,6 +257,21 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<Target Name="ResolveRazorCompileInputs" DependsOnTargets="$(ResolveRazorCompileInputsDependsOn)">
|
||||
</Target>
|
||||
|
||||
<Target Name="ResolveRazorEmbeddedResources" Condition="'$(EmbedRazorGenerateSources)'=='true'">
|
||||
<ItemGroup>
|
||||
<RazorEmbeddedResource Include="@(RazorGenerateWithTargetPath)">
|
||||
<LogicalName>/$([System.String]::Copy('%(RazorGenerateWithTargetPath.TargetPath)').Replace('\','/'))</LogicalName>
|
||||
<Type>Non-Resx</Type>
|
||||
<WithCulture>false</WithCulture>
|
||||
</RazorEmbeddedResource>
|
||||
|
||||
<!-- Similar to _GenerateCompileInputs -->
|
||||
<_RazorCoreCompileResourceInputs
|
||||
Include="@(RazorEmbeddedResource)"
|
||||
Condition="'%(RazorEmbeddedResource.WithCulture)'=='false' and '%(RazorEmbeddedResource.Type)'=='Non-Resx' " />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
This target is called after PrepareForPublish when RazorCompileOnPublish=true so that we can hook into publish.
|
||||
This target just hooks up other targets since Publish and PrepareForPublish don't have a DependsOnTargets
|
||||
|
|
|
|||
|
|
@ -314,7 +314,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private class BuildFailedException : MSBuildXunitException
|
||||
{
|
||||
public BuildFailedException(MSBuildResult result)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.DotNet.PlatformAbstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.dll");
|
||||
Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.pdb");
|
||||
|
||||
if (RuntimeEnvironment.OperatingSystemPlatform != Platform.Darwin)
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
// GetFullPath on OSX doesn't work well in travis. We end up computing a different path than will
|
||||
// end up in the MSBuild logs.
|
||||
|
|
@ -194,5 +194,27 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.FileExists(result, OutputPath, "ClassLibrary.PrecompiledViews.dll");
|
||||
Assert.FileExists(result, OutputPath, "ClassLibrary.PrecompiledViews.pdb");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimplePages", "LinkedDir")]
|
||||
public async Task Build_SetsUpEmbeddedResourcesWithLogicalName()
|
||||
{
|
||||
// Arrange
|
||||
var additionalProjectContent = @"
|
||||
<ItemGroup>
|
||||
<Content Include=""..\LinkedDir\LinkedFile.cshtml"" Link=""LinkedFileOut\LinkedFile.cshtml"" />
|
||||
</ItemGroup>
|
||||
";
|
||||
AddProjectFileContent(additionalProjectContent);
|
||||
Directory.CreateDirectory(Path.Combine(Project.DirectoryPath, "..", "LinkedDir"));
|
||||
|
||||
var result = await DotnetMSBuild("Build", "/t:_IntrospectRazorEmbeddedResources /p:RazorCompileOnBuild=true /p:EmbedRazorGenerateSources=true");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
Assert.BuildOutputContainsLine(result, $@"CompileResource: {Path.Combine("Pages", "Index.cshtml")} /Pages/Index.cshtml");
|
||||
Assert.BuildOutputContainsLine(result, $@"CompileResource: {Path.Combine("Areas", "Products", "Pages", "_ViewStart.cshtml")} /Areas/Products/Pages/_ViewStart.cshtml");
|
||||
Assert.BuildOutputContainsLine(result, $@"CompileResource: {Path.Combine("..", "LinkedDir", "LinkedFile.cshtml")} /LinkedFileOut/LinkedFile.cshtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.PlatformAbstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
{
|
||||
|
|
@ -100,7 +98,7 @@ $@"<Project>
|
|||
</Project>";
|
||||
File.WriteAllText(Path.Combine(projectDestination, "Before.Directory.Build.props"), beforeDirectoryPropsContent);
|
||||
|
||||
new List<string> { "Directory.Build.props", "Directory.Build.targets" }
|
||||
new List<string> { "Directory.Build.props", "Directory.Build.targets", "RazorTest.Introspection.targets" }
|
||||
.ForEach(file =>
|
||||
{
|
||||
var source = Path.Combine(testAppsRoot, file);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Xunit;
|
||||
|
|
@ -196,7 +197,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
|
||||
// We shouldn't need to hash the files
|
||||
Assert.FileDoesNotExist(result, Path.Combine(IntermediateOutputPath, "SimpleMvc.RazorCoreGenerate.cache"));
|
||||
|
||||
|
||||
Assert.FileCountEquals(result, 0, RazorIntermediateOutputPath, "*.cs");
|
||||
}
|
||||
|
||||
|
|
@ -204,14 +205,12 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_MvcRazorFilesToCompile_OverridesDefaultItems()
|
||||
{
|
||||
var content = @"
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<MvcRazorFilesToCompile Include=""Views/Home/About.cshtml"" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
var projectContent = @"
|
||||
<ItemGroup>
|
||||
<MvcRazorFilesToCompile Include=""Views/Home/About.cshtml"" />
|
||||
</ItemGroup>
|
||||
";
|
||||
File.WriteAllText(Path.Combine(Project.DirectoryPath, "After.Directory.Build.props"), content);
|
||||
AddProjectFileContent(projectContent);
|
||||
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
|
|
@ -225,17 +224,15 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_EnableDefaultRazorGenerateItems_False_OverridesDefaultItems()
|
||||
{
|
||||
var content = @"
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<EnableDefaultRazorGenerateItems>false</EnableDefaultRazorGenerateItems>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<RazorGenerate Include=""Views/Home/About.cshtml"" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
var projectContent = @"
|
||||
<PropertyGroup>
|
||||
<EnableDefaultRazorGenerateItems>false</EnableDefaultRazorGenerateItems>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<RazorGenerate Include=""Views/Home/About.cshtml"" />
|
||||
</ItemGroup>
|
||||
";
|
||||
File.WriteAllText(Path.Combine(Project.DirectoryPath, "After.Directory.Build.props"), content);
|
||||
AddProjectFileContent(projectContent);
|
||||
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
|
|
@ -245,9 +242,61 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.FileCountEquals(result, 1, RazorIntermediateOutputPath, "*.cs");
|
||||
}
|
||||
|
||||
[Fact(Skip = "Fails due to #1999")]
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc", "LinkedDir")]
|
||||
public async Task RazorGenerate_WorksWithLinkedFiles()
|
||||
{
|
||||
// Arrange
|
||||
var projectContent = @"
|
||||
<ItemGroup>
|
||||
<Content Include=""..\LinkedDir\LinkedFile.cshtml"" />
|
||||
<Content Include=""..\LinkedDir\LinkedFile2.cshtml"" Link=""LinkedFileOut\LinkedFile2.cshtml"" />
|
||||
<Content Include=""..\LinkedDir\LinkedFile3.cshtml"" Link=""LinkedFileOut\LinkedFileWithRename.cshtml"" />
|
||||
</ItemGroup>
|
||||
";
|
||||
AddProjectFileContent(projectContent);
|
||||
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget, "/t:_IntrospectRazorGenerateWithTargetPath");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "LinkedFile.cs");
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFile2.cs");
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFileWithRename.cs");
|
||||
|
||||
Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {Path.Combine("..", "LinkedDir", "LinkedFile.cshtml")} LinkedFile.cshtml {Path.Combine(RazorIntermediateOutputPath, "LinkedFile.cs")}");
|
||||
Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {Path.Combine("..", "LinkedDir", "LinkedFile2.cshtml")} LinkedFileOut\LinkedFile2.cshtml {Path.Combine(RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFile2.cs")}");
|
||||
Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {Path.Combine("..", "LinkedDir", "LinkedFile3.cshtml")} LinkedFileOut\LinkedFileWithRename.cshtml {Path.Combine(RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFileWithRename.cs")}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc", "LinkedDir")]
|
||||
public async Task RazorGenerate_PrintsErrorsFromLinkedFiles()
|
||||
{
|
||||
// Arrange
|
||||
var file = @"..\LinkedDir\LinkedErrorFile.cshtml";
|
||||
var projectContent = $@"
|
||||
<ItemGroup>
|
||||
<Content Include=""{file}"" Link=""LinkedFileOut\LinkedFile.cshtml"" />
|
||||
</ItemGroup>
|
||||
";
|
||||
AddProjectFileContent(projectContent);
|
||||
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
Assert.BuildFailed(result);
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
// GetFullPath on OSX doesn't work well in travis. We end up computing a different path than will
|
||||
// end up in the MSBuild logs.
|
||||
var errorLocation = Path.GetFullPath(Path.Combine(Project.DirectoryPath, "..", "LinkedDir", "LinkedErrorFile.cshtml")) + "(1,2)";
|
||||
Assert.BuildError(result, "RZ1006", errorLocation);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_FileWithAbsolutePath_IgnoresFile()
|
||||
public async Task RazorGenerate_FileWithAbsolutePath()
|
||||
{
|
||||
// In preview1 we totally ignore files that are specified with an absolute path
|
||||
var filePath = Path.Combine(Project.SolutionPath, "temp.cshtml");
|
||||
|
|
@ -258,7 +307,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
<Content Include=""{filePath}""/>
|
||||
</ItemGroup>");
|
||||
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget, "/t:_IntrospectRazorGenerateWithTargetPath");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
|
|
@ -274,7 +323,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Shared", "_Layout.cs");
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Shared", "_ValidationScriptsPartial.cs");
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Shared", "Error.cs");
|
||||
Assert.FileCountEquals(result, 8, RazorIntermediateOutputPath, "*.cs");
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "temp.cs");
|
||||
Assert.FileCountEquals(result, 9, RazorIntermediateOutputPath, "*.cs");
|
||||
Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {filePath} temp.cshtml {Path.Combine(RazorIntermediateOutputPath, "temp.cs")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,400 @@
|
|||
// 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 Xunit;
|
||||
using DirectoryNode = Microsoft.AspNetCore.Razor.Language.VirtualRazorProjectFileSystem.DirectoryNode;
|
||||
using FileNode = Microsoft.AspNetCore.Razor.Language.VirtualRazorProjectFileSystem.FileNode;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class VirtualRazorProjectFileSystemTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetItem_ReturnsNotFound_IfFileDoesNotExistInRoot()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/root-file.cshtml";
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
|
||||
// Act
|
||||
projectSystem.Add(new TestRazorProjectItem("/different-file.cshtml"));
|
||||
var result = projectSystem.GetItem(path);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsItemAddedToRoot()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/root-file.cshtml";
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var projectItem = new TestRazorProjectItem(path);
|
||||
|
||||
// Act
|
||||
projectSystem.Add(projectItem);
|
||||
var actual = projectSystem.GetItem(path);
|
||||
|
||||
// Assert
|
||||
Assert.Same(projectItem, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/subDirectory/file.cshtml")]
|
||||
[InlineData("/subDirectory/dir2/file.cshtml")]
|
||||
[InlineData("/subDirectory/dir2/dir3/file.cshtml")]
|
||||
public void GetItem_ReturnsItemAddedToNestedDirectory(string path)
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var projectItem = new TestRazorProjectItem(path);
|
||||
|
||||
// Act
|
||||
projectSystem.Add(projectItem);
|
||||
var actual = projectSystem.GetItem(path);
|
||||
|
||||
// Assert
|
||||
Assert.Same(projectItem, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsNotFound_WhenNestedDirectoryDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
|
||||
// Act
|
||||
var actual = projectSystem.GetItem("/subDirectory/dir3/file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.False(actual.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsNotFound_WhenNestedDirectoryDoesNotExist_AndPeerDirectoryExists()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var projectItem = new TestRazorProjectItem("/subDirectory/dir2/file.cshtml");
|
||||
|
||||
// Act
|
||||
projectSystem.Add(projectItem);
|
||||
var actual = projectSystem.GetItem("/subDirectory/dir3/file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.False(actual.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsNotFound_WhenFileDoesNotExistInNestedDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var projectItem = new TestRazorProjectItem("/subDirectory/dir2/file.cshtml");
|
||||
|
||||
// Act
|
||||
projectSystem.Add(projectItem);
|
||||
var actual = projectSystem.GetItem("/subDirectory/dir2/file2.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.False(actual.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_AtRoot_ReturnsAllFiles()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var file1 = new TestRazorProjectItem("/subDirectory/dir2/file1.cshtml");
|
||||
var file2 = new TestRazorProjectItem("/file2.cshtml");
|
||||
var file3 = new TestRazorProjectItem("/dir3/file3.cshtml");
|
||||
var file4 = new TestRazorProjectItem("/subDirectory/file4.cshtml");
|
||||
projectSystem.Add(file1);
|
||||
projectSystem.Add(file2);
|
||||
projectSystem.Add(file3);
|
||||
projectSystem.Add(file4);
|
||||
|
||||
// Act
|
||||
var result = projectSystem.EnumerateItems("/");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { file2, file4, file1, file3 }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_AtSubDirectory_ReturnsAllFilesUnderDirectoryHierarchy()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var file1 = new TestRazorProjectItem("/subDirectory/dir2/file1.cshtml");
|
||||
var file2 = new TestRazorProjectItem("/file2.cshtml");
|
||||
var file3 = new TestRazorProjectItem("/dir3/file3.cshtml");
|
||||
var file4 = new TestRazorProjectItem("/subDirectory/file4.cshtml");
|
||||
projectSystem.Add(file1);
|
||||
projectSystem.Add(file2);
|
||||
projectSystem.Add(file3);
|
||||
projectSystem.Add(file4);
|
||||
|
||||
// Act
|
||||
var result = projectSystem.EnumerateItems("/subDirectory");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { file4, file1 }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_WithNoFilesInRoot_ReturnsEmptySequence()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
|
||||
// Act
|
||||
var result = projectSystem.EnumerateItems("/");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_ForNonExistentDirectory_ReturnsEmptySequence()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
projectSystem.Add(new TestRazorProjectItem("/subDirectory/dir2/file1.cshtml"));
|
||||
projectSystem.Add(new TestRazorProjectItem("/file2.cshtml"));
|
||||
|
||||
// Act
|
||||
var result = projectSystem.EnumerateItems("/dir3");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetHierarchicalItems_Works()
|
||||
{
|
||||
// Arrange
|
||||
var projectSystem = new VirtualRazorProjectFileSystem();
|
||||
var viewImport1 = new TestRazorProjectItem("/_ViewImports.cshtml");
|
||||
var viewImport2 = new TestRazorProjectItem("/Views/Home/_ViewImports.cshtml");
|
||||
projectSystem.Add(viewImport1);
|
||||
projectSystem.Add(viewImport2);
|
||||
|
||||
// Act
|
||||
var items = projectSystem.FindHierarchicalItems("/", "/Views/Home/Index.cshtml", "_ViewImports.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
items,
|
||||
item => Assert.Same(viewImport2, item),
|
||||
item => Assert.False(item.Exists),
|
||||
item => Assert.Same(viewImport1, item));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_GetDirectory_ReturnsRoot()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
|
||||
// Act
|
||||
var result = root.GetDirectory("/");
|
||||
|
||||
// Assert
|
||||
Assert.Same(root, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_GetDirectory_ReturnsNull_IfDirectoryDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
|
||||
// Act
|
||||
var result = root.GetDirectory("/does-not/exist");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_AddFile_CanAddToRoot()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
var projectItem = new TestRazorProjectItem("/File.txt");
|
||||
|
||||
// Act
|
||||
root.AddFile(new FileNode("/File.txt", projectItem));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(root.Directories);
|
||||
Assert.Collection(
|
||||
root.Files,
|
||||
file => Assert.Same(projectItem, file.ProjectItem));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_AddFile_CanAddToNestedDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
var projectItem = new TestRazorProjectItem("/Pages/Shared/_Layout.cshtml");
|
||||
|
||||
// Act
|
||||
root.AddFile(new FileNode("/Pages/Shared/_Layout.cshtml", projectItem));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
root.Directories,
|
||||
directory =>
|
||||
{
|
||||
Assert.Equal("/Pages/", directory.Path);
|
||||
Assert.Empty(directory.Files);
|
||||
|
||||
Assert.Collection(
|
||||
directory.Directories,
|
||||
subDirectory =>
|
||||
{
|
||||
Assert.Equal("/Pages/Shared/", subDirectory.Path);
|
||||
Assert.Collection(
|
||||
subDirectory.Files,
|
||||
file => Assert.Same(projectItem, file.ProjectItem));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_AddMultipleFiles_ToSameDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
var projectItem1 = new TestRazorProjectItem("/Pages/Shared/_Layout.cshtml");
|
||||
var projectItem2 = new TestRazorProjectItem("/Pages/Shared/_Partial.cshtml");
|
||||
|
||||
// Act
|
||||
root.AddFile(new FileNode(projectItem1.FilePath, projectItem1));
|
||||
root.AddFile(new FileNode(projectItem2.FilePath, projectItem2));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
root.Directories,
|
||||
directory =>
|
||||
{
|
||||
Assert.Equal("/Pages/", directory.Path);
|
||||
Assert.Empty(directory.Files);
|
||||
|
||||
Assert.Collection(
|
||||
directory.Directories,
|
||||
subDirectory =>
|
||||
{
|
||||
Assert.Equal("/Pages/Shared/", subDirectory.Path);
|
||||
Assert.Collection(
|
||||
subDirectory.Files,
|
||||
file => Assert.Same(projectItem1, file.ProjectItem),
|
||||
file => Assert.Same(projectItem2, file.ProjectItem));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_AddsFiles_ToSiblingDirectories()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
var projectItem1 = new TestRazorProjectItem("/Pages/Products/Index.cshtml");
|
||||
var projectItem2 = new TestRazorProjectItem("/Pages/Accounts/About.cshtml");
|
||||
|
||||
// Act
|
||||
root.AddFile(new FileNode(projectItem1.FilePath, projectItem1));
|
||||
root.AddFile(new FileNode(projectItem2.FilePath, projectItem2));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
root.Directories,
|
||||
directory =>
|
||||
{
|
||||
Assert.Equal("/Pages/", directory.Path);
|
||||
Assert.Empty(directory.Files);
|
||||
|
||||
Assert.Collection(
|
||||
directory.Directories,
|
||||
subDirectory =>
|
||||
{
|
||||
Assert.Equal("/Pages/Products/", subDirectory.Path);
|
||||
Assert.Collection(
|
||||
subDirectory.Files,
|
||||
file => Assert.Same(projectItem1, file.ProjectItem));
|
||||
},
|
||||
subDirectory =>
|
||||
{
|
||||
Assert.Equal("/Pages/Accounts/", subDirectory.Path);
|
||||
Assert.Collection(
|
||||
subDirectory.Files,
|
||||
file => Assert.Same(projectItem2, file.ProjectItem));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_GetItem_ReturnsItemAtRoot()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
var projectItem = new TestRazorProjectItem("/_ViewStart.cshtml");
|
||||
root.AddFile(new FileNode(projectItem.FilePath, projectItem));
|
||||
|
||||
// Act
|
||||
var result = root.GetItem(projectItem.FilePath);
|
||||
|
||||
// Assert
|
||||
Assert.Same(result, projectItem);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_GetItem_WhenFilePathSharesSameNameAsSiblingDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var root = new DirectoryNode("/");
|
||||
var projectItem1 = new TestRazorProjectItem("/Home.cshtml");
|
||||
var projectItem2 = new TestRazorProjectItem("/Home/About.cshtml");
|
||||
root.AddFile(new FileNode(projectItem1.FilePath, projectItem1));
|
||||
root.AddFile(new FileNode(projectItem2.FilePath, projectItem2));
|
||||
|
||||
// Act
|
||||
var result = root.GetItem(projectItem1.FilePath);
|
||||
|
||||
// Assert
|
||||
Assert.Same(result, projectItem1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryNode_GetItem_WhenFileNameIsSameAsDirectoryName()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem1 = new TestRazorProjectItem("/Home/Home.cshtml");
|
||||
var projectItem2 = new TestRazorProjectItem("/Home/About.cshtml");
|
||||
var root = new DirectoryNode("/")
|
||||
{
|
||||
Directories =
|
||||
{
|
||||
new DirectoryNode("/Home/")
|
||||
{
|
||||
Files =
|
||||
{
|
||||
new FileNode(projectItem1.FilePath, projectItem1),
|
||||
new FileNode(projectItem2.FilePath, projectItem2),
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = root.GetItem(projectItem1.FilePath);
|
||||
|
||||
// Assert
|
||||
Assert.Same(result, projectItem1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public override string RelativePhysicalPath { get; }
|
||||
|
||||
public override bool Exists => true;
|
||||
public override bool Exists { get; } = true;
|
||||
|
||||
public string Content { get; set; } = "Default content";
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
public class CompositeRazorProjectFileSystemTest
|
||||
{
|
||||
[Fact]
|
||||
public void EnumerateItems_ReturnsResultsFromAllFileSystems()
|
||||
{
|
||||
// Arrange
|
||||
var basePath = "base-path";
|
||||
var file1 = new TestRazorProjectItem("file1");
|
||||
var file2 = new TestRazorProjectItem("file2");
|
||||
var file3 = new TestRazorProjectItem("file3");
|
||||
var fileSystem1 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.EnumerateItems(basePath) == new[] { file1 });
|
||||
var fileSystem2 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.EnumerateItems(basePath) == Enumerable.Empty<RazorProjectItem>());
|
||||
var fileSystem3 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.EnumerateItems(basePath) == new[] { file2, file3, });
|
||||
|
||||
var compositeRazorProjectFileSystem = new CompositeRazorProjectFileSystem(new[] { fileSystem1, fileSystem2, fileSystem3 });
|
||||
|
||||
// Act
|
||||
var result = compositeRazorProjectFileSystem.EnumerateItems(basePath);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { file1, file2, file3 }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_ReturnsEmptySequence_IfNoFileSystemReturnsResults()
|
||||
{
|
||||
// Arrange
|
||||
var basePath = "base-path";
|
||||
var fileSystem1 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.EnumerateItems(basePath) == Enumerable.Empty<RazorProjectItem>());
|
||||
var fileSystem2 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.EnumerateItems(basePath) == Enumerable.Empty<RazorProjectItem>());
|
||||
|
||||
var compositeRazorProjectFileSystem = new CompositeRazorProjectFileSystem(new[] { fileSystem1, fileSystem2 });
|
||||
|
||||
// Act
|
||||
var result = compositeRazorProjectFileSystem.EnumerateItems(basePath);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsFirstInstanceThatExists()
|
||||
{
|
||||
// Arrange
|
||||
var basePath = "base-path";
|
||||
var filePath = "file-path";
|
||||
var file1 = new NotFoundProjectItem(basePath, filePath);
|
||||
var file2 = new TestRazorProjectItem(filePath);
|
||||
RazorProjectItem nullItem = null;
|
||||
var fileSystem1 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.GetItem(filePath) == file1);
|
||||
var fileSystem2 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.GetItem(filePath) == nullItem);
|
||||
var fileSystem3 = Mock.Of<RazorProjectFileSystem>(
|
||||
f => f.GetItem(filePath) == file2);
|
||||
|
||||
var compositeRazorProjectFileSystem = new CompositeRazorProjectFileSystem(new[] { fileSystem1, fileSystem2, fileSystem3 });
|
||||
|
||||
// Act
|
||||
var result = compositeRazorProjectFileSystem.GetItem(filePath);
|
||||
|
||||
// Assert
|
||||
Assert.Same(file2, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Test.Common\Microsoft.AspNetCore.Razor.Test.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="RazorTest.Introspection.targets" />
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
@(
|
||||
|
|
@ -0,0 +1 @@
|
|||
@DateTime.UtcNow
|
||||
|
|
@ -0,0 +1 @@
|
|||
@DateTime.UtcNow
|
||||
|
|
@ -0,0 +1 @@
|
|||
@DateTime.UtcNow
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<Project>
|
||||
<Target Name="_IntrospectRazorGenerateWithTargetPath">
|
||||
<Message Text="RazorGenerateWithTargetPath: %(RazorGenerateWithTargetPath.Identity) %(RazorGenerateWithTargetPath.TargetPath) %(RazorGenerateWithTargetPath.GeneratedOutput)" Importance="High" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_IntrospectRazorEmbeddedResources">
|
||||
<Message Text="CompileResource: %(_RazorCoreCompileResourceInputs.Identity) %(_RazorCoreCompileResourceInputs.LogicalName)" Importance="High" />
|
||||
</Target>
|
||||
</Project>
|
||||
Loading…
Reference in New Issue