-
Notifications
You must be signed in to change notification settings - Fork 372
Inside the Template Engine
The template engine is composed of several different subsystems, which are designed to separate gathering, instantiating and processing templates.
This is responsible for holding all properties of the environment.
Template engine provides a default implementation: EngineEnvironmentSettings.
It has a single constructor that accepts ITemplateEngineHost and set up settings based on TemplateEngine.Edge implementation of component manager, paths, and environment. All this can also be passed in as optional parameters.
The interface is responsible for providing host information to template engine,
managing file system and logging. Host may also provide default parameter
values for templates via TryGetHostParamDefault
method.
Applications using template engine are often called “hosts”. dotnet/templating repo is managing one of such hosts: dotnet new CLI, which is part of .NET SDK.
Main host properties:
-
Identifier – unique name of the host
-
Version – version of the host
Those properties are used to identify host to various built-in components explained below.
If you are considering using the template engine core, you need to create an implementation of this interface representing your host. Template engine provides the default implementation: DefaultTemplateEngineHost.
Abstraction over file system, accessible from ITemplateEngineHost.
Each template engine host should support physical and in-memory file system and
virtualize the file system under a given path. Switching between physical and in-memory
file systems is done via ITemplateEngineHost.VirtualizePath
method.
Template engine provides default implementation of file systems: PhysicalFileSystem and InMemoryFileSystem.
ComponentManager
is responsible for loading components provided by the host from
ITemplateEngineHost.BuiltIns
. Additional components can be added via
IComponentManager.AddComponent
. Similar to templates, components are also
dynamically loaded from template packages during scanning. ComponentManager
implementation is not publicly accessible; however, it is used when default
implementation of IEngineEnvironmentSettings
is used.
Abstraction over environment variables. Default implementation uses system environment variables.
Provides the main paths used by template engine: global settings path, host settings path, host version settings path.
The default locations are:
-
<home directory>/.templateengine – global settings
-
<home directory>/.templateengine/<host ID> – host settings
-
<home directory>/.templateengine/<host ID>/<host version> – host version settings
TemplatePackageManager class
The class responsible for managing templates and templates packages.
-
Gets available providers to install/update/uninstall template packages available via IComponentManager
-
Gets available template packages
-
TemplatePackagesChanged which is triggered when list of template packages changes
-
Gets available templates
-
Manages template packages cache and template cache.
The host need to instantiate the class when needed. Note that the first-time run can be time consuming, so consider creating one instance for duration of hosting application.
TemplateCreator class
The class responsible for template creation, including dry run. The host need to instantiate the class when needed.
It is possible to supply evaluation results of parameters conditions when instantiating template via Edge API TemplateCreator.InstantiateAsync
. Example use case is instantiation from Visual Studio host, that will leverage condition evaluator integrated within the New Project Dialog.
This can be achieved by passing the structured InputDataSet
argument that is populated with EvaluatedInputParameterData
objects for evaluated parameters.
It is currently not possible to provide just partial external evaluation - meaning that the template engine evaluates either all the parameter conditions or none. If the InputDataSet
collection contains at least one EvaluatedInputParameterData
element, results of all parameter conditions are expected to be passed.
Template engine cross checks externally passed evaluations. If it encounteres mismatch between externally passed result and internal evaluation result a failed ITemplateCreationResult
is returned from InstantiateAsync
API. Failure is indicated by CondtionsEvaluationMismatch
in Status
property.
The class responsible for loading available constraint components and evaluating the constraints for given template.
Template engine publishes the following packages:
-
Microsoft.TemplateEngine.Abstractions – contains interfaces to work with template engine
-
Microsoft.TemplateEngine.Egde – enables hosting the template engine in the application
-
Microsoft.TemplateEngine.IDE – lightweight API over Edge (optional to use)
-
Microsoft.TemplateEngine.Orchestrator.RunnableProjects – template engine default generator (a.k.a. the generator of template.json format)
-
Microsoft.TemplateEngine.Utils – different utilities useful for template engine usage
Main purpose of components is to allow template authors and host implementers to supply their own implementations of different interfaces and to invoke them by specifying GUID or name that defines component in template.json. Components can be split into two main categories:
-
the components required to run templates:
-
macros
-
post actions (TBD)
-
generators
-
constraints
-
bind source
-
-
the components required by the host:
-
template package providers: managed and non-managed
-
installers
-
mount points
-
Template package providers responsible for providing template packages the template engine needs to manage. There are two kinds of providers:
-
Non-managed (ITemplatePackageProvider) – the template engine uses the packages provided by provider however cannot modify them
-
Managed (IManagedTemplatePackageProvider) – the template engine can install/update/uninstall packages to provider.
Template engine provides the following providers:
-
Global settings managed provider – the packages available to all template engine hosts using built in implementation.
-
(not available yet) host managed provider - the packages available to current host.
-
(not available yet) host version managed provider - the packages available to current host version.
Template engine also provides default non-managed provider implementation that can be used by other hosts to build simple providers or base implementation on. Here are two examples: SdkTemplates and OptionalWorkloads.
Template providers can implement the IPrioritizedComponent interface to setup the provider priority. The providers will be read following the priority with the priority with largest value of priority to be preferred in case the providers have same templates defined.
The default priority of Global Settings provider is 1000. If priority is not defined, the provider will be treated as provider with priority 0.
Note: it is possible to define negative priority, then the provider will be lower priority than default.
When implementing provider, note that the packages returned by provider will be processed exactly in the same order they provided, in case of template is available in several packages, the last package will win.
Installer installs, updates and uninstall the template packages that can be feed to template package providers.
Template engine provides two installer implementations:
-
NuGet – installs the template package from NuGet feed
-
Folder – installs the template package from given folder path
You can use them when implementing your own providers, they are available from IComponentManager.
Template package location is represented by mount point URI. Mount point that can load certain type of URI are also components.
Template engine provides two mountpoint factory implementations:
-
Zip/NuGet – manages zip package/NuGet package
-
File System – manages folder on local file system
They are available from IComponentManager.
It is possible to get mount point using bool TryGetMountPoint(string mountPointUri, out IMountPoint mountPoint) method of IEngineEnvironmentSettings.
The template may define certain constraint(s) under which it can be used. The constraint implementation evaluates those restrictions and whether such template is allowed to be used or is restricted.
Template engine host works with constraint using TemplateConstraintManager
class.
The template may define bind
symbols. Those symbols might be bound to values provided externally using bind symbol source components.
Default sources are:
- host provided parameters - uses prefix
host:
. Binds symbol to parameters provided by the host. - environment variables - uses prefix
env:
. Binds symbol to environment variables.
These components are defined in Microsoft.TemplateEngine.Edge
and are part of mandatory components collection to be loaded.
If the binding
does not have prefix, or prefixed value cannot be evaluated the evaluation without prefix is performed as the fallback.
In case there are more than one component which returns the value, the component priority will be used to define the value to be bounded. In case of same priorities, the evaluation results in error.
The template author needs to explicitly specify the prefix in this case. It is possible to restrict usage of prefix with setting IBindSymbolSource.RequiresPrefixMatch
to true
.
When creating template engine host, all built in components will be registered.
If you’d like to use the runnable projects (template.json) generator available from Microsoft.TemplateEngine.Orchestrator.RunnableProjects, you need to add it using AddComponent method of IComponentManager.
You can implement the following own components and register them:
-
ITemplateEngineProvider – non-managed template package provider, via ITemplatePackageProviderFactory
-
IManagedTemplateEngineProvider – managed template package provider, via ITemplatePackageProviderFactory
-
IMountPoint – mount point implementation, via IMountPointFactory
-
IInstaller – template package installer, via IInstallerFactory
-
ITemplateConstraint – constraint implementation, via ITemplateConstraintFactory
-
IBindSymbolSource – bind symbol source implementation
It is possible to register additional components to in the following way:
-
Components of ITemplateEngineHost.BuiltInComponents will be added when loading environment settings. For DefaultTemplateEngineHost they can be passed to constructor.
-
You can use IComponentManager.AddComponent method to add the component in runtime.
Note that components are not persisted, so they should be added each time EngineEnvironmentSettings instance is created.
The package provides Bootstrapper class allowing to setup and access main functionality of template engine via single entry point.
Note that it is not mandatory to use Bootstrapper class to use template engine, however if you don’t need advanced features it might be easier to start with using this API.
Basic workflow:
-
The application hosting template engine should implement and instantiate ITemplateEngineHost, or instantiate default implementation (DefaultTemplateEngineHost).
-
The application creates the instance of Bootstrapper:
-
If session state doesn’t need to be persisted use virtualizeConfiguration set to true – all the settings will be stored in memory only.
-
If the host assumes running default infrastructure, use loadDefaultComponents set to true – this will load the default generator and default infrastructure (providers, installers, mount points)
-
-
If the application has the templates specific to it, the application should implement ITemplatePackageProvider returning available template packages and register it via Bootstrapper.AddComponent method or provide it via built-in components of the host.
-
To get template packages, use GetTemplatePackages method.
Note: these templates are available to all template engine hosting applications for the current user.
-
To manage template packages installed globally, use InstallTemplatePackagesAsync, GetLatestVersionAsync, UpdateTemplatePackagesAsync, UninstallTemplatePackagesAsync methods.
Installation supports installing local sources (folder, packages) as well as installing NuGet packages from remote feeds.
Note: these actions impact set of template packages available to all template engine hosting applications for the current user.
-
To available list all templates, use GetTemplatesAsync method.
The method also supports filters and criterias to filter templates. The default filters are available in Utils.WellKnownSearchFilters class.
-
To dry run the template, use GetCreationEffectsAsync method.
This method needs template definition (ITemplateInfo) that can be obtained through GetTemplatesAsync.
-
To run the template, use CreateAsync method.
This method needs template definition (ITemplateInfo) that can be obtained through GetTemplatesAsync.
Note: Bootstrapper class needs to be disposed.