public void WriteDocument_WritesNamespace() { // Arrange var document = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(document); builder.Add(new NamespaceDeclarationIntermediateNode() { Content = "TestNamespace", }); var codeDocument = TestRazorCodeDocument.CreateEmpty(); var options = RazorCodeGenerationOptions.CreateDefault(); var target = CodeTarget.CreateDefault(codeDocument, options); var writer = new DefaultDocumentWriter(target, options); // Act var result = writer.WriteDocument(codeDocument, document); // Assert var csharp = result.GeneratedCode; Assert.Equal( @"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709"" // <auto-generated/> #pragma warning disable 1591 namespace TestNamespace { #line hidden } #pragma warning restore 1591 ", csharp, ignoreLineEndingDifferences: true); }
public void Execute_NoOps_IfClassNameNodeIsMissing() { // Arrange var irDocument = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(irDocument); var @namespace = new NamespaceDeclarationIntermediateNode() { Content = "SomeNamespace" }; builder.Push(@namespace); var pass = new AssemblyAttributeInjectionPass { Engine = RazorEngine.Create(), }; // Act pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument); // Assert Assert.Collection(irDocument.Children, node => Assert.Same(@namespace, node)); }
public void WriteCSharpCode_WhitespaceContent_DoesNothing() { // Arrange var codeWriter = new CodeWriter(); var writer = new RuntimeNodeWriter(); var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpCodeIntermediateNode(); IntermediateNodeBuilder.Create(node) .Add(new IntermediateToken() { Kind = TokenKind.CSharp, Content = " \t" }); // Act writer.WriteCSharpCode(context, node); // Assert var csharp = context.CodeWriter.GenerateCode(); Assert.Empty(csharp); }
public void Execute_HasRequiredInfo_AndImport_AddsItemAndSourceChecksum() { // Arrange var engine = CreateEngine(); var pass = new MetadataAttributePass() { Engine = engine, }; var sourceDocument = TestRazorSourceDocument.Create("", new RazorSourceDocumentProperties(null, "Foo\\Bar.cshtml")); var import = TestRazorSourceDocument.Create("@using System", new RazorSourceDocumentProperties(null, "Foo\\Import.cshtml")); var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { import, }); var irDocument = new DocumentIntermediateNode() { DocumentKind = "test", Options = RazorCodeGenerationOptions.Create((o) => { }), }; var builder = IntermediateNodeBuilder.Create(irDocument); var @namespace = new NamespaceDeclarationIntermediateNode { Annotations = { [CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace, }, Content = "Some.Namespace" }; builder.Push(@namespace); var @class = new ClassDeclarationIntermediateNode { Annotations = { [CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass, }, ClassName = "Test", }; builder.Add(@class); // Act pass.Execute(codeDocument, irDocument); // Assert Assert.Equal(2, irDocument.Children.Count); var item = Assert.IsType <RazorCompiledItemAttributeIntermediateNode>(irDocument.Children[0]); Assert.Equal("/Foo/Bar.cshtml", item.Identifier); Assert.Equal("test", item.Kind); Assert.Equal("Some.Namespace.Test", item.TypeName); Assert.Equal(3, @namespace.Children.Count); var checksum = Assert.IsType <RazorSourceChecksumAttributeIntermediateNode>(@namespace.Children[0]); Assert.NotNull(checksum.Checksum); // Not verifying the checksum here Assert.Equal("SHA1", checksum.ChecksumAlgorithm); Assert.Equal("/Foo/Bar.cshtml", checksum.Identifier); checksum = Assert.IsType <RazorSourceChecksumAttributeIntermediateNode>(@namespace.Children[1]); Assert.NotNull(checksum.Checksum); // Not verifying the checksum here Assert.Equal("SHA1", checksum.ChecksumAlgorithm); Assert.Equal("/Foo/Import.cshtml", checksum.Identifier); }
public Visitor(IntermediateNodeBuilder document, IntermediateNodeBuilder @namespace, IntermediateNodeBuilder @class, IntermediateNodeBuilder method) { _document = document; _namespace = @namespace; _class = @class; _method = method; }
public void Execute_AddsRazorPagettribute_ToPage() { // Arrange var expectedAttribute = "[assembly:global::Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAttribute(@\"/Views/Index.cshtml\", typeof(SomeNamespace.SomeName), null)]"; var irDocument = new DocumentIntermediateNode { DocumentKind = RazorPageDocumentClassifierPass.RazorPageDocumentKind, Options = RazorCodeGenerationOptions.CreateDefault(), }; var builder = IntermediateNodeBuilder.Create(irDocument); var pageDirective = new DirectiveIntermediateNode { Directive = PageDirective.Directive, }; builder.Add(pageDirective); var @namespace = new NamespaceDeclarationIntermediateNode { Content = "SomeNamespace", Annotations = { [CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace }, }; builder.Push(@namespace); var @class = new ClassDeclarationIntermediateNode { ClassName = "SomeName", Annotations = { [CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass, }, }; builder.Add(@class); var pass = new AssemblyAttributeInjectionPass { Engine = RazorProjectEngine.Create().Engine, }; var source = TestRazorSourceDocument.Create("test", new RazorSourceDocumentProperties(filePath: null, relativePath: "/Views/Index.cshtml")); var document = RazorCodeDocument.Create(source); // Act pass.Execute(document, irDocument); // Assert Assert.Collection(irDocument.Children, node => Assert.Same(pageDirective, node), node => { var csharpCode = Assert.IsType <CSharpCodeIntermediateNode>(node); var token = Assert.IsType <IntermediateToken>(Assert.Single(csharpCode.Children)); Assert.Equal(TokenKind.CSharp, token.Kind); Assert.Equal(expectedAttribute, token.Content); }, node => Assert.Same(@namespace, node)); }
public void WriteDocument_WritesMethod() { // Arrange var document = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(document); builder.Add(new MethodDeclarationIntermediateNode() { Modifiers = { "internal", "virtual", "async", }, MethodName = "TestMethod", Parameters = { new MethodParameter() { Modifiers = { "readonly", "ref", }, ParameterName = "a", TypeName = "int", }, new MethodParameter() { ParameterName = "b", TypeName = "string", } }, ReturnType = "string", }); var codeDocument = TestRazorCodeDocument.CreateEmpty(); var options = RazorCodeGenerationOptions.CreateDefault(); var target = CodeTarget.CreateDefault(codeDocument, options); var writer = new DefaultDocumentWriter(target, options); // Act var result = writer.WriteDocument(codeDocument, document); // Assert var csharp = result.GeneratedCode; Assert.Equal( @"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709"" // <auto-generated/> #pragma warning disable 1591 #pragma warning disable 1998 internal virtual async string TestMethod(readonly ref int a, string b) { } #pragma warning restore 1998 #pragma warning restore 1591 ", csharp, ignoreLineEndingDifferences: true); }
public ImportBuilder(IntermediateNodeBuilder innerBuilder) { _innerBuilder = innerBuilder; }
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary <string, SourceSpan?> namespaces) : base(document, new ImportBuilder(builder), namespaces) { }
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary <string, SourceSpan?> namespaces, string tagHelperPrefix) : base(document, builder, namespaces) { _tagHelperPrefix = tagHelperPrefix; }
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 = _optionsFeature.GetOptions(); var namespaces = new Dictionary <string, SourceSpan?>(StringComparer.Ordinal); // The import documents should be inserted logically before the main document. var imports = codeDocument.GetImportSyntaxTrees(); if (imports != null) { var importsVisitor = new ImportsVisitor(document, builder, namespaces); for (var j = 0; j < imports.Count; j++) { var import = imports[j]; importsVisitor.FilePath = import.Source.FilePath; importsVisitor.VisitBlock(import.Root); } } var tagHelperPrefix = tagHelperContext?.Prefix; var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix) { FilePath = syntaxTree.Source.FilePath, }; visitor.VisitBlock(syntaxTree.Root); // 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 @namespace in namespaces) { var @using = new UsingDirectiveIntermediateNode() { Content = @namespace.Key, Source = @namespace.Value, }; 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); }
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary <string, SourceSpan?> namespaces) { _document = document; _builder = builder; _namespaces = namespaces; }
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags) : base(document, new ImportBuilder(builder), featureFlags) { }
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, string tagHelperPrefix, RazorParserFeatureFlags featureFlags) : base(document, builder, featureFlags) { _tagHelperPrefix = tagHelperPrefix; }
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); }