private void EnsureValidPageDirective(RazorCodeDocument codeDocument, PageDirective pageDirective) { Debug.Assert(pageDirective != null); if (pageDirective.DirectiveNode.IsImported()) { pageDirective.DirectiveNode.Diagnostics.Add( RazorExtensionsDiagnosticFactory.CreatePageDirective_CannotBeImported(pageDirective.DirectiveNode.Source.Value)); } else { // The document contains a page directive and it is not imported. // We now want to make sure this page directive exists at the top of the file. // We are going to do that by re-parsing the document until the very first line that is not Razor comment // or whitespace. We then make sure the page directive still exists in the re-parsed IR tree. var leadingDirectiveCodeDocument = RazorCodeDocument.Create(codeDocument.Source); LeadingDirectiveParsingEngine.Engine.Process(leadingDirectiveCodeDocument); var leadingDirectiveDocumentNode = leadingDirectiveCodeDocument.GetDocumentIntermediateNode(); if (!PageDirective.TryGetPageDirective(leadingDirectiveDocumentNode, out var _)) { // The page directive is not the leading directive. Add an error. pageDirective.DirectiveNode.Diagnostics.Add( RazorExtensionsDiagnosticFactory.CreatePageDirective_MustExistAtTheTopOfFile(pageDirective.DirectiveNode.Source.Value)); } } }
public void TryGetPageDirective_ReturnsTrue_IfContentHasDirective() { // Arrange var content = "@page"; var sourceDocument = RazorSourceDocument.Create(content, "file"); var codeDocument = RazorCodeDocument.Create(sourceDocument); var engine = CreateEngine(); var irDocument = CreateIRDocument(engine, codeDocument); // Act var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); // Assert Assert.True(result); Assert.Null(pageDirective.RouteTemplate); }
public void TryGetPageDirective_ReturnsFalse_IfPageDoesNotHaveDirective() { // Arrange var content = "Hello world"; var sourceDocument = RazorSourceDocument.Create(content, "file"); var codeDocument = RazorCodeDocument.Create(sourceDocument); var engine = CreateEngine(); var irDocument = CreateIRDocument(engine, codeDocument); // Act var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); // Assert Assert.False(result); Assert.Null(pageDirective); }
public void TryGetPageDirective_ParsesRouteTemplate() { // Arrange var content = "@page \"some-route-template\""; var sourceDocument = RazorSourceDocument.Create(content, "file"); var codeDocument = RazorCodeDocument.Create(sourceDocument); var engine = CreateEngine(); var irDocument = CreateIRDocument(engine, codeDocument); // Act var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); // Assert Assert.True(result); Assert.Equal("some-route-template", pageDirective.RouteTemplate); }
public void TryGetPageDirective_ReturnsTrue_IfPageIsImported() { // Arrange var content = "Hello world"; var sourceDocument = RazorSourceDocument.Create(content, "file"); var importDocument = RazorSourceDocument.Create("@page", "imports.cshtml"); var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { importDocument }); var engine = CreateEngine(); var irDocument = CreateIRDocument(engine, codeDocument); // Act var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); // Assert Assert.True(result); Assert.Null(pageDirective.RouteTemplate); }
public void TryGetPageDirective_ReturnsTrue_IfPageIsMalformed() { // Arrange var content = "@page \"some-route-template\" Invalid"; var sourceDocument = RazorSourceDocument.Create(content, "file"); var codeDocument = RazorCodeDocument.Create(sourceDocument); var engine = CreateEngine(); var irDocument = CreateIRDocument(engine, codeDocument); // Act var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); // Assert Assert.True(result); Assert.Equal("some-route-template", pageDirective.RouteTemplate); Assert.NotNull(pageDirective.DirectiveNode); }
protected override void OnDocumentStructureCreated( RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method) { base.OnDocumentStructureCreated(codeDocument, @namespace, @class, method); @namespace.Content = "AspNetCore"; @class.BaseType = "global::Microsoft.AspNetCore.Mvc.RazorPages.Page"; var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath; if (string.IsNullOrEmpty(filePath)) { // It's possible for a Razor document to not have a file path. // Eg. When we try to generate code for an in memory document like default imports. var checksum = BytesToString(codeDocument.Source.GetChecksum()); @class.ClassName = $"AspNetCore_{checksum}"; } else { @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); } @class.Modifiers.Clear(); @class.Modifiers.Add("public"); method.MethodName = "ExecuteAsync"; method.Modifiers.Clear(); method.Modifiers.Add("public"); method.Modifiers.Add("async"); method.Modifiers.Add("override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; var document = codeDocument.GetDocumentIntermediateNode(); PageDirective.TryGetPageDirective(document, out var pageDirective); EnsureValidPageDirective(codeDocument, pageDirective); AddRouteTemplateMetadataAttribute(@namespace, @class, pageDirective); }
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { return(PageDirective.TryGetPageDirective(documentNode, out var pageDirective)); }
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { if (documentNode.Options.DesignTime) { return; } var @namespace = documentNode.FindPrimaryNamespace(); if (@namespace == null || string.IsNullOrEmpty(@namespace.Content)) { // No namespace node or it's incomplete. Skip. return; } var @class = documentNode.FindPrimaryClass(); if (@class == null || string.IsNullOrEmpty(@class.ClassName)) { // No class node or it's incomplete. Skip. return; } var generatedTypeName = $"{@namespace.Content}.{@class.ClassName}"; // The MVC attributes require a relative path to be specified so that we can make a view engine path. // We can't use a rooted path because we don't know what the project root is. // // If we can't sanitize the path, we'll just set it to null and let is blow up at runtime - we don't // want to create noise if this code has to run in some unanticipated scenario. var escapedPath = MakeVerbatimStringLiteral(ConvertToViewEnginePath(codeDocument.Source.RelativePath)); string attribute; if (documentNode.DocumentKind == MvcViewDocumentClassifierPass.MvcViewDocumentKind) { attribute = $"[assembly:{RazorViewAttribute}({escapedPath}, typeof({generatedTypeName}))]"; } else if (documentNode.DocumentKind == RazorPageDocumentClassifierPass.RazorPageDocumentKind && PageDirective.TryGetPageDirective(documentNode, out var pageDirective)) { var escapedRoutePrefix = MakeVerbatimStringLiteral(pageDirective.RouteTemplate); attribute = $"[assembly:{RazorPageAttribute}({escapedPath}, typeof({generatedTypeName}), {escapedRoutePrefix})]"; } else { return; } var index = documentNode.Children.IndexOf(@namespace); Debug.Assert(index >= 0); var pageAttribute = new CSharpCodeIntermediateNode(); pageAttribute.Children.Add(new IntermediateToken() { Kind = TokenKind.CSharp, Content = attribute, }); documentNode.Children.Insert(index, pageAttribute); }