mirror of
https://github.com/tests-always-included/mo.git
synced 2026-04-08 08:50:38 +02:00
Compare commits
2 Commits
2.4.1
...
search-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b2f6cea6c | ||
|
|
7a8d1d260e |
13
.github/workflows/ci.yaml
vendored
13
.github/workflows/ci.yaml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: CI
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
- name: Run tests
|
|
||||||
run: ./run-tests
|
|
||||||
- name: Run against spec
|
|
||||||
run: ./run-spec
|
|
||||||
45
.github/workflows/release.yaml
vendored
45
.github/workflows/release.yaml
vendored
@@ -1,45 +0,0 @@
|
|||||||
name: docker push
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
push_to_registry:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
- name: Docker meta
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
id: docker_meta
|
|
||||||
uses: crazy-max/ghaction-docker-meta@v1
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository }}
|
|
||||||
tag-match: v(.*)
|
|
||||||
- name: Set up QEMU
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Cache Docker layers
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
with:
|
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
|
||||||
push: true
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,5 +5,3 @@ tests/*.diff
|
|||||||
spec/
|
spec/
|
||||||
spec-runner/
|
spec-runner/
|
||||||
node_modules/
|
node_modules/
|
||||||
package.json
|
|
||||||
package-lock.json
|
|
||||||
|
|||||||
105
API.md
105
API.md
@@ -10,48 +10,70 @@ This documentation is generated automatically from the source of [mo] thanks to
|
|||||||
Public: Template parser function. Writes templates to stdout.
|
Public: Template parser function. Writes templates to stdout.
|
||||||
|
|
||||||
* $0 - Name of the mo file, used for getting the help message.
|
* $0 - Name of the mo file, used for getting the help message.
|
||||||
|
* --allow-function-arguments - Permit functions in templates to be called with additional arguments. This puts template data directly in to the path of an eval statement. Use with caution. Not listed in the help because it only makes sense when mo is sourced.
|
||||||
|
* --fail-not-set - (`-u`) Fail upon expansion of an unset variable. Default behavior is to silently ignore and expand into empty string.
|
||||||
|
* --false - (`-e`) Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY environment variable instead to a non-empty value to enable this behavior.
|
||||||
|
* --help - (`-h)` Display a help message.
|
||||||
|
* --source=FILE - (`-s=FILE`) Source a file into the environment before processing template files.
|
||||||
|
* --path=PATH - (`-p=PATH`) Colon-separated list of paths to search for templates. They are relative to where `mo` was executed.
|
||||||
|
* -- - Used to indicate the end of options. You may use this when filenames start with hyphens.
|
||||||
* $@ - Filenames to parse.
|
* $@ - Filenames to parse.
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
--allow-function-arguments
|
|
||||||
|
|
||||||
Permit functions in templates to be called with additional arguments. This puts template data directly in to the path of an eval statement. Use with caution. Not listed in the help because it only makes sense when mo is sourced.
|
|
||||||
|
|
||||||
-u, --fail-not-set
|
|
||||||
|
|
||||||
Fail upon expansion of an unset variable. Default behavior is to silently ignore and expand into empty string.
|
|
||||||
|
|
||||||
-x, --fail-on-function
|
|
||||||
|
|
||||||
Fail when a function used by a template returns an error status code. Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a non-empty value to enable this behavior.
|
|
||||||
|
|
||||||
-e, --false
|
|
||||||
|
|
||||||
Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY environment variable instead to a non-empty value to enable this behavior.
|
|
||||||
|
|
||||||
-h, --help
|
|
||||||
|
|
||||||
Display a help message.
|
|
||||||
|
|
||||||
-s=FILE, --source=FILE
|
|
||||||
|
|
||||||
Source a file into the environment before processing template files. This can be used multiple times.
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
Used to indicate the end of options. You may optionally use this when filenames may start with two hyphens.
|
|
||||||
|
|
||||||
Mo uses the following environment variables:
|
Mo uses the following environment variables:
|
||||||
|
|
||||||
* MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows functions referenced in templates to receive additional options and arguments. This puts the content from the template directly into an eval statement. Use with extreme care.
|
* MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows functions referenced in templates to receive additional options and arguments. This puts the content from the template directly into an eval statement. Use with extreme care.
|
||||||
* MO_FUNCTION_ARGS - Arguments passed to the function
|
|
||||||
* MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort with an error.
|
|
||||||
* MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env variable will be aborted with an error.
|
* MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env variable will be aborted with an error.
|
||||||
|
|
||||||
* MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be treated as an empty value for the purposes of conditionals.
|
* MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be treated as an empty value for the purposes of conditionals.
|
||||||
|
|
||||||
* MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a help message.
|
* MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a help message.
|
||||||
|
|
||||||
Returns nothing.
|
* MO_SEARCH_PATH - Colon-separated list of folders to search for templates. They are relative to where `mo` was executed.
|
||||||
|
|
||||||
|
Returns true (0) when there are no errors. Sometimes returns (1) when there are errors and sometimes those errors are consumed. It greatly depends on the error and your options.
|
||||||
|
|
||||||
|
|
||||||
|
`files`
|
||||||
|
-------
|
||||||
|
|
||||||
|
After we encounter two hyphens together, all the rest of the arguments are files.
|
||||||
|
|
||||||
|
|
||||||
|
`MO_ALLOW_FUNCTION_ARGUMENTS`
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
shellcheck disable=SC2030
|
||||||
|
|
||||||
|
|
||||||
|
`MO_FAIL_ON_UNSET`
|
||||||
|
------------------
|
||||||
|
|
||||||
|
shellcheck disable=SC2030
|
||||||
|
|
||||||
|
|
||||||
|
`MO_FALSE_IS_EMPTY`
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
shellcheck disable=SC2030
|
||||||
|
|
||||||
|
|
||||||
|
`doubleHyphens`
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Set a flag indicating we've encountered double hyphens
|
||||||
|
|
||||||
|
|
||||||
|
`files`
|
||||||
|
-------
|
||||||
|
|
||||||
|
Every arg that is not a flag or a option should be a file
|
||||||
|
|
||||||
|
|
||||||
|
`moProcessSearchPath()`
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Internal: Change relative paths into absolute paths
|
||||||
|
|
||||||
|
|
||||||
`moCallFunction()`
|
`moCallFunction()`
|
||||||
@@ -59,10 +81,9 @@ Returns nothing.
|
|||||||
|
|
||||||
Internal: Call a function.
|
Internal: Call a function.
|
||||||
|
|
||||||
* $1 - Variable for output
|
* $1 - Function to call
|
||||||
* $2 - Function to call
|
* $2 - Content to pass
|
||||||
* $3 - Content to pass
|
* $3 - Additional arguments as a single string
|
||||||
* $4 - Additional arguments as a single string
|
|
||||||
|
|
||||||
This can be dangerous, especially if you are using tags like {{someFunction ; rm -rf / }}
|
This can be dangerous, especially if you are using tags like {{someFunction ; rm -rf / }}
|
||||||
|
|
||||||
@@ -254,7 +275,7 @@ Returns nothing.
|
|||||||
Internal: Read a file into a variable.
|
Internal: Read a file into a variable.
|
||||||
|
|
||||||
* $1 - Variable name to receive the file's content
|
* $1 - Variable name to receive the file's content
|
||||||
* $2 - Filename to load - if empty, defaults to /dev/stdin
|
* $2 - Filename to load
|
||||||
|
|
||||||
Returns nothing.
|
Returns nothing.
|
||||||
|
|
||||||
@@ -310,6 +331,12 @@ Prefix all variables.
|
|||||||
Returns nothing.
|
Returns nothing.
|
||||||
|
|
||||||
|
|
||||||
|
`IFS`
|
||||||
|
-----
|
||||||
|
|
||||||
|
Search the path for the file
|
||||||
|
|
||||||
|
|
||||||
`moShow()`
|
`moShow()`
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@@ -430,4 +457,4 @@ Save the original command's path for usage later
|
|||||||
|
|
||||||
|
|
||||||
[mo]: ./mo
|
[mo]: ./mo
|
||||||
[tomdoc.sh]: https://github.com/tests-always-included/tomdoc.sh
|
[tomdoc.sh]: https://github.com/mlafeldt/tomdoc.sh
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
FROM alpine
|
|
||||||
|
|
||||||
RUN apk add --no-cache bash
|
|
||||||
ADD mo /usr/local/bin/mo
|
|
||||||
RUN chmod +x /usr/local/bin/mo
|
|
||||||
|
|
||||||
ENTRYPOINT /usr/local/bin/mo
|
|
||||||
83
README.md
83
README.md
@@ -27,7 +27,7 @@ Requirements
|
|||||||
* The "coreutils" package (`basename` and `cat`)
|
* The "coreutils" package (`basename` and `cat`)
|
||||||
* ... that's it. Why? Because bash **can**!
|
* ... that's it. Why? Because bash **can**!
|
||||||
|
|
||||||
If you intend to develop this and run the official specs, you also need node.js.
|
If you intend to develop this and run the official specs, you also need Node.js.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
@@ -41,7 +41,7 @@ There are a few ways you can install this tool. How you install it depends on h
|
|||||||
You can install this file in `/usr/local/bin/` or `/usr/bin/` by simply downloading it, changing the permissions, then moving it to the right location. Double check that your system's PATH includes the destination folder, otherwise users may have a hard time starting the command.
|
You can install this file in `/usr/local/bin/` or `/usr/bin/` by simply downloading it, changing the permissions, then moving it to the right location. Double check that your system's PATH includes the destination folder, otherwise users may have a hard time starting the command.
|
||||||
|
|
||||||
# Download
|
# Download
|
||||||
curl -sSL https://raw.githubusercontent.com/tests-always-included/mo/master/mo -o mo
|
curl -sSL https://git.io/get-mo -o mo
|
||||||
|
|
||||||
# Make executable
|
# Make executable
|
||||||
chmod +x mo
|
chmod +x mo
|
||||||
@@ -58,7 +58,7 @@ You can install this file in `/usr/local/bin/` or `/usr/bin/` by simply download
|
|||||||
This is very similar to installing it globally but it does not require root privileges. It is very important that your PATH includes the destination folder otherwise it won't work. Some local folders that are typically used are `~/bin/` and `~/.local/bin/`.
|
This is very similar to installing it globally but it does not require root privileges. It is very important that your PATH includes the destination folder otherwise it won't work. Some local folders that are typically used are `~/bin/` and `~/.local/bin/`.
|
||||||
|
|
||||||
# Download
|
# Download
|
||||||
curl -sSL https://raw.githubusercontent.com/tests-always-included/mo/master/mo -o mo
|
curl -sSL https://git.io/get-mo -o mo
|
||||||
|
|
||||||
# Make executable
|
# Make executable
|
||||||
chmod +x mo
|
chmod +x mo
|
||||||
@@ -78,7 +78,7 @@ This is very similar to installing it globally but it does not require root priv
|
|||||||
Bash scripts can source `mo` to include the functionality in their own routines. This usage typically would have `mo` saved to a `lib/` folder in an application and your other scripts would use `. lib/mo` to bring it into your project.
|
Bash scripts can source `mo` to include the functionality in their own routines. This usage typically would have `mo` saved to a `lib/` folder in an application and your other scripts would use `. lib/mo` to bring it into your project.
|
||||||
|
|
||||||
# Download
|
# Download
|
||||||
curl -sSL https://raw.githubusercontent.com/tests-always-included/mo/master/mo -o mo
|
curl -sSL https://git.io/get-mo -o mo
|
||||||
|
|
||||||
# Move into your project folder
|
# Move into your project folder
|
||||||
mv mo ~/projects/amazing-things/lib/
|
mv mo ~/projects/amazing-things/lib/
|
||||||
@@ -91,7 +91,7 @@ How to Use
|
|||||||
|
|
||||||
If you only plan using strings and numbers, nothing could be simpler. In your shell script you can choose to export the variables. The below script is [`demo/using-strings`](demo/using-strings).
|
If you only plan using strings and numbers, nothing could be simpler. In your shell script you can choose to export the variables. The below script is [`demo/using-strings`](demo/using-strings).
|
||||||
|
|
||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
cd "$(dirname "$0")" # Go to the script's directory
|
||||||
export TEST="This is a test"
|
export TEST="This is a test"
|
||||||
echo "Your message: {{TEST}}" | ../mo
|
echo "Your message: {{TEST}}" | ../mo
|
||||||
@@ -100,7 +100,7 @@ The result? "Your message: This is a test".
|
|||||||
|
|
||||||
Using arrays adds a slight level of complexity. *You must source `mo`.* Look at [`demo/using-arrays`](demo/using-arrays).
|
Using arrays adds a slight level of complexity. *You must source `mo`.* Look at [`demo/using-arrays`](demo/using-arrays).
|
||||||
|
|
||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
cd "$(dirname "$0")" # Go to the script's directory
|
||||||
export ARRAY=( one two "three three three" four five )
|
export ARRAY=( one two "three three three" four five )
|
||||||
. ../mo # This loads the "mo" function
|
. ../mo # This loads the "mo" function
|
||||||
@@ -118,77 +118,6 @@ There are more scripts available in the [demos directory](demo/) that could help
|
|||||||
There are additional features that the program supports. Try using `mo --help` to see what is available.
|
There are additional features that the program supports. Try using `mo --help` to see what is available.
|
||||||
|
|
||||||
|
|
||||||
Enhancements
|
|
||||||
-----------
|
|
||||||
|
|
||||||
In addition to many of the features built-in to Mustache, `mo` includes a number of unique features that make it a bit more powerful.
|
|
||||||
|
|
||||||
### Loop @key
|
|
||||||
|
|
||||||
`mo` implements Handlebar's `@key` references for outputting the key inside of a loop:
|
|
||||||
|
|
||||||
Env:
|
|
||||||
```bash
|
|
||||||
myarr=( foo bar )
|
|
||||||
|
|
||||||
# Bash v4+
|
|
||||||
declare -A myassoc
|
|
||||||
myassoc[hello]="mo"
|
|
||||||
myassoc[world]="is great"
|
|
||||||
```
|
|
||||||
|
|
||||||
Template:
|
|
||||||
```handlebars
|
|
||||||
{{#myarr}}
|
|
||||||
- {{@key}} {{.}}
|
|
||||||
{{/myarr}}
|
|
||||||
|
|
||||||
{{#myassoc}}
|
|
||||||
* {{@key}} {{.}}
|
|
||||||
{{/myassoc}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
```markdown
|
|
||||||
- 0 foo
|
|
||||||
- 1 bar
|
|
||||||
|
|
||||||
* hello mo
|
|
||||||
* world is great
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Helpers / Function Arguments
|
|
||||||
|
|
||||||
Function Arguments are not a part of the official Mustache implementation, and are more often associated with Handlebar's Helper functionality.
|
|
||||||
|
|
||||||
`mo` allows for passing strings to functions.
|
|
||||||
|
|
||||||
```handlebars
|
|
||||||
{{myfunc foo bar}}
|
|
||||||
```
|
|
||||||
|
|
||||||
For security reasons, these arguments are not immediately available to function calls without a flag.
|
|
||||||
|
|
||||||
#### with `--allow-function-arguments`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
myfunc() {
|
|
||||||
# Outputs "foo, bar"
|
|
||||||
echo "$1, $2";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using `$MO_FUNCTION_ARGS`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
myfunc() {
|
|
||||||
# Outputs "foo, bar"
|
|
||||||
echo "${MO_FUNCTION_ARGS[0]}, ${MO_FUNCTION_ARGS[1]}";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Concessions
|
Concessions
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
cd "$(dirname "$0")" # Go to the script's directory
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# This embeds a template in the script without using strange `cat` syntax.
|
# This embeds a template in the script without using strange `cat` syntax.
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# This sources a simple script with the env. variables needed for the template.
|
|
||||||
|
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
|
||||||
source ../mo
|
|
||||||
|
|
||||||
export NAME="Alex"
|
|
||||||
export ARRAY=( AAA BBB CCC )
|
|
||||||
|
|
||||||
# Include an external template
|
|
||||||
INCLUDE() {
|
|
||||||
cat "$MO_FUNCTION_ARGS"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Print section title
|
|
||||||
TITLE() {
|
|
||||||
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
|
||||||
echo "$MO_FUNCTION_ARGS"
|
|
||||||
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
|
||||||
}
|
|
||||||
|
|
||||||
cat <<EOF | mo -u
|
|
||||||
{{TITLE Part 1}}
|
|
||||||
{{INCLUDE function-args-part1}}
|
|
||||||
|
|
||||||
{{TITLE Part 2}}
|
|
||||||
{{INCLUDE function-args-part2}}
|
|
||||||
EOF
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Hello, my name is {{NAME}}.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{{#ARRAY}}
|
|
||||||
* {{.}}
|
|
||||||
{{/ARRAY}}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
cd "$(dirname "$0")" # Go to the script's directory
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
cd "$(dirname "$0")" # Go to the script's directory
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd "$(dirname "$0")"/..
|
cd "$(dirname "$0")"/..
|
||||||
|
|
||||||
|
|||||||
3
demo/relative-templates/child1
Normal file
3
demo/relative-templates/child1
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* Child1
|
||||||
|
{{> deeper/child2 }}
|
||||||
|
{{> ../relative-templates/deeper/child2 }}
|
||||||
1
demo/relative-templates/child3
Normal file
1
demo/relative-templates/child3
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* Child3
|
||||||
2
demo/relative-templates/deeper/child2
Normal file
2
demo/relative-templates/deeper/child2
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
* Child2
|
||||||
|
{{> ../child3 }}
|
||||||
7
demo/relative-templates/file
Normal file
7
demo/relative-templates/file
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
This is "file"
|
||||||
|
|
||||||
|
Child1 in different ways
|
||||||
|
{{> child1 }}
|
||||||
|
{{> ./child1 }}
|
||||||
|
{{> ../relative-templates/child1 }}
|
||||||
|
{{> deeper/../child1 }}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# This sources a simple script with the env. variables needed for the template.
|
# This sources a simple script with the env. variables needed for the template.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
cd "$(dirname "$0")" # Go to the script's directory
|
||||||
export ARRAY=( one two "three three three" four five )
|
export ARRAY=( one two "three three three" four five )
|
||||||
. ../mo
|
. ../mo
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# This example does not source `mo` and is intentionally restricted to
|
# This example does not source `mo` and is intentionally restricted to
|
||||||
# variables that are not arrays.
|
# variables that are not arrays.
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd "$(dirname "$0")" # Go to the script's directory
|
|
||||||
|
|
||||||
|
|
||||||
export OPEN="{{"
|
|
||||||
export CLOSE="}}"
|
|
||||||
cat <<'EOF' | mo
|
|
||||||
You can use environment variables to write output that has double braces.
|
|
||||||
|
|
||||||
{{OPEN}}sampleTag{{CLOSE}}
|
|
||||||
EOF
|
|
||||||
20
diagnostic
20
diagnostic
@@ -10,12 +10,11 @@
|
|||||||
# diagnose what's not working and fix those low-level functions first.
|
# diagnose what's not working and fix those low-level functions first.
|
||||||
|
|
||||||
PARENT_PID=$$
|
PARENT_PID=$$
|
||||||
cd "$(dirname "$0")" || exit 1
|
cd "$(dirname "$0")"
|
||||||
rm -f diagnostic.test
|
rm -f diagnostic.test
|
||||||
rm -f diagnostic.partial
|
rm -f diagnostic.partial
|
||||||
|
|
||||||
# Load mo's functions
|
# Load mo's functions
|
||||||
# shellcheck disable=SC1091
|
|
||||||
. ./mo
|
. ./mo
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
@@ -76,20 +75,21 @@ echo "ok"
|
|||||||
|
|
||||||
echo -n "moIsArray ... "
|
echo -n "moIsArray ... "
|
||||||
(
|
(
|
||||||
export TEST_NUM=1
|
TEST=1
|
||||||
moIsArray TEST_NUM && fail "Wrongly said number was an array"
|
moIsArray TEST && fail "Wrongly said number was an array"
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
export TEST_ARR=()
|
TEST=()
|
||||||
moIsArray TEST_ARR || fail "Wrongly said array was not an array"
|
moIsArray TEST || fail "Wrongly said array was not an array"
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
export TEST_EMPTY=""
|
# shellcheck disable=SC2034
|
||||||
moIsArray TEST_EMPTY && fail "Wrongly said string was an array"
|
TEST=""
|
||||||
|
moIsArray TEST && fail "Wrongly said string was an array"
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
unset TEST_UNSET
|
unset TEST
|
||||||
moIsArray TEST_UNSET && fail "Wrongly said undefined was an array"
|
moIsArray TEST && fail "Wrongly said undefined was an array"
|
||||||
)
|
)
|
||||||
echo "ok"
|
echo "ok"
|
||||||
|
|
||||||
|
|||||||
10
make-api-doc
10
make-api-doc
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# This requires tomdoc.sh to be in your PATH.
|
# This requires tomdoc.sh to be in your PATH.
|
||||||
# https://github.com/tests-always-included/tomdoc.sh
|
# https://github.com/mlafeldt/tomdoc.sh
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
|
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
API / Function Documentation
|
API / Function Documentation
|
||||||
@@ -13,8 +13,8 @@ This documentation is generated automatically from the source of [mo] thanks to
|
|||||||
|
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
sed 's/# shellcheck.*//' mo | tomdoc.sh -m
|
tomdoc.sh -m mo
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
[mo]: ./mo
|
[mo]: ./mo
|
||||||
[tomdoc.sh]: https://github.com/tests-always-included/tomdoc.sh
|
[tomdoc.sh]: https://github.com/mlafeldt/tomdoc.sh
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
239
mo
239
mo
@@ -16,16 +16,15 @@
|
|||||||
#/ Options:
|
#/ Options:
|
||||||
#/
|
#/
|
||||||
#/ -u, --fail-not-set
|
#/ -u, --fail-not-set
|
||||||
#/ Fail upon expansion of an unset variable.
|
#/ - Fail upon expansion of an unset variable.
|
||||||
#/ -x, --fail-on-function
|
|
||||||
#/ Fail when a function returns a non-zero status code.
|
|
||||||
#/ -e, --false
|
#/ -e, --false
|
||||||
#/ Treat the string "false" as empty for conditionals.
|
#/ - Treat the string "false" as empty for conditionals.
|
||||||
#/ -h, --help
|
#/ -h, --help
|
||||||
#/ This message.
|
#/ - This message.
|
||||||
#/ -s=FILE, --source=FILE
|
#/ -s=FILE, --source=FILE
|
||||||
#/ Load FILE into the environment before processing templates.
|
#/ - Load FILE into the environment before processing templates.
|
||||||
#/ Can be used multiple times.
|
#/ -p=PATH, --path=PATH
|
||||||
|
#/ - Set a colon-delimited list of folders to search for templates.
|
||||||
#
|
#
|
||||||
# Mo is under a MIT style licence with an additional non-advertising clause.
|
# Mo is under a MIT style licence with an additional non-advertising clause.
|
||||||
# See LICENSE.md for the full text.
|
# See LICENSE.md for the full text.
|
||||||
@@ -38,69 +37,50 @@
|
|||||||
# Public: Template parser function. Writes templates to stdout.
|
# Public: Template parser function. Writes templates to stdout.
|
||||||
#
|
#
|
||||||
# $0 - Name of the mo file, used for getting the help message.
|
# $0 - Name of the mo file, used for getting the help message.
|
||||||
|
# --allow-function-arguments - Permit functions in templates to be called with
|
||||||
|
# additional arguments. This puts template data directly in to the path
|
||||||
|
# of an eval statement. Use with caution. Not listed in the help
|
||||||
|
# because it only makes sense when mo is sourced.
|
||||||
|
# --fail-not-set - (`-u`) Fail upon expansion of an unset variable. Default
|
||||||
|
# behavior is to silently ignore and expand into empty string.
|
||||||
|
# --false - (`-e`) Treat "false" as an empty value. You may set the
|
||||||
|
# MO_FALSE_IS_EMPTY environment variable instead to a non-empty value
|
||||||
|
# to enable this behavior.
|
||||||
|
# --help - (`-h)` Display a help message.
|
||||||
|
# --source=FILE - (`-s=FILE`) Source a file into the environment before processing
|
||||||
|
# template files.
|
||||||
|
# --path=PATH - (`-p=PATH`) Colon-separated list of paths to search for templates.
|
||||||
|
# They are relative to where `mo` was executed.
|
||||||
|
# -- - Used to indicate the end of options. You may use this when filenames
|
||||||
|
# start with hyphens.
|
||||||
# $@ - Filenames to parse.
|
# $@ - Filenames to parse.
|
||||||
#
|
#
|
||||||
# Options:
|
|
||||||
#
|
|
||||||
# --allow-function-arguments
|
|
||||||
#
|
|
||||||
# Permit functions in templates to be called with additional arguments. This
|
|
||||||
# puts template data directly in to the path of an eval statement. Use with
|
|
||||||
# caution. Not listed in the help because it only makes sense when mo is
|
|
||||||
# sourced.
|
|
||||||
#
|
|
||||||
# -u, --fail-not-set
|
|
||||||
#
|
|
||||||
# Fail upon expansion of an unset variable. Default behavior is to silently
|
|
||||||
# ignore and expand into empty string.
|
|
||||||
#
|
|
||||||
# -x, --fail-on-function
|
|
||||||
#
|
|
||||||
# Fail when a function used by a template returns an error status code.
|
|
||||||
# Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a
|
|
||||||
# non-empty value to enable this behavior.
|
|
||||||
#
|
|
||||||
# -e, --false
|
|
||||||
#
|
|
||||||
# Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY
|
|
||||||
# environment variable instead to a non-empty value to enable this behavior.
|
|
||||||
#
|
|
||||||
# -h, --help
|
|
||||||
#
|
|
||||||
# Display a help message.
|
|
||||||
#
|
|
||||||
# -s=FILE, --source=FILE
|
|
||||||
#
|
|
||||||
# Source a file into the environment before processing template files.
|
|
||||||
# This can be used multiple times.
|
|
||||||
#
|
|
||||||
# --
|
|
||||||
#
|
|
||||||
# Used to indicate the end of options. You may optionally use this when
|
|
||||||
# filenames may start with two hyphens.
|
|
||||||
#
|
|
||||||
# Mo uses the following environment variables:
|
# Mo uses the following environment variables:
|
||||||
#
|
#
|
||||||
# MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
|
# MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
|
||||||
# functions referenced in templates to receive additional
|
# functions referenced in templates to receive additional options and
|
||||||
# options and arguments. This puts the content from the
|
# arguments. This puts the content from the template directly into an
|
||||||
# template directly into an eval statement. Use with extreme
|
# eval statement. Use with extreme care.
|
||||||
# care.
|
#
|
||||||
# MO_FUNCTION_ARGS - Arguments passed to the function
|
|
||||||
# MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
|
|
||||||
# with an error.
|
|
||||||
# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
|
# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
|
||||||
# variable will be aborted with an error.
|
# variable will be aborted with an error.
|
||||||
|
#
|
||||||
# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be
|
# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be
|
||||||
# treated as an empty value for the purposes of conditionals.
|
# treated as an empty value for the purposes of conditionals.
|
||||||
|
#
|
||||||
# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
|
# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
|
||||||
# help message.
|
# help message.
|
||||||
#
|
#
|
||||||
# Returns nothing.
|
# MO_SEARCH_PATH - Colon-separated list of folders to search for templates.
|
||||||
|
# They are relative to where `mo` was executed.
|
||||||
|
#
|
||||||
|
# Returns true (0) when there are no errors. Sometimes returns (1) when there
|
||||||
|
# are errors and sometimes those errors are consumed. It greatly depends on the
|
||||||
|
# error and your options.
|
||||||
mo() (
|
mo() (
|
||||||
# This function executes in a subshell so IFS is reset.
|
# This function executes in a subshell so IFS is reset.
|
||||||
# Namespace this variable so we don't conflict with desired values.
|
# Namespace this variable so we don't conflict with desired values.
|
||||||
local moContent f2source files doubleHyphens
|
local moContent f2source files doubleHyphens paths
|
||||||
|
|
||||||
IFS=$' \n\t'
|
IFS=$' \n\t'
|
||||||
files=()
|
files=()
|
||||||
@@ -109,8 +89,8 @@ mo() (
|
|||||||
if [[ $# -gt 0 ]]; then
|
if [[ $# -gt 0 ]]; then
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
if $doubleHyphens; then
|
if $doubleHyphens; then
|
||||||
#: After we encounter two hyphens together, all the rest
|
# After we encounter two hyphens together, all the rest
|
||||||
#: of the arguments are files.
|
# of the arguments are files.
|
||||||
files=("${files[@]}" "$arg")
|
files=("${files[@]}" "$arg")
|
||||||
else
|
else
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
@@ -129,22 +109,13 @@ mo() (
|
|||||||
MO_FAIL_ON_UNSET=true
|
MO_FAIL_ON_UNSET=true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-x | --fail-on-function)
|
|
||||||
# shellcheck disable=SC2030
|
|
||||||
MO_FAIL_ON_FUNCTION=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
-e | --false)
|
-e | --false)
|
||||||
# shellcheck disable=SC2030
|
# shellcheck disable=SC2030
|
||||||
MO_FALSE_IS_EMPTY=true
|
MO_FALSE_IS_EMPTY=true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-s=* | --source=*)
|
-s=* | --source=*)
|
||||||
if [[ "$arg" == --source=* ]]; then
|
f2source="${arg#*=}"
|
||||||
f2source="${arg#--source=}"
|
|
||||||
else
|
|
||||||
f2source="${arg#-s=}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$f2source" ]]; then
|
if [[ -f "$f2source" ]]; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
@@ -155,13 +126,17 @@ mo() (
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
-p=* | --path=*)
|
||||||
|
MO_SEARCH_PATH="$(moProcessSearchPath "${arg#*=}")"
|
||||||
|
;;
|
||||||
|
|
||||||
--)
|
--)
|
||||||
#: Set a flag indicating we've encountered double hyphens
|
# Set a flag indicating we've encountered double hyphens
|
||||||
doubleHyphens=true
|
doubleHyphens=true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
#: Every arg that is not a flag or a option should be a file
|
# Every arg that is not a flag or a option should be a file
|
||||||
files=(${files[@]+"${files[@]}"} "$arg")
|
files=(${files[@]+"${files[@]}"} "$arg")
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -174,41 +149,42 @@ mo() (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Internal: Change relative paths into absolute paths
|
||||||
|
moProcessSearchPath() {
|
||||||
|
local in out path startingPwd IFS
|
||||||
|
|
||||||
|
IFS=:
|
||||||
|
startingPwd=$PWD
|
||||||
|
|
||||||
|
for path in $1; do
|
||||||
|
cd "$startingPwd" && cd "$path" 2>/dev/null && out="$out:$PWD"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "${out:1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Internal: Call a function.
|
# Internal: Call a function.
|
||||||
#
|
#
|
||||||
# $1 - Variable for output
|
# $1 - Function to call
|
||||||
# $2 - Function to call
|
# $2 - Content to pass
|
||||||
# $3 - Content to pass
|
# $3 - Additional arguments as a single string
|
||||||
# $4 - Additional arguments as a single string
|
|
||||||
#
|
#
|
||||||
# This can be dangerous, especially if you are using tags like
|
# This can be dangerous, especially if you are using tags like
|
||||||
# {{someFunction ; rm -rf / }}
|
# {{someFunction ; rm -rf / }}
|
||||||
#
|
#
|
||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
moCallFunction() {
|
moCallFunction() {
|
||||||
local moArgs moContent moFunctionArgs moFunctionResult
|
local moArgs
|
||||||
|
|
||||||
moArgs=()
|
moArgs=()
|
||||||
moTrimWhitespace moFunctionArgs "$4"
|
|
||||||
|
|
||||||
# shellcheck disable=SC2031
|
# shellcheck disable=SC2031
|
||||||
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
|
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
|
||||||
# Intentionally bad behavior
|
moArgs=$3
|
||||||
# shellcheck disable=SC2206
|
|
||||||
moArgs=($4)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
moContent=$(echo -n "$3" | MO_FUNCTION_ARGS="$moFunctionArgs" eval "$2" "${moArgs[@]}") || {
|
echo -n "$2" | eval "$1" "$moArgs"
|
||||||
moFunctionResult=$?
|
|
||||||
# shellcheck disable=SC2031
|
|
||||||
if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then
|
|
||||||
echo "Function '$2' with args (${moArgs[*]+"${moArgs[@]}"}) failed with status code $moFunctionResult"
|
|
||||||
exit "$moFunctionResult"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2031
|
|
||||||
local "$1" && moIndirect "$1" "$moContent"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -260,7 +236,6 @@ moFindEndTag() {
|
|||||||
if [[ -z "${4-}" ]] && moIsStandalone standaloneBytes "$scanned" "${content[2]}" true; then
|
if [[ -z "${4-}" ]] && moIsStandalone standaloneBytes "$scanned" "${content[2]}" true; then
|
||||||
#: This is also a standalone tag - clean up whitespace
|
#: This is also a standalone tag - clean up whitespace
|
||||||
#: and move those whitespace bytes to the "tag" element
|
#: and move those whitespace bytes to the "tag" element
|
||||||
# shellcheck disable=SC2206
|
|
||||||
standaloneBytes=( $standaloneBytes )
|
standaloneBytes=( $standaloneBytes )
|
||||||
content[1]="${scanned:${standaloneBytes[0]}}${content[1]}${content[2]:0:${standaloneBytes[1]}}"
|
content[1]="${scanned:${standaloneBytes[0]}}${content[1]}${content[2]:0:${standaloneBytes[1]}}"
|
||||||
scanned="${scanned:0:${standaloneBytes[0]}}"
|
scanned="${scanned:0:${standaloneBytes[0]}}"
|
||||||
@@ -302,7 +277,7 @@ moFindEndTag() {
|
|||||||
moFindString() {
|
moFindString() {
|
||||||
local pos string
|
local pos string
|
||||||
|
|
||||||
string=${2%%"$3"*}
|
string=${2%%$3*}
|
||||||
[[ "$string" == "$2" ]] && pos=-1 || pos=${#string}
|
[[ "$string" == "$2" ]] && pos=-1 || pos=${#string}
|
||||||
local "$1" && moIndirect "$1" "$pos"
|
local "$1" && moIndirect "$1" "$pos"
|
||||||
}
|
}
|
||||||
@@ -332,22 +307,22 @@ moFullTagName() {
|
|||||||
#
|
#
|
||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
moGetContent() {
|
moGetContent() {
|
||||||
local moContent moFilename moTarget
|
local content filename target
|
||||||
|
|
||||||
moTarget=$1
|
target=$1
|
||||||
shift
|
shift
|
||||||
if [[ "${#@}" -gt 0 ]]; then
|
if [[ "${#@}" -gt 0 ]]; then
|
||||||
moContent=""
|
content=""
|
||||||
|
|
||||||
for moFilename in "$@"; do
|
for filename in "$@"; do
|
||||||
#: This is so relative paths work from inside template files
|
#: This is so relative paths work from inside template files
|
||||||
moContent="$moContent"'{{>'"$moFilename"'}}'
|
content="$content"'{{>'"$filename"'}}'
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
moLoadFile moContent || return 1
|
moLoadFile content /dev/stdin || return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local "$moTarget" && moIndirect "$moTarget" "$moContent"
|
local "$target" && moIndirect "$target" "$content"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -522,7 +497,6 @@ moIsFunction() {
|
|||||||
local functionList functionName
|
local functionList functionName
|
||||||
|
|
||||||
functionList=$(declare -F)
|
functionList=$(declare -F)
|
||||||
# shellcheck disable=SC2206
|
|
||||||
functionList=( ${functionList//declare -f /} )
|
functionList=( ${functionList//declare -f /} )
|
||||||
|
|
||||||
for functionName in "${functionList[@]}"; do
|
for functionName in "${functionList[@]}"; do
|
||||||
@@ -614,7 +588,7 @@ moJoin() {
|
|||||||
# Internal: Read a file into a variable.
|
# Internal: Read a file into a variable.
|
||||||
#
|
#
|
||||||
# $1 - Variable name to receive the file's content
|
# $1 - Variable name to receive the file's content
|
||||||
# $2 - Filename to load - if empty, defaults to /dev/stdin
|
# $2 - Filename to load
|
||||||
#
|
#
|
||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
moLoadFile() {
|
moLoadFile() {
|
||||||
@@ -625,7 +599,7 @@ moLoadFile() {
|
|||||||
# As a future optimization, it would be worth considering removing
|
# As a future optimization, it would be worth considering removing
|
||||||
# cat and replacing this with a read loop.
|
# cat and replacing this with a read loop.
|
||||||
|
|
||||||
content=$(cat -- "${2:-/dev/stdin}" && echo '.') || return 1
|
content=$(cat -- "$2" && echo '.') || return 1
|
||||||
len=$((${#content} - 1))
|
len=$((${#content} - 1))
|
||||||
content=${content:0:$len} # Remove last dot
|
content=${content:0:$len} # Remove last dot
|
||||||
|
|
||||||
@@ -666,7 +640,7 @@ moLoop() {
|
|||||||
moParse() {
|
moParse() {
|
||||||
# Keep naming variables mo* here to not overwrite needed variables
|
# Keep naming variables mo* here to not overwrite needed variables
|
||||||
# used in the string replacements
|
# used in the string replacements
|
||||||
local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag moKey
|
local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag
|
||||||
|
|
||||||
moCurrent=$2
|
moCurrent=$2
|
||||||
moIsBeginning=$3
|
moIsBeginning=$3
|
||||||
@@ -697,7 +671,9 @@ moParse() {
|
|||||||
if moTest "$moTag"; then
|
if moTest "$moTag"; then
|
||||||
# Show / loop / pass through function
|
# Show / loop / pass through function
|
||||||
if moIsFunction "$moTag"; then
|
if moIsFunction "$moTag"; then
|
||||||
moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs"
|
#: Consider piping the output to moGetContent
|
||||||
|
#: so the lambda does not execute in a subshell?
|
||||||
|
moContent=$(moCallFunction "$moTag" "${moBlock[0]}" "$moArgs")
|
||||||
moParse "$moContent" "$moCurrent" false
|
moParse "$moContent" "$moCurrent" false
|
||||||
moContent="${moBlock[2]}"
|
moContent="${moBlock[2]}"
|
||||||
elif moIsArray "$moTag"; then
|
elif moIsArray "$moTag"; then
|
||||||
@@ -782,17 +758,6 @@ moParse() {
|
|||||||
moShow "$moTag" "$moCurrent"
|
moShow "$moTag" "$moCurrent"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
'@key')
|
|
||||||
# Special vars
|
|
||||||
moStandaloneDenied moContent "${moContent[@]}"
|
|
||||||
# Current content (environment variable or function)
|
|
||||||
if [[ "$moCurrent" == *.* ]]; then
|
|
||||||
echo -n "${moCurrent#*.}"
|
|
||||||
else
|
|
||||||
echo -n "$moCurrent"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
*)
|
||||||
# Normal environment variable or function call
|
# Normal environment variable or function call
|
||||||
moStandaloneDenied moContent "${moContent[@]}"
|
moStandaloneDenied moContent "${moContent[@]}"
|
||||||
@@ -841,7 +806,6 @@ moPartial() {
|
|||||||
local moContent moFilename moIndent moIsBeginning moPartial moStandalone moUnindented
|
local moContent moFilename moIndent moIsBeginning moPartial moStandalone moUnindented
|
||||||
|
|
||||||
if moIsStandalone moStandalone "$2" "$4" "$5"; then
|
if moIsStandalone moStandalone "$2" "$4" "$5"; then
|
||||||
# shellcheck disable=SC2206
|
|
||||||
moStandalone=( $moStandalone )
|
moStandalone=( $moStandalone )
|
||||||
echo -n "${2:0:${moStandalone[0]}}"
|
echo -n "${2:0:${moStandalone[0]}}"
|
||||||
moIndent=${2:${moStandalone[0]}}
|
moIndent=${2:${moStandalone[0]}}
|
||||||
@@ -860,7 +824,39 @@ moPartial() {
|
|||||||
(
|
(
|
||||||
# It would be nice to remove `dirname` and use a function instead,
|
# It would be nice to remove `dirname` and use a function instead,
|
||||||
# but that's difficult when you're only given filenames.
|
# but that's difficult when you're only given filenames.
|
||||||
cd "$(dirname -- "$moFilename")" || exit 1
|
if ! cd "$(dirname -- "$moFilename")"; then
|
||||||
|
if [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
|
||||||
|
echo "Error changing to directory: $(dirname -- "$moFilename")" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mustache likes to be silent when there are errors.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$moFilename" != */* ]] && [[ ! -f "$moFilename" ]] && [[ -n "$MO_SEARCH_PATH" ]]; then
|
||||||
|
# Search the path for the file
|
||||||
|
IFS=:
|
||||||
|
|
||||||
|
for moSearchPath in $MO_SEARCH_PATH; do
|
||||||
|
if [[ ! -f "$moFilename" ]]; then
|
||||||
|
cd "$moSearchPath"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
IFS=$' \n\t'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "${moFilename##*/}" ]]; then
|
||||||
|
if [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
|
||||||
|
echo "File does not exist: $PWD/${moFilename##*/}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mustache likes to be silent when there are errors.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
moUnindented="$(
|
moUnindented="$(
|
||||||
moLoadFile moPartial "${moFilename##*/}" || exit 1
|
moLoadFile moPartial "${moFilename##*/}" || exit 1
|
||||||
moParse "${moPartial}" "$6" true
|
moParse "${moPartial}" "$6" true
|
||||||
@@ -895,11 +891,11 @@ moPartial() {
|
|||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
moShow() {
|
moShow() {
|
||||||
# Namespace these variables
|
# Namespace these variables
|
||||||
local moJoined moNameParts moContent
|
local moJoined moNameParts
|
||||||
|
|
||||||
if moIsFunction "$1"; then
|
if moIsFunction "$1"; then
|
||||||
moCallFunction moContent "$1" "" "$3"
|
CONTENT=$(moCallFunction "$1" "" "$3")
|
||||||
moParse "$moContent" "$2" false
|
moParse "$CONTENT" "$2" false
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -974,7 +970,6 @@ moStandaloneAllowed() {
|
|||||||
local bytes
|
local bytes
|
||||||
|
|
||||||
if moIsStandalone bytes "$2" "$4" "$5"; then
|
if moIsStandalone bytes "$2" "$4" "$5"; then
|
||||||
# shellcheck disable=SC2206
|
|
||||||
bytes=( $bytes )
|
bytes=( $bytes )
|
||||||
echo -n "${2:0:${bytes[0]}}"
|
echo -n "${2:0:${bytes[0]}}"
|
||||||
local "$1" && moIndirect "$1" "${4:${bytes[1]}}"
|
local "$1" && moIndirect "$1" "${4:${bytes[1]}}"
|
||||||
@@ -1102,13 +1097,13 @@ moTrimWhitespace() {
|
|||||||
moUsage() {
|
moUsage() {
|
||||||
grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
|
grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
|
||||||
echo ""
|
echo ""
|
||||||
echo "MO_VERSION=$MO_VERSION"
|
set | grep ^MO_VERSION=
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Save the original command's path for usage later
|
# Save the original command's path for usage later
|
||||||
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
|
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
|
||||||
MO_VERSION="2.4.1"
|
MO_VERSION="2.0.4"
|
||||||
|
|
||||||
# If sourced, load all functions.
|
# If sourced, load all functions.
|
||||||
# If executed, perform the actions as expected.
|
# If executed, perform the actions as expected.
|
||||||
|
|||||||
10
package.json
Normal file
10
package.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"async": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rm -rf package-lock.json node_modules/ spec/",
|
||||||
|
"install-tests": "npm install; git clone https://github.com/mustache/spec.git spec",
|
||||||
|
"test": "node run-spec.js spec/specs/*.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
run-spec
20
run-spec
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Create a package.json so the dependency package is installed in the local
|
|
||||||
# directory
|
|
||||||
echo '{"private":true, "dependencies":{"async": "*"}}' > package.json
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# Install or update the specs
|
|
||||||
if [[ ! -d spec ]]; then
|
|
||||||
git clone https://github.com/mustache/spec.git spec
|
|
||||||
else
|
|
||||||
(
|
|
||||||
cd spec;
|
|
||||||
git pull
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Actually run the specs
|
|
||||||
node run-spec.js spec/specs/*.json
|
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ function runTest(test, done) {
|
|||||||
var output, partials, script;
|
var output, partials, script;
|
||||||
|
|
||||||
script = [
|
script = [
|
||||||
'#!/usr/bin/env bash'
|
'#!/bin/bash'
|
||||||
];
|
];
|
||||||
partials = test.partials || {};
|
partials = test.partials || {};
|
||||||
|
|
||||||
|
|||||||
14
run-tests
14
run-tests
@@ -1,8 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
. ./mo
|
. ./mo
|
||||||
PASS=0
|
PASS=0
|
||||||
FAIL=0
|
FAIL=0
|
||||||
@@ -19,16 +18,13 @@ for TEST in tests/*.expected; do
|
|||||||
"${BASE}.sh"
|
"${BASE}.sh"
|
||||||
else
|
else
|
||||||
# Fall back to using .env and .template
|
# Fall back to using .env and .template
|
||||||
# shellcheck disable=SC1090
|
|
||||||
. "${BASE}.env"
|
. "${BASE}.env"
|
||||||
echo "Do not read this input" | mo "${BASE}.template"
|
echo "Do not read this input" | mo "${BASE}.template"
|
||||||
fi
|
fi
|
||||||
) | diff -U5 - "${TEST}" > "${BASE}.diff"
|
) | diff -U5 - "${TEST}" > "${BASE}.diff"
|
||||||
|
|
||||||
statusCode=$?
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo "FAIL"
|
||||||
if [[ $statusCode -ne 0 ]]; then
|
|
||||||
echo "FAIL (status code $statusCode)"
|
|
||||||
FAIL=$(( FAIL + 1 ))
|
FAIL=$(( FAIL + 1 ))
|
||||||
else
|
else
|
||||||
echo "ok"
|
echo "ok"
|
||||||
@@ -40,6 +36,4 @@ done
|
|||||||
echo ""
|
echo ""
|
||||||
echo "Pass: $PASS"
|
echo "Pass: $PASS"
|
||||||
echo "Fail: $FAIL"
|
echo "Fail: $FAIL"
|
||||||
if [[ $FAIL -gt 0 ]]; then
|
[[ $FAIL -gt 0 ]] && exit 1
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<b>0 - resque</b>
|
<b>resque</b>
|
||||||
<b>1 - hub</b>
|
<b>hub</b>
|
||||||
<b>2 - rip</b>
|
<b>rip</b>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{{#repo}}
|
{{#repo}}
|
||||||
<b>{{@key}} - {{.}}</b>
|
<b>{{.}}</b>
|
||||||
{{/repo}}
|
{{/repo}}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
declare -A repo
|
|
||||||
repo[resque]="Resque"
|
|
||||||
repo[hub]="Hub"
|
|
||||||
repo[rip]="Rip"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<b>hub - Hub</b>
|
|
||||||
<b>rip - Rip</b>
|
|
||||||
<b>resque - Resque</b>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{{#repo}}
|
|
||||||
<b>{{@key}} - {{.}}</b>
|
|
||||||
{{/repo}}
|
|
||||||
@@ -1 +1 @@
|
|||||||
cat: --help: No such file or directory
|
File does not exist: /home/fidian/repo/mo/tests/--help
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# This should display a message indicating that the file --help
|
# This should display a message indicating that the file --help
|
||||||
# could not be found. It should not display a help messsage.
|
# could not be found. It should not display a help messsage.
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
../mo -- --help 2>&1
|
../mo -u -- --help 2>&1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
unset __NO_SUCH_VAR
|
unset __NO_SUCH_VAR
|
||||||
POPULATED="words" EMPTY="" ../mo --fail-not-set ./fail-not-set-file.template 2>&1
|
POPULATED="words" EMPTY="" ../mo --fail-not-set ./fail-not-set-file.template 2>&1
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
unset __NO_SUCH_VAR
|
unset __NO_SUCH_VAR
|
||||||
POPULATED="words" EMPTY="" ../mo --fail-not-set 2>&1 <<EOF
|
POPULATED="words" EMPTY="" ../mo --fail-not-set 2>&1 <<EOF
|
||||||
Populated: {{POPULATED}};
|
Populated: {{POPULATED}};
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Fail on function? Function 'failFunction' with args () failed with status code 1
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
|
||||||
|
|
||||||
failFunction() {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
# Must be sourced to use functions
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
. ../mo
|
|
||||||
mo --fail-on-function 2>&1 <<EOF
|
|
||||||
Fail on function? {{failFunction}}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [[ $? -ne 1 ]]; then
|
|
||||||
echo "Did not return 1"
|
|
||||||
fi
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
USER=j.doe ADMIN=false ../mo --false false-is-empty-arg.template
|
USER=j.doe ADMIN=false ../mo --false false-is-empty-arg.template
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
testArgs() {
|
|
||||||
echo "$MO_FUNCTION_ARGS"
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
No args: [] - done
|
|
||||||
One arg: [one] - done
|
|
||||||
Multiple arguments: [aa bb cc 'x' " ! {[_.|] - done
|
|
||||||
Evil: [bla; cat /etc/issue] - done
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
No args: [{{testArgs}}] - done
|
|
||||||
One arg: [{{testArgs one}}] - done
|
|
||||||
Multiple arguments: [{{testArgs aa bb cc 'x' " ! {[_.| }}] - done
|
|
||||||
Evil: [{{testArgs bla; cat /etc/issue}}] - done
|
|
||||||
@@ -14,15 +14,14 @@ Simple usage:
|
|||||||
Options:
|
Options:
|
||||||
|
|
||||||
-u, --fail-not-set
|
-u, --fail-not-set
|
||||||
Fail upon expansion of an unset variable.
|
- Fail upon expansion of an unset variable.
|
||||||
-x, --fail-on-function
|
|
||||||
Fail when a function returns a non-zero status code.
|
|
||||||
-e, --false
|
-e, --false
|
||||||
Treat the string "false" as empty for conditionals.
|
- Treat the string "false" as empty for conditionals.
|
||||||
-h, --help
|
-h, --help
|
||||||
This message.
|
- This message.
|
||||||
-s=FILE, --source=FILE
|
-s=FILE, --source=FILE
|
||||||
Load FILE into the environment before processing templates.
|
- Load FILE into the environment before processing templates.
|
||||||
Can be used multiple times.
|
-p=PATH, --path=PATH
|
||||||
|
- Set a colon-delimited list of folders to search for templates.
|
||||||
|
|
||||||
MO_VERSION=2.4.1
|
MO_VERSION=2.0.4
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
../mo --help
|
../mo -u --help
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
cat: --something: No such file or directory
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# This should display a message indicating that the file --something
|
# This should display a message indicating that the file --something
|
||||||
# could not be found.
|
# could not be found.
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
../mo --something 2>&1
|
../mo --something 2>&1
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
cat: partial-missing.partial: No such file or directory
|
File does not exist: /home/fidian/repo/mo/tests/partial-missing.partial
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
../mo partial-missing.template 2>&1
|
../mo -u partial-missing.template 2>&1
|
||||||
|
returned=$?
|
||||||
|
|
||||||
if [[ $? -ne 1 ]]; then
|
if [[ $returned -ne 1 ]]; then
|
||||||
echo "Did not return 1"
|
echo "Did not return 1. Instead, returned $returned."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
echo "Do not display this" | ../mo --source=invalid 2>&1
|
echo "Do not display this" | ../mo --source=invalid 2>&1
|
||||||
|
|
||||||
if [[ $? -ne 1 ]]; then
|
if [[ $? -ne 1 ]]; then
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
export A=from1
|
|
||||||
export B=from1
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export B=from2
|
|
||||||
export C=from2
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
A: from1
|
|
||||||
B: from2
|
|
||||||
C: from2
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
|
||||||
cat <<EOF | ../mo --source=source-multiple-1.vars --source=source-multiple-2.vars
|
|
||||||
A: {{A}}
|
|
||||||
B: {{B}}
|
|
||||||
C: {{C}}
|
|
||||||
EOF
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
echo "Do not display this" | ../mo --source= 2>&1
|
echo "Do not display this" | ../mo --source= 2>&1
|
||||||
|
|
||||||
if [[ $? -ne 1 ]]; then
|
if [[ $? -ne 1 ]]; then
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd "${0%/*}" || exit 1
|
cd "${0%/*}"
|
||||||
cat <<EOF | ../mo --source=source.vars
|
. ../mo
|
||||||
|
cat <<EOF | mo --source=source.vars
|
||||||
{{VAR}}
|
{{VAR}}
|
||||||
{{#ARR}}
|
{{#ARR}}
|
||||||
* {{.}}
|
* {{.}}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export VAR=value
|
export VAR=value
|
||||||
export ARR=(1 2 3)
|
export ARR=(1 2 3)
|
||||||
declare -A ASSOC_ARR
|
declare -A ASSOC_ARR
|
||||||
# Can not export associative arrays, otherwise they turn into indexed arrays
|
|
||||||
ASSOC_ARR=([a]=AAA [b]=BBB)
|
ASSOC_ARR=([a]=AAA [b]=BBB)
|
||||||
|
|||||||
Reference in New Issue
Block a user