@rse/nunjucks-cli 2.1.0 → 2.2.1

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,18 @@
2
2
  ChangeLog
3
3
  =========
4
4
 
5
+ 2.2.1 (2025-12-26)
6
+ ------------------
7
+
8
+ - CLEANUP: cleanups
9
+
10
+ 2.2.0 (2025-12-26)
11
+ ------------------
12
+
13
+ - IMPROVEMENT: finally fix SEA packaging
14
+ - IMPROVEMENT: provide Docker container publishing
15
+ - CLEANUP: cleanup distribution archive packaging
16
+
5
17
  2.1.0 (2025-12-26)
6
18
  ------------------
7
19
 
package/Dockerfile CHANGED
@@ -3,7 +3,7 @@
3
3
  ##
4
4
 
5
5
  # build arguments (early)
6
- ARG IMAGE_PREFIX=docker.io/engelschall/
6
+ ARG IMAGE_PREFIX=ghcr.io/rse/
7
7
  ARG IMAGE_NAME=nunjucks-cli
8
8
  ARG IMAGE_VERSION=2.1.0
9
9
  ARG IMAGE_RELEASE=20251226
@@ -12,6 +12,10 @@ ARG IMAGE_ALIAS=latest
12
12
  # derive image from a certain base image
13
13
  FROM node:24-alpine3.23
14
14
 
15
+ # link to Github repository
16
+ LABEL org.opencontainers.image.source=https://github.com/rse/nunjucks-cli
17
+ LABEL org.opencontainers.image.description="Nunjucks Template Rendering Command-Line Interface"
18
+
15
19
  # add additional build tools
16
20
  RUN apk update && \
17
21
  apk upgrade
@@ -27,9 +31,7 @@ WORKDIR /app
27
31
  ENV HOME=/app
28
32
 
29
33
  # install tool
30
- RUN npm install -g \
31
- @rse/nunjucks-cli@1.5.2 \
32
- @rse/nunjucks-addons@1.0.8
34
+ RUN npm install -g @rse/nunjucks-cli
33
35
 
34
36
  # cleanup Alpine
35
37
  RUN rm -rf /var/cache/apk/*
package/README.md CHANGED
@@ -48,6 +48,14 @@ $ npx --yes --package @rse/nunjucks-cli --package @rse/nunjucks-addons -- \
48
48
  nunjucks -e @rse/nunjucks-addons [...]
49
49
  ```
50
50
 
51
+ Furthermore, instead of using NPM at all, you can also use Docker (at
52
+ least as long as you are working with stdin/stdout or perform the
53
+ corresponding bind-mounts):
54
+
55
+ ```sh
56
+ $ docker run -i --rm ghcr.io/rse/nunjucks-cli [...] -
57
+ ```
58
+
51
59
  Command-Line Interface (CLI)
52
60
  ----------------------------
53
61
 
@@ -57,6 +65,8 @@ Short excerpt of the CLI options and arguments from the companion [Unix manpage]
57
65
  $ nunjucks
58
66
  [-h|--help]
59
67
  [-V|--version]
68
+ [-e|--env <env-file>]
69
+ [-E|--envs]
60
70
  [-c|--config <config-file>]
61
71
  [-C|--option <key>=<value>]
62
72
  [-d|--defines <context-file>]
@@ -70,6 +80,12 @@ $ nunjucks
70
80
  Show usage help.
71
81
  - `-V`|`--version`:<br/>
72
82
  Show program version information.
83
+ - `-e`|`--env` *env-file*:<br/>
84
+ Load environment file with key/value definitions.
85
+ These can later be accessed with the global `env` variable.
86
+ - [`-E`|`--envs`]:<br/>
87
+ Automatically load environment files with key/value definitions
88
+ from all `.env` files in current and all parent directories.
73
89
  - `-c`|`--config` `<config-file>`:<br/>
74
90
  Load Nunjucks configuration YAML file.
75
91
  - `-C`|`--option` `<key>=<value>`:<br/>
@@ -0,0 +1,213 @@
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
+ /* internal requirements */
20
+ import pkg from "./package.json" with { type: "json" };
21
+ (async () => {
22
+ /* load my own information */
23
+ const my = pkg;
24
+ /* parse command-line arguments */
25
+ const program = new Command();
26
+ const reduceArray = (v, l) => l.concat([v]);
27
+ program.name("nunjucks")
28
+ .description("Nunjucks Template Rendering Command-Line Interface")
29
+ .showHelpAfterError("hint: use option --help for usage information")
30
+ .option("-h, --help", "show usage help", false)
31
+ .option("-V, --version", "show program version information", false)
32
+ .option("-e, --env <env-file>", "load environment key/value file", reduceArray, [])
33
+ .option("-E, --envs", "load all environment key/value files", false)
34
+ .option("-c, --config <config-file>", "load Nunjucks configuration YAML file", "")
35
+ .option("-C, --option <key>=<value>", "set Nunjucks configuration option", reduceArray, [])
36
+ .option("-d, --defines <context-file>", "load context definition YAML file", reduceArray, [])
37
+ .option("-D, --define <key>=<value>", "set context definition key/value", reduceArray, [])
38
+ .option("-p, --plugin <module-name>", "load Nunjucks JavaScript plugin module", reduceArray, [])
39
+ .option("-o, --output <output-file>", "save output file", "-")
40
+ .argument("[<input-file>]", "input file");
41
+ program.parse(process.argv);
42
+ const argv = {
43
+ ...program.opts(),
44
+ _: program.args
45
+ };
46
+ /* handle special help request */
47
+ if (argv.help) {
48
+ console.log(program.helpInformation());
49
+ console.log("Example:\n $ echo \"Hello, {{ who }}!\" | nunjucks -Dwho=World -\n");
50
+ process.exit(0);
51
+ }
52
+ /* handle special version request */
53
+ if (argv.version) {
54
+ console.log(`${my.name} ${my.version} (Node.js ${process.versions.node}, Nunjucks: ${my.dependencies.nunjucks})`);
55
+ console.log(`${my.description}`);
56
+ console.log(`Copyright (c) 2019-2025 ${my.author.name} <${my.author.url}>`);
57
+ console.log(`Licensed under ${my.license} <http://spdx.org/licenses/${my.license}.html>`);
58
+ process.exit(0);
59
+ }
60
+ /* read input file */
61
+ let input = "";
62
+ if (argv._.length > 1) {
63
+ console.error(chalk.red("nunjucks: ERROR: invalid number of arguments (zero or one input file expected)"));
64
+ process.exit(1);
65
+ }
66
+ let inputFile = argv._[0] ?? "-";
67
+ if (inputFile === "-") {
68
+ inputFile = "<stdin>";
69
+ process.stdin.setEncoding("utf-8");
70
+ const BUFSIZE = 256;
71
+ const buf = Buffer.alloc(BUFSIZE);
72
+ while (true) {
73
+ let bytesRead = 0;
74
+ try {
75
+ bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, null);
76
+ }
77
+ catch (ex) {
78
+ if (ex.code === "EAGAIN")
79
+ continue;
80
+ else if (ex.code === "EOF")
81
+ break;
82
+ else
83
+ throw ex;
84
+ }
85
+ if (bytesRead === 0)
86
+ break;
87
+ input += buf.toString("utf8", 0, bytesRead);
88
+ }
89
+ }
90
+ else {
91
+ if (!fs.existsSync(inputFile)) {
92
+ console.error(chalk.red(`nunjucks: ERROR: failed to find input file: "${inputFile}"`));
93
+ process.exit(1);
94
+ }
95
+ input = fs.readFileSync(inputFile, { encoding: "utf8" });
96
+ }
97
+ /* provide context variables for template */
98
+ let context = {};
99
+ for (const define of argv.defines) {
100
+ try {
101
+ context = deepmerge(context, jsYAML.load(fs.readFileSync(define, { encoding: "utf8" })));
102
+ }
103
+ catch (ex) {
104
+ console.error(chalk.red(`nunjucks: ERROR: failed to load context YAML file: ${ex.toString()}`));
105
+ process.exit(1);
106
+ }
107
+ }
108
+ /* load environment variables from all default files */
109
+ if (argv.envs) {
110
+ const files = findup.findUpMultipleSync(".env");
111
+ if (files.length > 0)
112
+ dotenvx.config({ path: files, quiet: true });
113
+ }
114
+ /* load environment variables from environment files */
115
+ if (argv.env.length > 0) {
116
+ for (const env of argv.env) {
117
+ if (!fs.existsSync(env)) {
118
+ console.error(chalk.red(`nunjucks: ERROR: environment file not found: "${env}"`));
119
+ process.exit(1);
120
+ }
121
+ }
122
+ dotenvx.config({ path: argv.env, quiet: true });
123
+ }
124
+ /* expose environment variables to template */
125
+ context.env = process.env;
126
+ /* add context defines */
127
+ argv.define.forEach((define) => {
128
+ const match = define.match(/^([^=]+)(?:=(.*))?$/);
129
+ if (!match)
130
+ return;
131
+ let [, key, val] = match;
132
+ if (!key)
133
+ return;
134
+ if (val === undefined)
135
+ val = "true";
136
+ context[key] = val;
137
+ });
138
+ /* determine Nunjucks options */
139
+ let options = {};
140
+ if (argv.config) {
141
+ try {
142
+ options = jsYAML.load(fs.readFileSync(argv.config, { encoding: "utf8" }));
143
+ }
144
+ catch (ex) {
145
+ console.error(chalk.red(`nunjucks: ERROR: failed to load options YAML file: ${ex.toString()}`));
146
+ process.exit(1);
147
+ }
148
+ }
149
+ if (argv.option.length > 0)
150
+ options = Object.assign(options, argv.option);
151
+ options = {
152
+ autoescape: false,
153
+ throwOnUndefined: false,
154
+ trimBlocks: true,
155
+ lstripBlocks: true,
156
+ watch: false,
157
+ noCache: true,
158
+ ...options
159
+ };
160
+ /* configure environment */
161
+ const env = nunjucks.configure(inputFile, options);
162
+ /* load external plugin modules */
163
+ for (const plugin of argv.plugin) {
164
+ let modpath = path.resolve(plugin);
165
+ if (!fs.existsSync(modpath)) {
166
+ try {
167
+ const require = createRequire(import.meta.url);
168
+ modpath = require.resolve(plugin);
169
+ }
170
+ catch (_ex) {
171
+ modpath = null;
172
+ }
173
+ }
174
+ if (modpath === null) {
175
+ console.error(chalk.red(`nunjucks: ERROR: failed to find plugin module: ${plugin}`));
176
+ process.exit(1);
177
+ }
178
+ /* dynamically import the module */
179
+ let mod;
180
+ try {
181
+ mod = await import(modpath);
182
+ /* handle both default and named exports */
183
+ mod = mod.default ?? mod;
184
+ }
185
+ catch (ex) {
186
+ console.error(chalk.red(`nunjucks: ERROR: failed to load plugin module: ${ex.toString()}`));
187
+ process.exit(1);
188
+ }
189
+ if (!(mod !== null && typeof mod === "function")) {
190
+ console.error(chalk.red(`nunjucks: ERROR: failed to call plugin file: "${modpath}"`));
191
+ process.exit(1);
192
+ }
193
+ mod(env);
194
+ }
195
+ /* render Nunjucks template */
196
+ let output;
197
+ try {
198
+ output = env.renderString(input, context);
199
+ }
200
+ catch (ex) {
201
+ console.error(chalk.red(`nunjucks: ERROR: failed to render template: ${ex.toString()}`));
202
+ process.exit(1);
203
+ }
204
+ /* write output */
205
+ if (argv.output === "-")
206
+ process.stdout.write(output);
207
+ else
208
+ fs.writeFileSync(argv.output, output, { encoding: "utf8" });
209
+ /* die gracefully */
210
+ process.exit(0);
211
+ })().catch((err) => {
212
+ console.error(chalk.red(`nunjucks: ERROR: ${err.toString()}`));
213
+ });
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@rse/nunjucks-cli",
3
+ "publishConfig": { "access": "public" },
4
+ "version": "2.2.0",
5
+ "stdver": "2.2.0-GA",
6
+ "description": "Nunjucks Template Rendering Command-Line Interface",
7
+ "author": {
8
+ "name": "Dr. Ralf S. Engelschall",
9
+ "email": "rse@engelschall.com",
10
+ "url": "http://engelschall.com"
11
+ },
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/rse/nunjucks-cli.git"
16
+ },
17
+ "bugs": {
18
+ "url": "http://github.com/rse/nunjucks-cli/issues"
19
+ },
20
+ "bin": {
21
+ "nunjucks": "dst-stage1/nunjucks.js"
22
+ },
23
+ "man": "dst-stage1/nunjucks.1",
24
+ "type": "module",
25
+ "engines": {
26
+ "node": ">=22.18.0"
27
+ },
28
+ "dependencies": {
29
+ "nunjucks": "3.2.4",
30
+ "chalk": "5.6.2",
31
+ "commander": "14.0.2",
32
+ "js-yaml": "4.1.1",
33
+ "deepmerge": "4.3.1",
34
+ "@dotenvx/dotenvx": "1.51.2",
35
+ "find-up": "8.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "eslint": "9.39.2",
39
+ "@eslint/js": "9.39.2",
40
+ "neostandard": "0.12.2",
41
+ "eslint-plugin-promise": "7.2.1",
42
+ "eslint-plugin-import": "2.32.0",
43
+ "eslint-plugin-n": "17.23.1",
44
+ "globals": "16.5.0",
45
+ "remark-cli": "12.0.1",
46
+ "remark": "15.0.1",
47
+ "remark-man": "9.0.0",
48
+ "typescript": "5.9.3",
49
+ "typescript-eslint": "8.50.1",
50
+ "@rse/stx": "1.1.4",
51
+ "shx": "0.4.0",
52
+ "@yao-pkg/pkg": "6.11.0",
53
+ "vite": "7.3.0",
54
+ "@wroud/vite-plugin-tsc": "0.12.2",
55
+ "rollup-plugin-node-externals": "8.1.2",
56
+ "terser": "5.44.1",
57
+ "@types/node": "25.0.3",
58
+ "@types/js-yaml": "4.0.9",
59
+ "@types/nunjucks": "3.2.6"
60
+ },
61
+ "scripts": {
62
+ "start": "stx -v4 -c stx.conf",
63
+ "test": "npm start test"
64
+ }
65
+ }
@@ -0,0 +1 @@
1
+ {"root":["../nunjucks.ts"],"version":"5.9.3"}