/// <summary> /// Parses the template specified by <paramref name="projectItem" />. /// </summary> /// <param name="projectItem">The <see cref="T:Microsoft.AspNetCore.Razor.Language.RazorProjectItem" />.</param> /// <returns>The <see cref="T:Microsoft.AspNetCore.Razor.Language.RazorCSharpDocument" />.</returns> // Token: 0x06000465 RID: 1125 RVA: 0x00009E14 File Offset: 0x00008014 public Microsoft.AspNetCore.Razor.Language.RazorCSharpDocument GenerateCode(Microsoft.AspNetCore.Razor.Language.RazorProjectItem projectItem) { if (projectItem == null) { throw new ArgumentNullException("projectItem"); } if (!projectItem.Exists) { throw new InvalidOperationException($"FormatRazorTemplateEngine_ItemCouldNotBeFound {projectItem.FilePath}"); } Microsoft.AspNetCore.Razor.Language.RazorCodeDocument codeDocument = this.CreateCodeDocument(projectItem); return(this.GenerateCode(codeDocument)); }
protected abstract void ProcessCore(RazorCodeDocument codeDocument);
/// <summary> /// Parses the template specified by <paramref name="codeDocument" />. /// </summary> /// <param name="codeDocument">The <see cref="T:Microsoft.AspNetCore.Razor.Language.RazorProjectItem" />.</param> /// <returns>The <see cref="T:Microsoft.AspNetCore.Razor.Language.RazorCSharpDocument" />.</returns> // Token: 0x06000466 RID: 1126 RVA: 0x00009E57 File Offset: 0x00008057 public virtual Microsoft.AspNetCore.Razor.Language.RazorCSharpDocument GenerateCode(Microsoft.AspNetCore.Razor.Language.RazorCodeDocument codeDocument) { if (codeDocument == null) { throw new ArgumentNullException("codeDocument"); } this.Engine.Process(codeDocument); return(codeDocument.GetCSharpDocument()); }
protected override void ExecuteCore(RazorCodeDocument codeDocument) { var syntaxTree = codeDocument.GetSyntaxTree(); ThrowForMissingDocumentDependency(syntaxTree); var feature = Engine.Features.OfType <ITagHelperFeature>().FirstOrDefault(); if (feature == null) { // No feature, nothing to do. return; } // We need to find directives in all of the *imports* as well as in the main razor file // // The imports come logically before the main razor file and are in the order they // should be processed. var descriptors = feature.GetDescriptors(); var visitor = new DirectiveVisitor(descriptors); var imports = codeDocument.GetImportSyntaxTrees(); if (imports != null) { for (var i = 0; i < imports.Count; i++) { var import = imports[i]; visitor.VisitBlock(import.Root); } } visitor.VisitBlock(syntaxTree.Root); var tagHelperPrefix = visitor.TagHelperPrefix; descriptors = visitor.Matches.ToArray(); var context = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors); codeDocument.SetTagHelperContext(context); if (descriptors.Count == 0) { // No descriptors, no-op. return; } var errorSink = new ErrorSink(); var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptors); var root = syntaxTree.Root; root = rewriter.Rewrite(root, errorSink); // Temporary code while we're still using legacy diagnostics in the SyntaxTree. var errorList = new List <RazorDiagnostic>(); errorList.AddRange(errorSink.Errors.Select(error => RazorDiagnostic.Create(error))); errorList.AddRange(descriptors.SelectMany(d => d.GetAllDiagnostics())); var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList); var newSyntaxTree = RazorSyntaxTree.Create(root, syntaxTree.Source, diagnostics, syntaxTree.Options); codeDocument.SetSyntaxTree(newSyntaxTree); }
protected override void ExecuteCore(RazorCodeDocument codeDocument) { var syntaxTree = codeDocument.GetSyntaxTree(); ThrowForMissingDocumentDependency(syntaxTree); var descriptors = codeDocument.GetTagHelpers(); if (descriptors == null) { var feature = Engine.Features.OfType <ITagHelperFeature>().FirstOrDefault(); if (feature == null) { // No feature, nothing to do. return; } descriptors = feature.GetDescriptors(); } // We need to find directives in all of the *imports* as well as in the main razor file // // The imports come logically before the main razor file and are in the order they // should be processed. DirectiveVisitor visitor = null; var parserOptions = codeDocument.GetParserOptions(); if (FileKinds.IsComponent(codeDocument.GetFileKind()) && (parserOptions == null || parserOptions.FeatureFlags.AllowComponentFileKind)) { codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var currentNamespace); visitor = new ComponentDirectiveVisitor(codeDocument.Source.FilePath, descriptors, currentNamespace); } else { visitor = new TagHelperDirectiveVisitor(descriptors); } var imports = codeDocument.GetImportSyntaxTrees(); if (imports != null) { for (var i = 0; i < imports.Count; i++) { var import = imports[i]; visitor.Visit(import); } } visitor.Visit(syntaxTree); // This will always be null for a component document. var tagHelperPrefix = visitor.TagHelperPrefix; descriptors = visitor.Matches.ToArray(); var context = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors); codeDocument.SetTagHelperContext(context); if (descriptors.Count == 0) { // No descriptors, no-op. return; } var rewrittenSyntaxTree = TagHelperParseTreeRewriter.Rewrite(syntaxTree, tagHelperPrefix, descriptors); codeDocument.SetSyntaxTree(rewrittenSyntaxTree); }
public abstract void Process(RazorCodeDocument document);
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { return(true); }
protected abstract bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode);
public void Execute(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { codeDocument.SetInputDocumentKind(InputDocumentKind.Component); }
// In general documents will have a relative path (relative to the project root). // We can only really compute a nice namespace when we know a relative path. // // However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't // set up correctly. public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace) { if (document == null) { throw new ArgumentNullException(nameof(document)); } var filePath = document.Source.FilePath; var relativePath = document.Source.RelativePath; if (filePath == null || relativePath == null || filePath.Length < relativePath.Length) { @namespace = null; return(false); } relativePath = NormalizePath(relativePath); // If the document or it's imports contains a @namespace directive, we want to use that over the root namespace. var baseNamespace = string.Empty; var appendSuffix = true; var lastNamespaceContent = string.Empty; var lastNamespaceLocation = SourceSpan.Undefined; var importSyntaxTrees = document.GetImportSyntaxTrees(); if (importSyntaxTrees != null) { // ImportSyntaxTrees is usually set. Just being defensive. foreach (var importSyntaxTree in importSyntaxTrees) { if (importSyntaxTree != null && NamespaceVisitor.TryGetLastNamespaceDirective(importSyntaxTree, out var importNamespaceContent, out var importNamespaceLocation)) { lastNamespaceContent = importNamespaceContent; lastNamespaceLocation = importNamespaceLocation; } } } var syntaxTree = document.GetSyntaxTree(); if (syntaxTree != null && NamespaceVisitor.TryGetLastNamespaceDirective(syntaxTree, out var namespaceContent, out var namespaceLocation)) { lastNamespaceContent = namespaceContent; lastNamespaceLocation = namespaceLocation; } // If there are multiple @namespace directives in the heirarchy, // we want to pick the closest one to the current document. if (!string.IsNullOrEmpty(lastNamespaceContent)) { baseNamespace = lastNamespaceContent; var directiveLocationDirectory = NormalizeDirectory(lastNamespaceLocation.FilePath); // We're specifically using OrdinalIgnoreCase here because Razor treats all paths as case-insensitive. if (!document.Source.FilePath.StartsWith(directiveLocationDirectory, StringComparison.OrdinalIgnoreCase) || document.Source.FilePath.Length <= directiveLocationDirectory.Length) { // The most relevant directive is not from the directory hierarchy, can't compute a suffix. appendSuffix = false; } else { // We know that the document containing the namespace directive is in the current document's heirarchy. // Let's compute the actual relative path that we'll use to compute the namespace suffix. relativePath = document.Source.FilePath.Substring(directiveLocationDirectory.Length); } } else if (fallbackToRootNamespace) { var options = document.GetCodeGenerationOptions() ?? document.GetDocumentIntermediateNode()?.Options; baseNamespace = options?.RootNamespace; appendSuffix = true; } if (string.IsNullOrEmpty(baseNamespace)) { // There was no valid @namespace directive and we couldn't compute the RootNamespace. @namespace = null; return(false); } var builder = new StringBuilder(); // Sanitize the base namespace, but leave the dots. var segments = baseNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries); builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[0])); for (var i = 1; i < segments.Length; i++) { builder.Append('.'); builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[i])); } if (appendSuffix) { // If we get here, we already have a base namespace and the relative path that should be used as the namespace suffix. segments = relativePath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries); // Skip the last segment because it's the FileName. for (var i = 0; i < segments.Length - 1; i++) { builder.Append('.'); builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[i])); } } @namespace = builder.ToString(); return(true); // We want to normalize the path of the file containing the '@namespace' directive to just the containing // directory with a trailing separator. // // Not using Path.GetDirectoryName here because it doesn't meet these requirements, and we want to handle // both 'view engine' style paths and absolute paths. // // We also don't normalize the separators here. We expect that all documents are using a consistent style of path. // // If we can't normalize the path, we just return null so it will be ignored. string NormalizeDirectory(string path) { char[] Separators = new char[] { '\\', '/' }; if (string.IsNullOrEmpty(path)) { return(null); } var lastSeparator = path.LastIndexOfAny(Separators); if (lastSeparator == -1) { return(null); } // Includes the separator return(path.Substring(0, lastSeparator + 1)); } }
protected override void ExecuteCore(RazorCodeDocument codeDocument) { var syntaxTree = codeDocument.GetSyntaxTree(); ThrowForMissingDocumentDependency(syntaxTree); // This might not have been set if there are no tag helpers. var tagHelperContext = codeDocument.GetTagHelperContext(); var document = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(document); document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions(); IReadOnlyList <UsingReference> importedUsings = Array.Empty <UsingReference>(); // The import documents should be inserted logically before the main document. var imports = codeDocument.GetImportSyntaxTrees(); if (imports != null) { var importsVisitor = new ImportsVisitor(document, builder, syntaxTree.Options.FeatureFlags); for (var j = 0; j < imports.Count; j++) { var import = imports[j]; importsVisitor.FilePath = import.Source.FilePath; importsVisitor.VisitBlock(import.Root); } importedUsings = importsVisitor.Usings; } var tagHelperPrefix = tagHelperContext?.Prefix; var visitor = new MainSourceVisitor(document, builder, tagHelperPrefix, syntaxTree.Options.FeatureFlags) { FilePath = syntaxTree.Source.FilePath, }; visitor.VisitBlock(syntaxTree.Root); // 1. Prioritize non-imported usings over imported ones. // 2. Don't import usings that already exist in primary document. // 3. Allow duplicate usings in primary document (C# warning). var usingReferences = new List <UsingReference>(visitor.Usings); for (var j = importedUsings.Count - 1; j >= 0; j--) { if (!usingReferences.Contains(importedUsings[j])) { usingReferences.Insert(0, importedUsings[j]); } } // In each lowering piece above, namespaces were tracked. We render them here to ensure every // lowering action has a chance to add a source location to a namespace. Ultimately, closest wins. var i = 0; foreach (var reference in usingReferences) { var @using = new UsingDirectiveIntermediateNode() { Content = reference.Namespace, Source = reference.Source, }; builder.Insert(i++, @using); } ImportDirectives(document); // The document should contain all errors that currently exist in the system. This involves // adding the errors from the primary and imported syntax trees. for (i = 0; i < syntaxTree.Diagnostics.Count; i++) { document.Diagnostics.Add(syntaxTree.Diagnostics[i]); } if (imports != null) { for (i = 0; i < imports.Count; i++) { var import = imports[i]; for (var j = 0; j < import.Diagnostics.Count; j++) { document.Diagnostics.Add(import.Diagnostics[j]); } } } codeDocument.SetDocumentIntermediateNode(document); }
// Internal for testing internal string ProcessTagHelperPrefix(List <TagHelperDirectiveDescriptor> directives, RazorCodeDocument codeDocument) { // We only support a single prefix directive. TagHelperDirectiveDescriptor prefixDirective = null; for (var i = 0; i < directives.Count; i++) { if (directives[i].DirectiveType == TagHelperDirectiveType.TagHelperPrefix) { // We only expect to see a single one of these per file, but that's enforced at another level. prefixDirective = directives[i]; } } var prefix = prefixDirective?.DirectiveText; if (prefix != null && !IsValidTagHelperPrefix(prefix, prefixDirective.Location, prefixDirective.Diagnostics)) { prefix = null; } if (!string.IsNullOrEmpty(prefix)) { return(prefixDirective.DirectiveText); } return(null); }