diff --git a/src/Microsoft.AspNetCore.Razor.Language/AssemblyExtension.cs b/src/Microsoft.AspNetCore.Razor.Language/AssemblyExtension.cs index b93323e018..83b70cd7aa 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/AssemblyExtension.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/AssemblyExtension.cs @@ -27,5 +27,29 @@ namespace Microsoft.AspNetCore.Razor.Language public override string ExtensionName { get; } public Assembly Assembly { get; } + + public RazorExtensionInitializer CreateInitializer() + { + // It's not an error to have an assembly with no initializers. This is useful to specify a dependency + // that doesn't really provide any Razor configuration. + var attributes = Assembly.GetCustomAttributes(); + foreach (var attribute in attributes) + { + // Using extension names and requiring them to line up allows a single assembly to ship multiple + // extensions/initializers for different configurations. + if (!string.Equals(attribute.ExtensionName, ExtensionName, StringComparison.Ordinal)) + { + continue; + } + + // There's no real protection/exception handling here because this set isn't really user-extensible + // right now. This would be a great place to add some additional diagnostics and hardening in the + // future. + var initializer = (RazorExtensionInitializer)Activator.CreateInstance(attribute.InitializerType); + return initializer; + } + + return null; + } } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs index e76bdffc94..341be49296 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorProjectEngine.cs @@ -159,29 +159,8 @@ namespace Microsoft.AspNetCore.Razor.Language // lid on how things work until we add official support for extensibility everywhere. So, this is // intentionally inflexible for the time being. var extension = extensions[i] as AssemblyExtension; - if (extension == null) - { - continue; - } - - // It's not an error to have an assembly with no initializers. This is useful to specify a dependency - // that doesn't really provide any Razor configuration. - var attributes = extension.Assembly.GetCustomAttributes(); - foreach (var attribute in attributes) - { - // Using extension names and requiring them to line up allows a single assembly to ship multiple - // extensions/initializers for different configurations. - if (!string.Equals(attribute.ExtensionName, extension.ExtensionName, StringComparison.Ordinal)) - { - continue; - } - - // There's no real protection/exception handling here because this set isn't really user-extensible - // right now. This would be a great place to add some additional diagnostics and hardening in the - // future. - var initializer = (RazorExtensionInitializer)Activator.CreateInstance(attribute.InitializerType); - initializer.Initialize(builder); - } + var initializer = extension?.CreateInitializer(); + initializer?.Initialize(builder); } } } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ExportCustomProjectEngineFactoryAttribute.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/ExportCustomProjectEngineFactoryAttribute.cs new file mode 100644 index 0000000000..923cb0a66e --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/ExportCustomProjectEngineFactoryAttribute.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Composition; + +namespace Microsoft.CodeAnalysis.Razor +{ + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public class ExportCustomProjectEngineFactoryAttribute : ExportAttribute, ICustomProjectEngineFactoryMetadata + { + public ExportCustomProjectEngineFactoryAttribute(string configurationName) + : base(typeof(IProjectEngineFactory)) + { + if (configurationName == null) + { + throw new ArgumentNullException(nameof(configurationName)); + } + + ConfigurationName = configurationName; + } + + public string ConfigurationName { get; } + + public bool SupportsSerialization { get; set; } + } +} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/ICustomProjectEngineFactoryMetadata.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/ICustomProjectEngineFactoryMetadata.cs new file mode 100644 index 0000000000..3f93a6e605 --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/ICustomProjectEngineFactoryMetadata.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.Razor +{ + public interface ICustomProjectEngineFactoryMetadata + { + string ConfigurationName { get; } + + bool SupportsSerialization { get; } + } +} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/IProjectEngineFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/IProjectEngineFactory.cs new file mode 100644 index 0000000000..903c47842b --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/IProjectEngineFactory.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.CodeAnalysis.Razor +{ + public interface IProjectEngineFactory + { + RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action configure); + } +}