| File: Program.cs | Web Access |
| Project: src\aspnetcore\src\Middleware\tools\RazorPageGenerator\RazorPageGenerator.csproj (dotnet-razorpagegenerator) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Extensions; namespace RazorPageGenerator; public class Program { public static int Main(string[] args) { if (args == null || args.Length < 1) { Console.WriteLine("Invalid argument(s)."); Console.WriteLine(@"Usage: dotnet razorpagegenerator <root-namespace-of-views> [directory path [#line path prefix]] Examples: dotnet razorpagegenerator Microsoft.AspNetCore.Diagnostics.RazorViews - process all views in ""Views"" subfolders of the current directory; use filename in #line directives dotnet razorpagegenerator Microsoft.AspNetCore.Diagnostics.RazorViews c:\project - process all views in ""Views"" subfolders of c:\project directory; use filename in #line directives dotnet razorpagegenerator Microsoft.AspNetCore.Diagnostics.RazorViews c:\project ../Views/ - process all views in ""Views"" subfolders of c:\project directory; use ""../Views/{filename}"" in line directives "); return 1; } var rootNamespace = args[0]; var targetProjectDirectory = args.Length > 1 ? args[1] : Directory.GetCurrentDirectory(); var projectEngine = CreateProjectEngine(rootNamespace, targetProjectDirectory); var physicalPathPrefix = args.Length > 2 ? args[2] : string.Empty; var results = MainCore(projectEngine, targetProjectDirectory, physicalPathPrefix); foreach (var result in results) { File.WriteAllText(result.FilePath, result.GeneratedCode); } Console.WriteLine(); Console.WriteLine($"{results.Count} files successfully generated."); Console.WriteLine(); return 0; } public static RazorProjectEngine CreateProjectEngine(string rootNamespace, string targetProjectDirectory, Action<RazorProjectEngineBuilder> configure = null) { var fileSystem = RazorProjectFileSystem.Create(targetProjectDirectory); var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder => { builder .SetNamespace(rootNamespace) .SetBaseType("Microsoft.Extensions.RazorViews.BaseView") .ConfigureClass((document, @class) => { @class.ClassName = Path.GetFileNameWithoutExtension(document.Source.FilePath); @class.Modifiers.Clear(); @class.Modifiers.Add("internal"); }); SectionDirective.Register(builder); builder.Features.Add(new SuppressChecksumOptionsFeature()); builder.Features.Add(new SuppressMetadataAttributesFeature()); if (configure != null) { configure(builder); } builder.AddDefaultImports(@" @using System @using System.Threading.Tasks "); }); return projectEngine; } public static IList<RazorPageGeneratorResult> MainCore( RazorProjectEngine projectEngine, string targetProjectDirectory, string physicalPathPrefix) { var viewDirectories = Directory.EnumerateDirectories(targetProjectDirectory, "Views", SearchOption.AllDirectories); var fileCount = 0; var results = new List<RazorPageGeneratorResult>(); foreach (var viewDir in viewDirectories) { Console.WriteLine(); Console.WriteLine(" Generating code files for views in {0}", viewDir); var viewDirPath = viewDir.Substring(targetProjectDirectory.Length).Replace('\\', '/'); var cshtmlFiles = projectEngine.FileSystem.EnumerateItems(viewDirPath); if (!cshtmlFiles.Any()) { Console.WriteLine(" No .cshtml or .razor files were found."); continue; } foreach (var item in cshtmlFiles) { Console.WriteLine(" Generating code file for view {0}...", item.FileName); results.Add(GenerateCodeFile(projectEngine, item, physicalPathPrefix)); Console.WriteLine(" Done!"); fileCount++; } } return results; } private static RazorPageGeneratorResult GenerateCodeFile( RazorProjectEngine projectEngine, RazorProjectItem projectItem, string physicalPathPrefix) { var projectItemWrapper = new FileSystemRazorProjectItemWrapper(projectItem, physicalPathPrefix); var codeDocument = projectEngine.Process(projectItemWrapper); var cSharpDocument = codeDocument.GetCSharpDocument(); if (cSharpDocument.Diagnostics.Any()) { var diagnostics = string.Join(Environment.NewLine, cSharpDocument.Diagnostics); Console.WriteLine($"One or more parse errors encountered. This will not prevent the generator from continuing: {Environment.NewLine}{diagnostics}."); } var generatedCodeFilePath = Path.ChangeExtension(projectItem.PhysicalPath, ".Designer.cs"); return new RazorPageGeneratorResult { FilePath = generatedCodeFilePath, GeneratedCode = cSharpDocument.GeneratedCode, }; } private sealed class SuppressChecksumOptionsFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature { public int Order { get; set; } public void Configure(RazorCodeGenerationOptionsBuilder options) { ArgumentNullException.ThrowIfNull(options); options.SuppressChecksum = true; } } private sealed class SuppressMetadataAttributesFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature { public int Order { get; set; } public void Configure(RazorCodeGenerationOptionsBuilder options) { ArgumentNullException.ThrowIfNull(options); options.SuppressMetadataAttributes = true; } } private sealed class FileSystemRazorProjectItemWrapper : RazorProjectItem { private readonly RazorProjectItem _source; public FileSystemRazorProjectItemWrapper(RazorProjectItem item, string physicalPathPrefix) { _source = item; // Mask the full name since we don't want a developer's local file paths to be committed. PhysicalPath = $"{physicalPathPrefix}{_source.FileName}"; } public override string BasePath => _source.BasePath; public override string FilePath => _source.FilePath; public override string PhysicalPath { get; } public override bool Exists => _source.Exists; public override Stream Read() { var processedContent = ProcessFileIncludes(); return new MemoryStream(Encoding.UTF8.GetBytes(processedContent)); } private string ProcessFileIncludes() { var basePath = System.IO.Path.GetDirectoryName(_source.PhysicalPath); var cshtmlContent = File.ReadAllText(_source.PhysicalPath); var startMatch = "<%$ include: "; var endMatch = " %>"; var startIndex = 0; while (startIndex < cshtmlContent.Length) { startIndex = cshtmlContent.IndexOf(startMatch, startIndex, StringComparison.Ordinal); if (startIndex == -1) { break; } var endIndex = cshtmlContent.IndexOf(endMatch, startIndex, StringComparison.Ordinal); if (endIndex == -1) { throw new InvalidOperationException($"Invalid include file format in {_source.PhysicalPath}. Usage example: <%$ include: ErrorPage.js %>"); } var includeFileName = cshtmlContent.Substring(startIndex + startMatch.Length, endIndex - (startIndex + startMatch.Length)); Console.WriteLine(" Inlining file {0}", includeFileName); var includeFileContent = File.ReadAllText(System.IO.Path.Combine(basePath, includeFileName)); cshtmlContent = string.Concat(cshtmlContent.AsSpan(0, startIndex), includeFileContent, cshtmlContent.AsSpan(endIndex + endMatch.Length)); startIndex = startIndex + includeFileContent.Length; } return cshtmlContent; } } }