The previous implementation of `WithParser` used regex, which struggles
with parsing nested structures correctly. This commit improves
`WithParser` to track and parse all nested `with` expressions.
Other improvements:
- Throw meaningful errors when syntax is wrong. Replacing the prior
behavior of silently ignoring such issues.
- Remove `I` prefix from related interfaces to align with newer code
conventions.
- Add more unit tests for `with` expression.
- Improve documentation for templating.
- `ExpressionRegexBuilder`:
- Use words `capture` and `match` correctly.
- Fix minor issues revealed by new and improved tests:
- Change regex for matching anything except surrounding
whitespaces. The new regex ensures that it works even without
having any preceeding text.
- Change regex for capturing pipelines. The old regex was only
matching (non-greedy) first character of the pipeline in tests,
new regex matches the full pipeline.
- `ExpressionRegexBuilder.spec.ts`:
- Ensure consistent way to define `describe` and `it` blocks.
- Replace `expectRegex` tests, regex expectations test internal
behavior of the class, not the external.
- Simplified tests by eliminating the need for UUID suffixes/prefixes.
200 lines
5.2 KiB
Markdown
200 lines
5.2 KiB
Markdown
# Templating
|
|
|
|
## Benefits of templating
|
|
|
|
- **Code sharing:** Share code across scripts for consistent practices and easier maintenance.
|
|
- **Script independence:** Generate self-contained scripts, eliminating the need for external code.
|
|
- **Cleaner code:** Use pipes for complex operations, resulting in more readable and streamlined code.
|
|
|
|
## Expressions
|
|
|
|
**Syntax:**
|
|
|
|
Expressions are enclosed within `{{` and `}}`.
|
|
Example: `Hello {{ $name }}!`.
|
|
They are a core component of templating, enhancing scripts with dynamic capabilities and functionality.
|
|
|
|
**Syntax similarity:**
|
|
|
|
The syntax shares similarities with [Go Templates ❤️](https://pkg.go.dev/text/template), but with some differences:
|
|
|
|
**Function definitions:**
|
|
|
|
You can use expressions in function definition.
|
|
Refer to [Function](./collection-files.md#function) for more details.
|
|
|
|
Example usage:
|
|
|
|
```yaml
|
|
name: GreetFunction
|
|
parameters:
|
|
- name: name
|
|
code: Hello {{ $name }}!
|
|
```
|
|
|
|
If you assign `name` the value `world`, invoking `GreetFunction` would result in `Hello world!`.
|
|
|
|
**Function arguments:**
|
|
|
|
You can also use expressions in arguments in nested function calls.
|
|
Refer to [`Function | collection-files.md`](./collection-files.md#functioncall) for more details.
|
|
|
|
Example with nested function calls:
|
|
|
|
```yaml
|
|
-
|
|
name: PrintMessageFunction
|
|
parameters:
|
|
- name: message
|
|
code: echo "{{ $message }}"
|
|
-
|
|
name: GreetUserFunction
|
|
parameters:
|
|
- name: userName
|
|
call:
|
|
name: PrintMessageFunction
|
|
parameters:
|
|
argument: 'Hello, {{ $userName }}!'
|
|
```
|
|
|
|
Here, if `userName` is `Alice`, invoking `GreetUserFunction` would execute `echo "Hello, Alice!"`.
|
|
|
|
**Nested templates:**
|
|
|
|
You can nest expressions inside expressions (also called "nested templates").
|
|
This means that an expression can output another expression where compiler will compile both.
|
|
|
|
For example, following would compile first [with expression](#with), and then [parameter substitution](#parameter-substitution) in its output:
|
|
|
|
```go
|
|
{{ with $condition }}
|
|
echo {{ $text }}
|
|
{{ end }}
|
|
```
|
|
|
|
### Parameter substitution
|
|
|
|
Parameter substitution dynamically replaces variable references with their corresponding values in the script.
|
|
|
|
**Example function:**
|
|
|
|
```yaml
|
|
name: DisplayTextFunction
|
|
parameters:
|
|
- name: 'text'
|
|
code: echo {{ $text }}
|
|
```
|
|
|
|
Invoking `DisplayTextFunction` with `text` set to `"Hello, world!"` would result in `echo "Hello, World!"`.
|
|
|
|
### with
|
|
|
|
The `with` expression enables conditional rendering and provides a context variable for simpler code.
|
|
|
|
**Optional block rendering:**
|
|
|
|
If the provided variable is falsy (`false`, `null`, or empty), the compiler skips the enclosed block of code.
|
|
A "block" lies between the with start (`{{ with .. }}`) and end (`{{ end }}`) expressions, defining its boundaries.
|
|
|
|
Example:
|
|
|
|
```go
|
|
{{ with $optionalVariable }}
|
|
Hello
|
|
{{ end }}
|
|
```
|
|
|
|
This would display `Hello` if `$optionalVariable` is truthy.
|
|
|
|
**Parameter declaration:**
|
|
|
|
You should set `optional: true` for the argument if you use it like `{{ with $argument }} .. {{ end }}`.
|
|
|
|
Declare parameters used for `with` condition as optional such as:
|
|
|
|
```yaml
|
|
name: ConditionalOutputFunction
|
|
parameters:
|
|
- name: 'data'
|
|
optional: true
|
|
code: |-
|
|
{{ with $data }}
|
|
Data is: {{ . }}
|
|
{{ end }}
|
|
```
|
|
|
|
**Context variable:**
|
|
|
|
`with` statement binds its context (value of the provided parameter value) as arbitrary `.` value.
|
|
`{{ . }}` syntax gives you access to the context variable.
|
|
This is optional to use, and not required to use `with` expressions.
|
|
|
|
For example:
|
|
|
|
```go
|
|
{{ with $parameterName }}Parameter value is {{ . }} here {{ end }}
|
|
```
|
|
|
|
**Multiline text:**
|
|
|
|
It supports multiline text inside the block. You can write something like:
|
|
|
|
```go
|
|
{{ with $argument }}
|
|
First line
|
|
Second line
|
|
{{ end }}
|
|
```
|
|
|
|
**Inner expressions:**
|
|
|
|
You can also embed other expressions inside its block, such as [parameter substitution](#parameter-substitution):
|
|
|
|
```go
|
|
{{ with $condition }}
|
|
This is a different parameter: {{ $text }}
|
|
{{ end }}
|
|
```
|
|
|
|
This also includes nesting `with` statements:
|
|
|
|
```go
|
|
{{ with $condition1 }}
|
|
Value of $condition1: {{ . }}
|
|
{{ with $condition2 }}
|
|
Value of $condition2: {{ . }}
|
|
{{ end }}
|
|
{{ end }}
|
|
```
|
|
|
|
### Pipes
|
|
|
|
Pipes are functions designed for text manipulation.
|
|
They allow for a sequential application of operations resembling [Unix pipelines](https://en.wikipedia.org/wiki/Pipeline_(Unix)), also known as "chaining".
|
|
Each pipeline's output becomes the input of the following pipe.
|
|
|
|
**Pre-defined**:
|
|
|
|
Pipes are pre-defined by the system.
|
|
You cannot create pipes in [collection files](./collection-files.md).
|
|
[A dedicated compiler](./application.md#parsing-and-compiling) provides pre-defined pipes to consume in collection files.
|
|
|
|
**Compatibility:**
|
|
|
|
You can combine pipes with other expressions such as [parameter substitution](#parameter-substitution) and [with](#with) syntax.
|
|
|
|
For example:
|
|
|
|
```go
|
|
{{ with $script }} echo "{{ . | inlinePowerShell | escapeDoubleQuotes }}" {{ end }}
|
|
```
|
|
|
|
**Naming:**
|
|
|
|
❗ Pipe names must be camelCase without any space or special characters.
|
|
|
|
**Available pipes:**
|
|
|
|
- `inlinePowerShell`: Converts a multi-lined PowerShell script to a single line.
|
|
- `escapeDoubleQuotes`: Escapes `"` characters for batch command execution, allows you to use them inside double quotes (`"`).
|