/// <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));
 }
Esempio n. 2
0
 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);
        }
Esempio n. 6
0
 public abstract void Process(RazorCodeDocument document);
 protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
 {
     return(true);
 }
 protected abstract bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode);
Esempio n. 9
0
 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);
        }