// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.Mvc.Razor.Directives; using Microsoft.AspNet.Razor; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Runtime.TagHelpers; namespace Microsoft.AspNet.Mvc.Razor { public class MvcRazorHost : RazorEngineHost, IMvcRazorHost { private const string BaseType = "Microsoft.AspNet.Mvc.Razor.RazorPage"; private static readonly string[] _defaultNamespaces = new[] { "System", "System.Linq", "System.Collections.Generic", "Microsoft.AspNet.Mvc", "Microsoft.AspNet.Mvc.Rendering", }; private static readonly Chunk[] _defaultInheritedChunks = new[] { new InjectChunk("Microsoft.AspNet.Mvc.Rendering.IHtmlHelper", "Html"), new InjectChunk("Microsoft.AspNet.Mvc.IViewComponentHelper", "Component"), new InjectChunk("Microsoft.AspNet.Mvc.IUrlHelper", "Url"), }; private readonly IFileProvider _fileProvider; // CodeGenerationContext.DefaultBaseClass is set to MyBaseType. // This field holds the type name without the generic decoration (MyBaseType) private readonly string _baseType; private ChunkInheritanceUtility _chunkInheritanceUtility; #if NET45 /// /// Initializes a new instance of with the specified /// . /// /// The path to the application base. public MvcRazorHost(string root) : this(new PhysicalFileProvider(root)) { } #endif /// /// Initializes a new instance of using the specified . /// /// A rooted at the application base path. public MvcRazorHost(IFileProvider fileProvider) : base(new CSharpRazorCodeLanguage()) { _fileProvider = fileProvider; _baseType = BaseType; TagHelperDescriptorResolver = new TagHelperDescriptorResolver(); DefaultBaseClass = BaseType + "<" + DefaultModel + ">"; DefaultNamespace = "Asp"; // Enable instrumentation by default to allow precompiled views to work with BrowserLink. EnableInstrumentation = true; GeneratedClassContext = new GeneratedClassContext( executeMethodName: "ExecuteAsync", writeMethodName: "Write", writeLiteralMethodName: "WriteLiteral", writeToMethodName: "WriteTo", writeLiteralToMethodName: "WriteLiteralTo", templateTypeName: "Microsoft.AspNet.Mvc.Razor.HelperResult", defineSectionMethodName: "DefineSection", generatedTagHelperContext: new GeneratedTagHelperContext { ExecutionContextTypeName = typeof(TagHelperExecutionContext).FullName, ExecutionContextAddMethodName = nameof(TagHelperExecutionContext.Add), ExecutionContextAddTagHelperAttributeMethodName = nameof(TagHelperExecutionContext.AddTagHelperAttribute), ExecutionContextAddHtmlAttributeMethodName = nameof(TagHelperExecutionContext.AddHtmlAttribute), ExecutionContextOutputPropertyName = nameof(TagHelperExecutionContext.Output), RunnerTypeName = typeof(TagHelperRunner).FullName, RunnerRunAsyncMethodName = nameof(TagHelperRunner.RunAsync), ScopeManagerTypeName = typeof(TagHelperScopeManager).FullName, ScopeManagerBeginMethodName = nameof(TagHelperScopeManager.Begin), ScopeManagerEndMethodName = nameof(TagHelperScopeManager.End), OutputGenerateStartTagMethodName = nameof(TagHelperOutput.GenerateStartTag), OutputGenerateContentMethodName = nameof(TagHelperOutput.GenerateContent), OutputGenerateEndTagMethodName = nameof(TagHelperOutput.GenerateEndTag), // Can't use nameof because RazorPage is not accessible here. CreateTagHelperMethodName = "CreateTagHelper", StartWritingScopeMethodName = "StartWritingScope", EndWritingScopeMethodName = "EndWritingScope", }) { ResolveUrlMethodName = "Href", BeginContextMethodName = "BeginContext", EndContextMethodName = "EndContext" }; foreach (var ns in _defaultNamespaces) { NamespaceImports.Add(ns); } } /// /// Gets the model type used by default when no model is specified. /// /// This value is used as the generic type argument for the base type public virtual string DefaultModel { get { return "dynamic"; } } /// public string MainClassNamePrefix { get { return "ASPV_"; } } /// /// Gets the list of chunks that are injected by default by this host. /// public virtual IReadOnlyList DefaultInheritedChunks { get { return _defaultInheritedChunks; } } /// /// Gets or sets the name attribute that is used to decorate properties that are injected and need to be /// activated. /// public virtual string ActivateAttribute { get { return "Microsoft.AspNet.Mvc.ActivateAttribute"; } } /// /// Gets the type name used to represent model expression properties. /// public virtual string ModelExpressionType { get { return "Microsoft.AspNet.Mvc.Rendering.ModelExpression"; } } /// /// Gets the method name used to create model expressions. /// public virtual string CreateModelExpressionMethod { get { return "CreateModelExpression"; } } private ChunkInheritanceUtility ChunkInheritanceUtility { get { if (_chunkInheritanceUtility == null) { // This needs to be lazily evaluated to support DefaultInheritedChunks being virtual. _chunkInheritanceUtility = new ChunkInheritanceUtility(this, _fileProvider, DefaultInheritedChunks); } return _chunkInheritanceUtility; } } /// public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream) { // Adding a prefix so that the main view class can be easily identified. var className = MainClassNamePrefix + ParserHelpers.SanitizeClassName(rootRelativePath); var engine = new RazorTemplateEngine(this); return engine.GenerateCode(inputStream, className, DefaultNamespace, rootRelativePath); } /// public override RazorParser DecorateRazorParser([NotNull] RazorParser razorParser, string sourceFileName) { var inheritedCodeTrees = ChunkInheritanceUtility.GetInheritedCodeTrees(sourceFileName); return new MvcRazorParser(razorParser, inheritedCodeTrees, DefaultInheritedChunks); } /// public override ParserBase DecorateCodeParser([NotNull] ParserBase incomingCodeParser) { return new MvcRazorCodeParser(_baseType); } /// public override CodeBuilder DecorateCodeBuilder([NotNull] CodeBuilder incomingBuilder, [NotNull] CodeBuilderContext context) { var inheritedChunks = ChunkInheritanceUtility.GetInheritedCodeTrees(context.SourceFile); ChunkInheritanceUtility.MergeInheritedCodeTrees(context.CodeTreeBuilder.CodeTree, inheritedChunks, DefaultModel); return new MvcCSharpCodeBuilder(context, DefaultModel, ActivateAttribute, new GeneratedTagHelperAttributeContext { ModelExpressionTypeName = ModelExpressionType, CreateModelExpressionMethodName = CreateModelExpressionMethod }); } } }