@rse/nunjucks-cli 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
  ChangeLog
3
3
  =========
4
4
 
5
+ 2.1.0 (2025-12-26)
6
+ ------------------
7
+
8
+ - REFACTOR: change option -e/--extension to option -p/--plugin
9
+ - IMPROVEMENT: add new option -e for loading custom .env files
10
+ - IMPROVEMENT: add new option -E for loading standard .env files
11
+
5
12
  2.0.0 (2025-12-21)
6
13
  ------------------
7
14
 
package/Dockerfile CHANGED
@@ -5,8 +5,8 @@
5
5
  # build arguments (early)
6
6
  ARG IMAGE_PREFIX=docker.io/engelschall/
7
7
  ARG IMAGE_NAME=nunjucks-cli
8
- ARG IMAGE_VERSION=2.0.0
9
- ARG IMAGE_RELEASE=20251221
8
+ ARG IMAGE_VERSION=2.1.0
9
+ ARG IMAGE_RELEASE=20251226
10
10
  ARG IMAGE_ALIAS=latest
11
11
 
12
12
  # derive image from a certain base image
package/README.md CHANGED
@@ -61,7 +61,7 @@ $ nunjucks
61
61
  [-C|--option <key>=<value>]
62
62
  [-d|--defines <context-file>]
63
63
  [-D|--define <key>=<value>]
64
- [-e|--extension <module-name>]
64
+ [-p|--plugin <module-name>]
65
65
  [-o|--output <output-file>|-]
66
66
  <input-file>|-
67
67
  ```
@@ -80,8 +80,8 @@ $ nunjucks
80
80
  - `-D`|`--define` `<key>=<value>`:<br/>
81
81
  Set context definition key/value.
82
82
  Can occur multiple times.
83
- - `-e`|`--extension` `<module-name>`:<br/>
84
- Load Nunjucks JavaScript extension module (installed via NPM).
83
+ - `-p`|`--plugin` `<module-name>`:<br/>
84
+ Load Nunjucks JavaScript plugin module (installed via NPM).
85
85
  - `-o`|`--output` `<output-file>`|`-`:<br/>
86
86
  Save output file (or stdout).
87
87
  - `<input-file>`|`-`:<br/>
package/nunjucks.1 CHANGED
@@ -1,9 +1,9 @@
1
- .TH "NUNJUCKS" "1" "June 2025" "" ""
1
+ .TH "NUNJUCKS" "1" "December 2025" "" ""
2
2
  .SH "NAME"
3
3
  \fBnunjucks\fR - Template Rendering Engine
4
4
  .SH "SYNOPSIS"
5
5
  .P
6
- \fBnunjucks\fR \[lB]\fB-h\fR|\fB--help\fR\[rB] \[lB]\fB-V\fR|\fB--version\fR\[rB] \[lB]\fB-c\fR|\fB--config\fR \fIconfig-file\fR\[rB] \[lB]\fB-C\fR|\fB--option\fR \fIkey\fR=\fIvalue\fR\[rB] \[lB]\fB-d\fR|\fB--defines\fR \fIcontext-file\fR\[rB] \[lB]\fB-D\fR|\fB--define\fR \fIkey\fR=\fIvalue\fR\[rB] \[lB]\fB-e\fR|\fB--extension\fR \fImodule-name\fR\[rB] \[lB]\fB-o\fR|\fB--output\fR \fIoutput-file\fR|\fB-\fR\[rB] \[lB]\fIinput-file\fR|\fB-\fR\[rB]
6
+ \fBnunjucks\fR \[lB]\fB-h\fR|\fB--help\fR\[rB] \[lB]\fB-V\fR|\fB--version\fR\[rB] \[lB]\fB-e\fR|\fB--env\fR \fIenv-file\fR\[rB] \[lB]\fB-E\fR|\fB--envs\fR\[rB] \[lB]\fB-c\fR|\fB--config\fR \fIconfig-file\fR\[rB] \[lB]\fB-C\fR|\fB--option\fR \fIkey\fR=\fIvalue\fR\[rB] \[lB]\fB-d\fR|\fB--defines\fR \fIcontext-file\fR\[rB] \[lB]\fB-D\fR|\fB--define\fR \fIkey\fR=\fIvalue\fR\[rB] \[lB]\fB-p\fR|\fB--plugin\fR \fImodule-name\fR\[rB] \[lB]\fB-o\fR|\fB--output\fR \fIoutput-file\fR|\fB-\fR\[rB] \[lB]\fIinput-file\fR|\fB-\fR\[rB]
7
7
  .SH "DESCRIPTION"
8
8
  .P
9
9
  \fBnunjucks\fR(1) is a small command-line utility to render templates with the rich and powerful templating language \fBMozilla Nunjucks\fR \fI\(lahttps://mozilla.github.io/nunjucks/\(ra\fR. This allows you to define your configuration in a YAML file and then render an output file based on a template input file where your configuration can be expanded. It optionally can load Nunjucks addons like the ones from the companion \fBNunjucks Addons\fR \fI\(lahttps://github.com/rse/nunjucks-addons\(ra\fR package.
@@ -12,23 +12,27 @@
12
12
  The following top-level options and arguments exist:
13
13
  .RS 0
14
14
  .IP \(bu 4
15
- \[lB]\fB-h\fR|\fB--help\fR\[rB] Show usage help.
15
+ \[lB]\fB-h\fR|\fB--help\fR\[rB]: Show usage help.
16
16
  .IP \(bu 4
17
- \[lB]\fB-V\fR|\fB--version\fR\[rB] Show program version information.
17
+ \[lB]\fB-V\fR|\fB--version\fR\[rB]: Show program version information.
18
18
  .IP \(bu 4
19
- \[lB]\fB-c\fR|\fB--config\fR \fIconfig-file\fR\[rB] Load Nunjucks configuration YAML file.
19
+ \[lB]\fB-e\fR|\fB--env\fR \fIenv-file\fR\[rB]: Load environment file with key/value definitions. These can later be accessed with the global \fBenv\fR variable.
20
20
  .IP \(bu 4
21
- \[lB]\fB-C\fR|\fB--option\fR \fIkey\fR=\fIvalue\fR\[rB] Set Nunjucks configuration option.
21
+ \[lB]\fB-E\fR|\fB--envs\fR\[rB]: Automatically load environment files with key/value definitions from all \fB.env\fR files in current and all parent directories.
22
22
  .IP \(bu 4
23
- \[lB]\fB-d\fR|\fB--defines\fR \fIcontext-file\fR\[rB] Load context definition YAML file. Can occur multiple times.
23
+ \[lB]\fB-c\fR|\fB--config\fR \fIconfig-file\fR\[rB]: Load Nunjucks configuration YAML file.
24
24
  .IP \(bu 4
25
- \[lB]\fB-D\fR|\fB--define\fR \fIkey\fR=\fIvalue\fR\[rB] Set context definition key/value. Can occur multiple times.
25
+ \[lB]\fB-C\fR|\fB--option\fR \fIkey\fR=\fIvalue\fR\[rB]: Set Nunjucks configuration option.
26
26
  .IP \(bu 4
27
- \[lB]\fB-e\fR|\fB--extension\fR \fImodule-name\fR\[rB] Load Nunjucks JavaScript extension module (installed via NPM).
27
+ \[lB]\fB-d\fR|\fB--defines\fR \fIcontext-file\fR\[rB]: Load context definition YAML file. Can occur multiple times.
28
28
  .IP \(bu 4
29
- \[lB]\fB-o\fR|\fB--output\fR \fIoutput-file\fR|\fB-\fR\[rB] Save output file (or stdout).
29
+ \[lB]\fB-D\fR|\fB--define\fR \fIkey\fR=\fIvalue\fR\[rB]: Set context definition key/value. Can occur multiple times.
30
30
  .IP \(bu 4
31
- \[lB]\fB<input-file>\fR|\fB-\fR\[rB] Load input file (or stdin).
31
+ \[lB]\fB-p\fR|\fB--plugin\fR \fImodule-name\fR\[rB]: Load Nunjucks JavaScript plugin module (installed via NPM).
32
+ .IP \(bu 4
33
+ \[lB]\fB-o\fR|\fB--output\fR \fIoutput-file\fR|\fB-\fR\[rB]: Save output file (or stdout).
34
+ .IP \(bu 4
35
+ \[lB]\fB<input-file>\fR|\fB-\fR\[rB]: Load input file (or stdin).
32
36
  .RE 0
33
37
 
34
38
  .SH "EXAMPLE"
package/nunjucks.js ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ ** nunjucks -- Nunjucks Template Rendering Command-Line Interface
4
+ ** Copyright (c) 2019-2025 Dr. Ralf S. Engelschall <http://engelschall.com>
5
+ ** Licensed under MIT <http://spdx.org/licenses/MIT.html>
6
+ */
7
+ /* built-in requirements */
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { createRequire } from "node:module";
11
+ /* external requirements */
12
+ import { Command } from "commander";
13
+ import chalk from "chalk";
14
+ import jsYAML from "js-yaml";
15
+ import nunjucks from "nunjucks";
16
+ import deepmerge from "deepmerge";
17
+ import dotenvx from "@dotenvx/dotenvx";
18
+ import * as findup from "find-up";
19
+ /* load my own information */
20
+ const my = JSON.parse(await fs.promises.readFile(new URL("./package.json", import.meta.url), "utf-8"));
21
+ /* parse command-line arguments */
22
+ const program = new Command();
23
+ const reduceArray = (v, l) => l.concat([v]);
24
+ program.name("nunjucks")
25
+ .description("Nunjucks Template Rendering Command-Line Interface")
26
+ .showHelpAfterError("hint: use option --help for usage information")
27
+ .option("-h, --help", "show usage help", false)
28
+ .option("-V, --version", "show program version information", false)
29
+ .option("-e, --env <env-file>", "load environment key/value file", reduceArray, [])
30
+ .option("-E, --envs", "load all environment key/value files", false)
31
+ .option("-c, --config <config-file>", "load Nunjucks configuration YAML file", "")
32
+ .option("-C, --option <key>=<value>", "set Nunjucks configuration option", reduceArray, [])
33
+ .option("-d, --defines <context-file>", "load context definition YAML file", reduceArray, [])
34
+ .option("-D, --define <key>=<value>", "set context definition key/value", reduceArray, [])
35
+ .option("-p, --plugin <module-name>", "load Nunjucks JavaScript plugin module", reduceArray, [])
36
+ .option("-o, --output <output-file>", "save output file", "-")
37
+ .argument("[<input-file>]", "input file");
38
+ program.parse(process.argv);
39
+ const argv = {
40
+ ...program.opts(),
41
+ _: program.args
42
+ };
43
+ /* handle special help request */
44
+ if (argv.help) {
45
+ console.log(program.helpInformation());
46
+ console.log("Example:\n $ echo \"Hello, {{ who }}!\" | nunjucks -Dwho=World -\n");
47
+ process.exit(0);
48
+ }
49
+ /* handle special version request */
50
+ if (argv.version) {
51
+ console.log(`${my.name} ${my.version} (Node.js ${process.versions.node}, Nunjucks: ${my.dependencies.nunjucks})`);
52
+ console.log(`${my.description}`);
53
+ console.log(`Copyright (c) 2019-2025 ${my.author.name} <${my.author.url}>`);
54
+ console.log(`Licensed under ${my.license} <http://spdx.org/licenses/${my.license}.html>`);
55
+ process.exit(0);
56
+ }
57
+ /* read input file */
58
+ let input = "";
59
+ if (argv._.length > 1) {
60
+ console.error(chalk.red("nunjucks: ERROR: invalid number of arguments (zero or one input file expected)"));
61
+ process.exit(1);
62
+ }
63
+ let inputFile = argv._[0] ?? "-";
64
+ if (inputFile === "-") {
65
+ inputFile = "<stdin>";
66
+ process.stdin.setEncoding("utf-8");
67
+ const BUFSIZE = 256;
68
+ const buf = Buffer.alloc(BUFSIZE);
69
+ while (true) {
70
+ let bytesRead = 0;
71
+ try {
72
+ bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, null);
73
+ }
74
+ catch (ex) {
75
+ if (ex.code === "EAGAIN")
76
+ continue;
77
+ else if (ex.code === "EOF")
78
+ break;
79
+ else
80
+ throw ex;
81
+ }
82
+ if (bytesRead === 0)
83
+ break;
84
+ input += buf.toString("utf8", 0, bytesRead);
85
+ }
86
+ }
87
+ else {
88
+ if (!fs.existsSync(inputFile)) {
89
+ console.error(chalk.red(`nunjucks: ERROR: failed to find input file: "${inputFile}"`));
90
+ process.exit(1);
91
+ }
92
+ input = fs.readFileSync(inputFile, { encoding: "utf8" });
93
+ }
94
+ /* provide context variables for template */
95
+ let context = {};
96
+ for (const define of argv.defines) {
97
+ try {
98
+ context = deepmerge(context, jsYAML.load(fs.readFileSync(define, { encoding: "utf8" })));
99
+ }
100
+ catch (ex) {
101
+ console.error(chalk.red(`nunjucks: ERROR: failed to load context YAML file: ${ex.toString()}`));
102
+ process.exit(1);
103
+ }
104
+ }
105
+ /* load environment variables from all default files */
106
+ if (argv.envs) {
107
+ const files = findup.findUpMultipleSync(".env");
108
+ if (files.length > 0)
109
+ dotenvx.config({ path: files, quiet: true });
110
+ }
111
+ /* load environment variables from environment files */
112
+ if (argv.env.length > 0) {
113
+ for (const env of argv.env) {
114
+ if (!fs.existsSync(env)) {
115
+ console.error(chalk.red(`nunjucks: ERROR: environment file not found: "${env}"`));
116
+ process.exit(1);
117
+ }
118
+ }
119
+ dotenvx.config({ path: argv.env, quiet: true });
120
+ }
121
+ /* expose environment variables to template */
122
+ context.env = process.env;
123
+ /* add context defines */
124
+ argv.define.forEach((define) => {
125
+ const match = define.match(/^([^=]+)(?:=(.*))?$/);
126
+ if (!match)
127
+ return;
128
+ let [, key, val] = match;
129
+ if (!key)
130
+ return;
131
+ if (val === undefined)
132
+ val = "true";
133
+ context[key] = val;
134
+ });
135
+ /* determine Nunjucks options */
136
+ let options = {};
137
+ if (argv.config) {
138
+ try {
139
+ options = jsYAML.load(fs.readFileSync(argv.config, { encoding: "utf8" }));
140
+ }
141
+ catch (ex) {
142
+ console.error(chalk.red(`nunjucks: ERROR: failed to load options YAML file: ${ex.toString()}`));
143
+ process.exit(1);
144
+ }
145
+ }
146
+ if (argv.option.length > 0)
147
+ options = Object.assign(options, argv.option);
148
+ options = {
149
+ autoescape: false,
150
+ throwOnUndefined: false,
151
+ trimBlocks: true,
152
+ lstripBlocks: true,
153
+ watch: false,
154
+ noCache: true,
155
+ ...options
156
+ };
157
+ /* configure environment */
158
+ const env = nunjucks.configure(inputFile, options);
159
+ /* load external plugin modules */
160
+ for (const plugin of argv.plugin) {
161
+ let modpath = path.resolve(plugin);
162
+ if (!fs.existsSync(modpath)) {
163
+ try {
164
+ const require = createRequire(import.meta.url);
165
+ modpath = require.resolve(plugin);
166
+ }
167
+ catch (_ex) {
168
+ modpath = null;
169
+ }
170
+ }
171
+ if (modpath === null) {
172
+ console.error(chalk.red(`nunjucks: ERROR: failed to find plugin module: ${plugin}`));
173
+ process.exit(1);
174
+ }
175
+ /* dynamically import the module */
176
+ let mod;
177
+ try {
178
+ mod = await import(modpath);
179
+ /* handle both default and named exports */
180
+ mod = mod.default ?? mod;
181
+ }
182
+ catch (ex) {
183
+ console.error(chalk.red(`nunjucks: ERROR: failed to load plugin module: ${ex.toString()}`));
184
+ process.exit(1);
185
+ }
186
+ if (!(mod !== null && typeof mod === "function")) {
187
+ console.error(chalk.red(`nunjucks: ERROR: failed to call plugin file: "${modpath}"`));
188
+ process.exit(1);
189
+ }
190
+ mod(env);
191
+ }
192
+ /* render Nunjucks template */
193
+ let output;
194
+ try {
195
+ output = env.renderString(input, context);
196
+ }
197
+ catch (ex) {
198
+ console.error(chalk.red(`nunjucks: ERROR: failed to render template: ${ex.toString()}`));
199
+ process.exit(1);
200
+ }
201
+ /* write output */
202
+ if (argv.output === "-")
203
+ process.stdout.write(output);
204
+ else
205
+ fs.writeFileSync(argv.output, output, { encoding: "utf8" });
206
+ /* die gracefully */
207
+ process.exit(0);
package/nunjucks.md CHANGED
@@ -6,11 +6,13 @@
6
6
  `nunjucks`
7
7
  \[`-h`|`--help`\]
8
8
  \[`-V`|`--version`\]
9
+ \[`-e`|`--env` *env-file*\]
10
+ \[`-E`|`--envs`\]
9
11
  \[`-c`|`--config` *config-file*\]
10
12
  \[`-C`|`--option` *key*=*value*\]
11
13
  \[`-d`|`--defines` *context-file*\]
12
14
  \[`-D`|`--define` *key*=*value*\]
13
- \[`-e`|`--extension` *module-name*\]
15
+ \[`-p`|`--plugin` *module-name*\]
14
16
  \[`-o`|`--output` *output-file*|`-`\]
15
17
  \[*input-file*|`-`\]
16
18
 
@@ -27,33 +29,41 @@ It optionally can load Nunjucks addons like the ones from the companion
27
29
 
28
30
  The following top-level options and arguments exist:
29
31
 
30
- - \[`-h`|`--help`\]
32
+ - \[`-h`|`--help`\]:
31
33
  Show usage help.
32
34
 
33
- - \[`-V`|`--version`\]
35
+ - \[`-V`|`--version`\]:
34
36
  Show program version information.
35
37
 
36
- - \[`-c`|`--config` *config-file*\]
38
+ - \[`-e`|`--env` *env-file*\]:
39
+ Load environment file with key/value definitions.
40
+ These can later be accessed with the global `env` variable.
41
+
42
+ - \[`-E`|`--envs`\]:
43
+ Automatically load environment files with key/value definitions
44
+ from all `.env` files in current and all parent directories.
45
+
46
+ - \[`-c`|`--config` *config-file*\]:
37
47
  Load Nunjucks configuration YAML file.
38
48
 
39
- - \[`-C`|`--option` *key*=*value*\]
49
+ - \[`-C`|`--option` *key*=*value*\]:
40
50
  Set Nunjucks configuration option.
41
51
 
42
- - \[`-d`|`--defines` *context-file*\]
52
+ - \[`-d`|`--defines` *context-file*\]:
43
53
  Load context definition YAML file.
44
54
  Can occur multiple times.
45
55
 
46
- - \[`-D`|`--define` *key*=*value*\]
56
+ - \[`-D`|`--define` *key*=*value*\]:
47
57
  Set context definition key/value.
48
58
  Can occur multiple times.
49
59
 
50
- - \[`-e`|`--extension` *module-name*\]
51
- Load Nunjucks JavaScript extension module (installed via NPM).
60
+ - \[`-p`|`--plugin` *module-name*\]:
61
+ Load Nunjucks JavaScript plugin module (installed via NPM).
52
62
 
53
- - \[`-o`|`--output` *output-file*|`-`\]
63
+ - \[`-o`|`--output` *output-file*|`-`\]:
54
64
  Save output file (or stdout).
55
65
 
56
- - \[`<input-file>`|`-`\]
66
+ - \[`<input-file>`|`-`\]:
57
67
  Load input file (or stdin).
58
68
 
59
69
  ## EXAMPLE
package/nunjucks.ts CHANGED
@@ -16,6 +16,8 @@ import chalk from "chalk"
16
16
  import jsYAML from "js-yaml"
17
17
  import nunjucks from "nunjucks"
18
18
  import deepmerge from "deepmerge"
19
+ import dotenvx from "@dotenvx/dotenvx"
20
+ import * as findup from "find-up"
19
21
 
20
22
  /* type definitions */
21
23
  type PackageInfo = {
@@ -38,11 +40,13 @@ type OptionsType = {
38
40
  type CLIOptions = {
39
41
  help: boolean
40
42
  version: boolean
43
+ env: string[]
44
+ envs: boolean
41
45
  config: string
42
46
  option: string[]
43
47
  defines: string[]
44
48
  define: string[]
45
- extension: string[]
49
+ plugin: string[]
46
50
  output: string
47
51
  _: string[]
48
52
  }
@@ -58,11 +62,13 @@ program.name("nunjucks")
58
62
  .showHelpAfterError("hint: use option --help for usage information")
59
63
  .option("-h, --help", "show usage help", false)
60
64
  .option("-V, --version", "show program version information", false)
65
+ .option("-e, --env <env-file>", "load environment key/value file", reduceArray, [])
66
+ .option("-E, --envs", "load all environment key/value files", false)
61
67
  .option("-c, --config <config-file>", "load Nunjucks configuration YAML file", "")
62
68
  .option("-C, --option <key>=<value>", "set Nunjucks configuration option", reduceArray, [])
63
69
  .option("-d, --defines <context-file>", "load context definition YAML file", reduceArray, [])
64
70
  .option("-D, --define <key>=<value>", "set context definition key/value", reduceArray, [])
65
- .option("-e, --extension <module-name>", "load Nunjucks JavaScript extension module", reduceArray, [])
71
+ .option("-p, --plugin <module-name>", "load Nunjucks JavaScript plugin module", reduceArray, [])
66
72
  .option("-o, --output <output-file>", "save output file", "-")
67
73
  .argument("[<input-file>]", "input file")
68
74
  program.parse(process.argv)
@@ -116,7 +122,7 @@ if (inputFile === "-") {
116
122
  }
117
123
  else {
118
124
  if (!fs.existsSync(inputFile)) {
119
- console.error(chalk.red(`nunjucks: ERROR: failed to find input file: ${inputFile}`))
125
+ console.error(chalk.red(`nunjucks: ERROR: failed to find input file: "${inputFile}"`))
120
126
  process.exit(1)
121
127
  }
122
128
  input = fs.readFileSync(inputFile, { encoding: "utf8" })
@@ -134,6 +140,24 @@ for (const define of argv.defines) {
134
140
  }
135
141
  }
136
142
 
143
+ /* load environment variables from all default files */
144
+ if (argv.envs) {
145
+ const files = findup.findUpMultipleSync(".env")
146
+ if (files.length > 0)
147
+ dotenvx.config({ path: files, quiet: true })
148
+ }
149
+
150
+ /* load environment variables from environment files */
151
+ if (argv.env.length > 0) {
152
+ for (const env of argv.env) {
153
+ if (!fs.existsSync(env)) {
154
+ console.error(chalk.red(`nunjucks: ERROR: environment file not found: "${env}"`))
155
+ process.exit(1)
156
+ }
157
+ }
158
+ dotenvx.config({ path: argv.env, quiet: true })
159
+ }
160
+
137
161
  /* expose environment variables to template */
138
162
  context.env = process.env
139
163
 
@@ -176,20 +200,20 @@ options = {
176
200
  /* configure environment */
177
201
  const env = nunjucks.configure(inputFile, options)
178
202
 
179
- /* load external extension files */
180
- for (const extension of argv.extension) {
181
- let modpath: string | null = path.resolve(extension)
203
+ /* load external plugin modules */
204
+ for (const plugin of argv.plugin) {
205
+ let modpath: string | null = path.resolve(plugin)
182
206
  if (!fs.existsSync(modpath)) {
183
207
  try {
184
208
  const require = createRequire(import.meta.url)
185
- modpath = require.resolve(extension)
209
+ modpath = require.resolve(plugin)
186
210
  }
187
211
  catch (_ex) {
188
212
  modpath = null
189
213
  }
190
214
  }
191
215
  if (modpath === null) {
192
- console.error(chalk.red(`nunjucks: ERROR: failed to find extension module: ${extension}`))
216
+ console.error(chalk.red(`nunjucks: ERROR: failed to find plugin module: ${plugin}`))
193
217
  process.exit(1)
194
218
  }
195
219
 
@@ -202,11 +226,11 @@ for (const extension of argv.extension) {
202
226
  mod = mod.default ?? mod
203
227
  }
204
228
  catch (ex: any) {
205
- console.error(chalk.red(`nunjucks: ERROR: failed to load extension module: ${ex.toString()}`))
229
+ console.error(chalk.red(`nunjucks: ERROR: failed to load plugin module: ${ex.toString()}`))
206
230
  process.exit(1)
207
231
  }
208
232
  if (!(mod !== null && typeof mod === "function")) {
209
- console.error(chalk.red(`nunjucks: ERROR: failed to call extension file: ${modpath}`))
233
+ console.error(chalk.red(`nunjucks: ERROR: failed to call plugin file: "${modpath}"`))
210
234
  process.exit(1)
211
235
  }
212
236
  mod(env)
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@rse/nunjucks-cli",
3
3
  "publishConfig": { "access": "public" },
4
- "version": "2.0.0",
5
- "stdver": "2.0.0-GA",
4
+ "version": "2.1.0",
5
+ "stdver": "2.1.0-GA",
6
6
  "description": "Nunjucks Template Rendering Command-Line Interface",
7
7
  "author": {
8
8
  "name": "Dr. Ralf S. Engelschall",
@@ -18,7 +18,7 @@
18
18
  "url": "http://github.com/rse/nunjucks-cli/issues"
19
19
  },
20
20
  "bin": {
21
- "nunjucks": "nunjucks.ts"
21
+ "nunjucks": "nunjucks.js"
22
22
  },
23
23
  "man": "nunjucks.1",
24
24
  "type": "module",
@@ -30,7 +30,9 @@
30
30
  "chalk": "5.6.2",
31
31
  "commander": "14.0.2",
32
32
  "js-yaml": "4.1.1",
33
- "deepmerge": "4.3.1"
33
+ "deepmerge": "4.3.1",
34
+ "@dotenvx/dotenvx": "1.51.2",
35
+ "find-up": "8.0.0"
34
36
  },
35
37
  "devDependencies": {
36
38
  "eslint": "9.39.2",
@@ -44,14 +46,15 @@
44
46
  "remark": "15.0.1",
45
47
  "remark-man": "9.0.0",
46
48
  "typescript": "5.9.3",
47
- "typescript-eslint": "8.50.0",
48
- "@rse/stx": "1.1.3",
49
+ "typescript-eslint": "8.50.1",
50
+ "@rse/stx": "1.1.4",
49
51
  "shx": "0.4.0",
50
52
  "@yao-pkg/pkg": "6.11.0",
51
53
 
52
54
  "@types/node": "25.0.3",
53
55
  "@types/js-yaml": "4.0.9",
54
- "@types/nunjucks": "3.2.6"
56
+ "@types/nunjucks": "3.2.6",
57
+ "@types/find-up": "4.0.2"
55
58
  },
56
59
  "scripts": {
57
60
  "start": "stx -v4 -c stx.conf",
package/stx.conf CHANGED
@@ -7,27 +7,28 @@
7
7
  # static code analysis
8
8
  lint
9
9
  eslint --config eslint.mjs nunjucks.ts && \
10
- tsc --project tsconfig.json
10
+ tsc --noEmit --project tsconfig.json
11
11
 
12
- # build manual page
13
- man
12
+ # build JavaScript from TypeScript
13
+ build
14
+ tsc --project tsconfig.json && \
14
15
  remark --quiet --use remark-man --output nunjucks.1 nunjucks.md
15
16
 
16
17
  # execute simple smole test
17
- test
18
- echo 'Hello, {{who}}!' | node nunjucks.ts -D who=world -
18
+ test : build
19
+ echo 'Hello, {{who}}!' | node nunjucks.js -D who=world -
19
20
 
20
21
  # build all-in-one packages
21
22
  build:pkg [hostname=en4.*]
22
23
 
23
24
  # package distribution archives
24
- package : build:pkg [hostname=en4.*]
25
+ package : build build:pkg [hostname=en4.*]
25
26
  VERSION=`sed -n '/"version":/ s/.*: *"\(.*\)".*/\1/p' package.json` && \
26
27
  targets="node24-linux-x64,node24-linux-arm64" && \
27
28
  targets="$targets,node24-win-x64,node24-win-arm64" && \
28
29
  targets="$targets,node24-macos-x64,node24-macos-arm64" && \
29
30
  sed -e 's;@rse/nunjucks-cli;nunjucks;' <package.json >nunjucks.json && \
30
- pkg --sea --public -c nunjucks.json -t "$targets" nunjucks.ts && \
31
+ pkg --sea --public -c nunjucks.json -t "$targets" nunjucks.js && \
31
32
  rm -f nunjucks.json && \
32
33
  shx mv nunjucks-linux-x64 nunjucks-lnx-x64 && \
33
34
  shx mv nunjucks-linux-arm64 nunjucks-lnx-a64 && \
@@ -42,7 +43,7 @@ package : build:pkg [hostname=en4.*]
42
43
  mkdir -p nunjucks-$VERSION-mac-a64/ && \
43
44
  mkdir -p nunjucks-$VERSION-lnx-x64/ && \
44
45
  mkdir -p nunjucks-$VERSION-lnx-a64/ && \
45
- cp -p nunjucks.ts nunjucks-$VERSION/nunjucks.ts && \
46
+ cp -p nunjucks.js nunjucks-$VERSION/nunjucks.js && \
46
47
  cp -p nunjucks-win-x64.exe nunjucks-$VERSION-win-x64/nunjucks.exe && \
47
48
  cp -p nunjucks-win-a64.exe nunjucks-$VERSION-win-a64/nunjucks.exe && \
48
49
  cp -p nunjucks-mac-x64 nunjucks-$VERSION-mac-x64/nunjucks && \
@@ -81,6 +82,7 @@ publish : package [hostname=en4.*]
81
82
 
82
83
  # remove all generated artifacts
83
84
  clean
85
+ shx rm -f nunjucks.js && \
84
86
  shx rm -rf nunjucks-*.zip
85
87
 
86
88
  # remove all generated artifacts
package/tsconfig.json CHANGED
@@ -5,13 +5,13 @@
5
5
  "lib": [ "ES2024" ],
6
6
  "moduleResolution": "node16",
7
7
  "resolveJsonModule": true,
8
- "allowImportingTsExtensions": true,
8
+ "declaration": false,
9
9
  "allowSyntheticDefaultImports": true,
10
10
  "esModuleInterop": true,
11
11
  "forceConsistentCasingInFileNames": true,
12
12
  "strict": true,
13
13
  "skipLibCheck": true,
14
- "noEmit": true,
14
+ "outDir": ".",
15
15
  "types": [ "node" ]
16
16
  },
17
17
  "include": [