-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
92e2b88
commit 917045b
Showing
3 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
docs/contributing/how-to-create-new-generated-symbol.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# How to create new generated symbol type | ||
|
||
Available generated symbol types are described [here](../Available-Symbols-Generators.md). | ||
Generated symbols are processed by a corresponding macro component that can generate new variables before the template is instantiated. New variables can be created from other variables (usually parameters) or independently. | ||
|
||
We appreciate creating new types of generated symbols and macros by the community. | ||
|
||
Generated symbols follow the following syntax: | ||
```json | ||
{ | ||
"symbols": | ||
{ | ||
"symbolName": | ||
{ | ||
"type": "generated", | ||
"generator": "your-type", //type of generated symbol; should be same as type of the macro implementing it | ||
"dataType": "string", //data type of the value that symbol generates: "string", "choice", "bool", "float", "int", "hex", "text" | ||
"parameters": | ||
{ | ||
"name": "value" //key-value parameters for the symbol. The value may be JSON array or object, if more complicated configuration is needed. | ||
}, | ||
"replaces": "to-be-replaced", // the text to replace with the value of this symbol in template content | ||
"fileRename": "to-be-replaced", // defines the portion of file name which will be replaced by symbol value | ||
} | ||
|
||
} | ||
} | ||
``` | ||
|
||
To create new generated symbol type, follow the following guideline | ||
|
||
1. Implement [`Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Abstractions.IGeneratedSymbolMacro<T>`](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Abstractions/IGeneratedSymbolMacro.cs) interface. | ||
|
||
Any macro implementation is a component ([`IIdentifiedComponent`](../../src/Microsoft.TemplateEngine.Abstractions/IIdentifiedComponent.cs)) and will be loaded by template engine. | ||
The existing implementation macro components are located in [`Macros`](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Macros) folder of `Microsoft.TemplateEngine.Orchestrator.RunnableProjects` projects. | ||
|
||
The implementation should have: | ||
- `Id` property - unique GUID for component | ||
- `Type` property - unique `string` name, matching generated symbol type | ||
- `Evaluate(IEngineEnvironmentSettings environmentSettings, IVariableCollection variables, T config)` method - evaluates the variables based on `config` specified | ||
- `Evaluate(IEngineEnvironmentSettings environmentSettings, IVariableCollection variables, IGeneratedSymbolConfig generatedSymbolConfig)` method - evaluates the variables based on `generatedSymbolConfig` specified | ||
- `CreateConfig(IEngineEnvironmentSettings environmentSettings, IGeneratedSymbolConfig generatedSymbolConfig)` method - creates macro-specific configuration from `generatedSymbolConfig`, that later on can be passed to `Evaluate` method | ||
|
||
You may want to derive your own implementation from `BaseGeneratedSymbolMacro<T>` base class that offers some common logic. Configuration of the macro may derive from `BaseMacroConfig` class, that already have some utilities to parse the JSON configuration. | ||
When using base class, you need to implement the following members: | ||
- `Id` and `Type` properties assigned to constant unique values | ||
- `Evaluate` method: the method should create new variables to `IVariableCollection variableCollection` using parsed `config` and existing variables. It is recommended to log errors and debug information using logger available from `environmentSettings`. If you need to access the file system, do so via `environmentSettings.Host.FileSystem` abstraction. | ||
- `CreateConfig` method - creates macro specific config from `IGeneratedSymbolConfig` config. | ||
|
||
The very basic implementation may be: | ||
```CSharp | ||
internal class HelloMacro : BaseGeneratedSymbolMacro<HelloMacroConfig> | ||
{ | ||
public override string Type => "hello"; | ||
|
||
public override Guid Id { get; } = new Guid("342BC62F-8FED-4E5A-AB59-F9AB98030155"); | ||
|
||
public override void Evaluate(IEngineEnvironmentSettings environmentSettings, IVariableCollection variableCollection, HelloMacroConfig config) | ||
{ | ||
string greetings = $"Hello {config.NameToGreet}!"; | ||
//set configured variable to Hello Name! | ||
variableCollection[config.VariableName] = greetings; | ||
environmentSettings.Host.Logger.LogDebug("[{macro}]: Variable '{var}' was assigned to value '{value}'.", nameof(HelloMacro), config.VariableName, greetings); | ||
} | ||
|
||
protected override HelloMacroConfig CreateConfig(IGeneratedSymbolConfig generatedSymbolConfig) => new(this, generatedSymbolConfig); | ||
} | ||
|
||
internal class HelloMacroConfig : BaseMacroConfig<HelloMacro, HelloMacroConfig> | ||
{ | ||
internal HelloMacroConfig(HelloMacro macro, string variableName, string? dataType = null, string nameToGreet) : base(macro, variableName, dataType) | ||
{ | ||
if (string.IsNullOrEmpty(nameToGreet)) | ||
{ | ||
throw new ArgumentException($"'{nameof(nameToGreet)}' cannot be null or empty.", nameof(nameToGreet)); | ||
} | ||
|
||
NameToGreet = nameToGreet; | ||
} | ||
|
||
internal HelloMacroConfig(HelloMacro macro, IGeneratedSymbolConfig generatedSymbolConfig) | ||
: base(macro, generatedSymbolConfig.VariableName, generatedSymbolConfig.DataType) | ||
{ | ||
NameToGreet = GetMandatoryParameterValue(generatedSymbolConfig, nameof(NameToGreet)); | ||
} | ||
|
||
internal string NameToGreet { get; } | ||
} | ||
``` | ||
|
||
`IGeneratedSymbolConfig` config already contains the pre-parsed JSON from template.json. It has properties for: symbol name, data type (if specified) and parameters collection. | ||
Parameters collection contains parameter key-value pairs from JSON. Note that value is in JSON format, i.e. if the parameter value is string, the it contains `"\"string-value\""`. | ||
It is recommend to get `JToken` using `JToken.Parse` on this value when parsing the value or use helper methods available in `BaseMacroConfig` that can parse the data. | ||
|
||
2. Once the macro is implemented, add it to [components collection](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Components.cs). | ||
|
||
3. [optional] Update [JSON schema](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Schemas/JSON/template.json) with new generated symbol syntax. | ||
If you do so, also add [a new test case](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/GeneratorTest.json) for testing the syntax. | ||
|
||
4. Add unit tests for new implementation. Macro related unit tests are located in [this folder](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/MacroTests/). | ||
For more complete scenario, consider adding the full template generation tests to [`RunnableProjectGeneratorTests.cs`](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/RunnableProjectGeneratorTests.cs). | ||
|
||
5. Update documentation in [docs folder](../Available-Symbols-Generators.md). | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# How to create a new value form | ||
|
||
Available value forms are described [here](../Runnable-Project-Templates---Value-Forms.md). | ||
Value form represent the form of the parameter, usually related to specific casing. | ||
|
||
We appreciate creating new value forms by the community. | ||
|
||
Value forms follow the following syntax: | ||
```json | ||
{ | ||
"forms": | ||
{ | ||
"nameOfTheForm": | ||
{ | ||
"identifier": "name-unique", //type of value form; should be unique value | ||
"param1": "value1", //key-value parameters for the form. The value may be JSON array or object, if more complicated configuration is needed. | ||
"param2": "value2" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
To create new value form, follow the following guideline: | ||
|
||
1. Implement [`Microsoft.TemplateEngine.Orchestrator.RunnableProjects.ValueForms.IValueFormFactory`](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms/IValueFormFactory.cs) interface. | ||
|
||
The existing implementation of value form are located in [`ValueForms`](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms) folder of `Microsoft.TemplateEngine.Orchestrator.RunnableProjects` projects. | ||
|
||
The implementation should have: | ||
- `Identifier` property - unique identifier of the form | ||
- `Type` property - unique `string` name, matching generated symbol type | ||
- `Create(string? name = null)` method - creates the form for default configuration | ||
- `FromJObject(string name, JObject? configuration = null)` method - creates the form for json configuration | ||
|
||
The `IValueForm` has `Identifier` property that matches factory `Identifier`, `Name` corresponding the name of the form in JSON and `Process` method that create the form for the passed `value`. | ||
|
||
You may want to derive your own implementation from one of the base classes: | ||
- `BaseValueFormFactory` - basic features | ||
- `ActionableValueFormFactory`- the form that just does the action without configuration | ||
- `ConfigurableValueFormFactory`- the form that performs the action and based on configuration | ||
- `DependantValueFormFactory` - the form that needs other forms for processing | ||
|
||
The very basic implementation may be: | ||
```CSharp | ||
internal class HelloValueForm : ActionableValueFormFactory | ||
{ | ||
internal const string FormIdentifier = "hello"; | ||
|
||
public HelloValueForm() : base(FormIdentifier) | ||
{ | ||
} | ||
|
||
protected override string Process(string value) => $"Hello {value}!"; | ||
} | ||
``` | ||
|
||
2. Once the value form is implemented, add it to [value form collection](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueFormRegistry.cs). | ||
|
||
3. [optional] Update [JSON schema](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Schemas/JSON/template.json) with new value form syntax. | ||
If you do so, also add [a new test case](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/) for testing the syntax. | ||
|
||
4. Add unit tests for new implementation. Macro related unit tests are located in [this folder](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/ValueFormTests/). | ||
For more complete scenario, consider adding the full template generation tests to [`RunnableProjectGeneratorTests.cs`](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/RunnableProjectGeneratorTests.cs). | ||
|
||
5. Update documentation in [docs folder](../Runnable-Project-Templates---Value-Forms.md). |