@rhinostone/swig 2.2.0 → 2.4.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/HISTORY.md +14 -0
- package/ROADMAP.md +10 -0
- package/bin/swig.js +275 -88
- package/dist/swig.js +55 -7
- package/dist/swig.min.js +2 -2
- package/dist/swig.min.js.map +1 -1
- package/lib/lexer.js +6 -0
- package/lib/swig.js +4 -4
- package/package.json +3 -4
- package/.changes/unreleased/.gitkeep +0 -0
- package/.changes/v0.1.2.md +0 -6
- package/.changes/v0.1.3.md +0 -6
- package/.changes/v0.1.5.md +0 -11
- package/.changes/v0.1.6.md +0 -6
- package/.changes/v0.1.7.md +0 -7
- package/.changes/v0.1.8.md +0 -7
- package/.changes/v0.1.9.md +0 -7
- package/.changes/v0.10.0.md +0 -11
- package/.changes/v0.11.0.md +0 -15
- package/.changes/v0.11.1.md +0 -6
- package/.changes/v0.11.2.md +0 -6
- package/.changes/v0.12.0.md +0 -7
- package/.changes/v0.12.1.md +0 -11
- package/.changes/v0.13.0.md +0 -8
- package/.changes/v0.13.1.md +0 -8
- package/.changes/v0.13.2.md +0 -9
- package/.changes/v0.13.3.md +0 -5
- package/.changes/v0.13.4.md +0 -5
- package/.changes/v0.13.5.md +0 -4
- package/.changes/v0.14.0.md +0 -6
- package/.changes/v0.2.0.md +0 -7
- package/.changes/v0.2.1.md +0 -6
- package/.changes/v0.2.2.md +0 -6
- package/.changes/v0.2.3.md +0 -10
- package/.changes/v0.3.0.md +0 -6
- package/.changes/v0.4.0.md +0 -11
- package/.changes/v0.5.0.md +0 -11
- package/.changes/v0.6.0.md +0 -12
- package/.changes/v0.6.1.md +0 -6
- package/.changes/v0.7.0.md +0 -7
- package/.changes/v0.8.0.md +0 -18
- package/.changes/v0.9.0.md +0 -13
- package/.changes/v0.9.1.md +0 -6
- package/.changes/v0.9.2.md +0 -6
- package/.changes/v0.9.3.md +0 -6
- package/.changes/v0.9.4.md +0 -10
- package/.changes/v1.0.0-pre1.md +0 -22
- package/.changes/v1.0.0-pre2.md +0 -18
- package/.changes/v1.0.0-pre3.md +0 -7
- package/.changes/v1.0.0-rc1.md +0 -12
- package/.changes/v1.0.0-rc2.md +0 -10
- package/.changes/v1.0.0-rc3.md +0 -7
- package/.changes/v1.0.0.md +0 -7
- package/.changes/v1.1.0.md +0 -6
- package/.changes/v1.2.0.md +0 -10
- package/.changes/v1.2.1.md +0 -4
- package/.changes/v1.2.2.md +0 -4
- package/.changes/v1.3.0.md +0 -14
- package/.changes/v1.3.2.md +0 -6
- package/.changes/v1.4.0.md +0 -13
- package/.changes/v1.4.1.md +0 -5
- package/.changes/v1.4.2.md +0 -10
- package/.changes/v1.4.3.md +0 -4
- package/.changes/v1.4.4.md +0 -6
- package/.changes/v1.4.5.md +0 -10
- package/.changes/v1.4.6.md +0 -6
- package/.changes/v1.4.7.md +0 -8
- package/.changes/v1.5.0.md +0 -4
- package/.changes/v1.6.0.md +0 -4
- package/.changes/v2.0.0-alpha.1.md +0 -4
- package/.changes/v2.0.0-alpha.2.md +0 -4
- package/.changes/v2.0.0-alpha.4.md +0 -10
- package/.changes/v2.0.0-alpha.5.md +0 -16
- package/.changes/v2.0.0-alpha.6.md +0 -4
- package/.changes/v2.0.0-alpha.7.md +0 -4
- package/.changes/v2.0.0-alpha.8.md +0 -4
- package/.changes/v2.0.0.md +0 -6
- package/.changes/v2.0.1.md +0 -8
- package/.changes/v2.1.0.md +0 -8
- package/.changes/v2.2.0.md +0 -8
- package/.playwright-mcp/console-2026-04-22T15-34-20-480Z.log +0 -2
- package/.playwright-mcp/console-2026-04-22T15-35-04-265Z.log +0 -11
- package/.playwright-mcp/console-2026-04-22T15-37-26-953Z.log +0 -1
- package/.playwright-mcp/console-2026-04-22T15-51-15-160Z.log +0 -148
- package/.playwright-mcp/console-2026-04-22T15-51-33-405Z.log +0 -74
- package/.playwright-mcp/console-2026-04-22T15-51-53-922Z.log +0 -74
- package/.playwright-mcp/console-2026-04-22T15-53-10-736Z.log +0 -74
- package/.playwright-mcp/console-2026-04-22T15-53-40-091Z.log +0 -883
- package/.playwright-mcp/console-2026-04-22T16-12-02-541Z.log +0 -74
- package/.playwright-mcp/console-2026-04-22T16-33-44-982Z.log +0 -13973
- package/.playwright-mcp/page-2026-04-22T15-34-24-524Z.yml +0 -1
- package/.playwright-mcp/page-2026-04-22T15-35-04-346Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T15-37-27-039Z.yml +0 -5
- package/.playwright-mcp/page-2026-04-22T15-51-15-600Z.yml +0 -76
- package/.playwright-mcp/page-2026-04-22T15-51-33-605Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T15-51-54-206Z.yml +0 -676
- package/.playwright-mcp/page-2026-04-22T15-53-11-277Z.yml +0 -632
- package/.playwright-mcp/page-2026-04-22T15-53-40-297Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T16-12-02-855Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T16-33-45-281Z.yml +0 -0
package/HISTORY.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
[2.4.0](https://github.com/gina-io/swig/tree/v2.4.0) / 2026-05-15
|
|
2
|
+
-----------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
* **Added** Ternary (`a ? b : c`) and Elvis (`a ?: b`) operator support in template expressions, usable in `{{ }}` output and in tag arguments such as `{% if %}`, `{% set %}`, and `{% for %}`.
|
|
5
|
+
|
|
6
|
+
* **Fixed** `make coverage` now invokes mocha directly via `node node_modules/mocha/bin/_mocha` instead of the `node_modules/.bin/mocha` shim, which exits silently with no output on Node >= 18 — the 95% line-coverage gate is enforced again rather than silently passing.
|
|
7
|
+
|
|
8
|
+
* **Fixed** `swig compile -o <dir>` no longer re-throws `EEXIST` when the output directory already exists. The mkdir guard matched the legacy numeric errno 47, which no longer identifies EEXIST on modern Node; it now matches `e.code === 'EEXIST'`, the stable cross-version identifier.
|
|
9
|
+
|
|
10
|
+
[2.3.0](https://github.com/gina-io/swig/tree/v2.3.0) / 2026-05-14
|
|
11
|
+
-----------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
* **Changed** Drop `yargs` and `terser` from production `dependencies`. CLI argument parsing now uses a small built-in zero-dependency parser; `terser` (used only by `swig compile --minify`) is loaded lazily and moves to `devDependencies`. A library install of `@rhinostone/swig` now pulls in only `@rhinostone/swig-core`.
|
|
14
|
+
|
|
1
15
|
[2.2.0](https://github.com/gina-io/swig/tree/v2.2.0) / 2026-05-11
|
|
2
16
|
-----------------------------------------------------------------
|
|
3
17
|
|
package/ROADMAP.md
CHANGED
|
@@ -22,6 +22,16 @@ _No near-term scheduled items. See [Future (post-2.0)](#future-post-20) for upco
|
|
|
22
22
|
|
|
23
23
|
## Completed
|
|
24
24
|
|
|
25
|
+
### v2.4.0 (May 2026)
|
|
26
|
+
|
|
27
|
+
- Native ternary (`a ? b : c`) and Elvis (`a ?: b`) operator support across `@rhinostone/swig` template expressions. Usable in `{{ }}` output and in tag arguments such as `{% if %}`, `{% set %}`, and `{% for %}`. The ternary's `else` branch is required — `{{ x ? "a" }}` throws `Expected colon in ternary expression`. Backend support already existed via `IRConditional` (exercised by `@rhinostone/swig-twig`); this release wires the native parser to produce it.
|
|
28
|
+
- Fixed `swig compile -o <dir>` re-throwing `EEXIST` when the output directory already exists. The mkdir guard matched the legacy numeric `errno 47`, which no longer identifies `EEXIST` on modern Node; the guard now matches `e.code === 'EEXIST'`, the stable cross-version identifier.
|
|
29
|
+
- Fixed `make coverage cov-reporter=travis-cov` silently no-opping the 95% line-coverage gate on Node >= 18. The `coverage:` target invoked the broken `node_modules/.bin/mocha` shim (which exits 0 with no output regardless of pass/fail); it now invokes mocha directly via `node node_modules/mocha/bin/_mocha`, matching the `test:` target.
|
|
30
|
+
|
|
31
|
+
### v2.3.0 (May 2026)
|
|
32
|
+
|
|
33
|
+
- Reduced `@rhinostone/swig`'s production dependency footprint to a single package. `yargs` and `terser` were CLI-only — the library entry point never loaded them — but sat in production `dependencies`, so every library install pulled in their full dependency trees. The CLI's argument parsing is now handled by a small built-in zero-dependency parser; `terser` (used only by `swig compile --minify`) is loaded lazily and ships as a `devDependency`, with `--minify` printing an install hint instead of crashing if it is absent. A library install of `@rhinostone/swig` now pulls in only `@rhinostone/swig-core`. No change to the CLI surface or rendering behavior.
|
|
34
|
+
|
|
25
35
|
### v2.2.0 (May 2026)
|
|
26
36
|
|
|
27
37
|
- `renderFile(path, locals, cb)` and `compileFile(path, options, cb)` now automatically route to the async-codegen path when the configured loader signals async support via `loader.async === true`. The async path defers template resolution from parse time to render time via a new `_swig.getTemplate(path, options)` runtime helper that returns `Promise<TemplateFn>`; `extends`, `include`, `import`, and `from` emit deferred IR shapes and the shared backend wraps the compiled body in an `AsyncFunction`. Block overrides thread through the inheritance chain via a sixth `_blocks` positional argument; macro imports pick up exports via a `Promise<{output, exports}>` template-fn return shape. Both `@rhinostone/swig` and `@rhinostone/swig-twig` flavors — parity across the two surfaces. Static template targets (string literals in `extends` / `include` / `import` / `from`) work end-to-end against async loaders; dynamic targets surface a clear runtime error and are tracked as a follow-up. The sync render path is unchanged — loaders without `loader.async === true` continue to use the established sync `_swig.compileFile(...)` resolution, including the built-in `loaders.fs` and `loaders.memory` which remain dual-mode.
|
package/bin/swig.js
CHANGED
|
@@ -2,109 +2,73 @@
|
|
|
2
2
|
/*jslint es5: true */
|
|
3
3
|
|
|
4
4
|
var swig = require('../index'),
|
|
5
|
-
yargs = require('yargs'),
|
|
6
5
|
fs = require('fs'),
|
|
7
6
|
path = require('path'),
|
|
8
7
|
filters = require('../lib/filters'),
|
|
9
|
-
utils = require('../lib/utils')
|
|
10
|
-
terser = require('terser');
|
|
11
|
-
|
|
12
|
-
var command,
|
|
13
|
-
wrapstart = 'var tpl = ',
|
|
14
|
-
wrapend = ';',
|
|
15
|
-
argv = yargs
|
|
16
|
-
.usage('\n Usage:\n' +
|
|
17
|
-
' $0 compile [files] [options]\n' +
|
|
18
|
-
' $0 compile --recursive <dir> [options]\n' +
|
|
19
|
-
' $0 run [files] [options]\n' +
|
|
20
|
-
' $0 render [files] [options]\n'
|
|
21
|
-
)
|
|
22
|
-
.describe({
|
|
23
|
-
v: 'Show the Swig version number.',
|
|
24
|
-
o: 'Output location.',
|
|
25
|
-
h: 'Show this help screen.',
|
|
26
|
-
j: 'Variable context as a JSON file.',
|
|
27
|
-
c: 'Variable context as a CommonJS-style file. Used only if option `j` is not provided.',
|
|
28
|
-
m: 'Minify compiled functions with terser',
|
|
29
|
-
r: 'Recursively compile every template in <dir> into a single AOT bundle module.',
|
|
30
|
-
'ext': 'Comma-separated list of file extensions to include when using --recursive (e.g. ".html,.swig"). Defaults to no filter.',
|
|
31
|
-
'filters': 'Custom filters as a CommonJS-style file',
|
|
32
|
-
'tags': 'Custom tags as a CommonJS-style file',
|
|
33
|
-
'options': 'Customize Swig\'s Options from a CommonJS-style file',
|
|
34
|
-
'wrap-start': 'Template wrapper beginning for "compile".',
|
|
35
|
-
'wrap-end': 'Template wrapper end for "compile".',
|
|
36
|
-
'method-name': 'Method name to set template to and run from.'
|
|
37
|
-
})
|
|
38
|
-
.alias('v', 'version')
|
|
39
|
-
.alias('o', 'output')
|
|
40
|
-
.default('o', 'stdout')
|
|
41
|
-
.alias('h', 'help')
|
|
42
|
-
.alias('j', 'json')
|
|
43
|
-
.alias('c', 'context')
|
|
44
|
-
.alias('m', 'minify')
|
|
45
|
-
.alias('r', 'recursive')
|
|
46
|
-
.default('wrap-start', wrapstart)
|
|
47
|
-
.default('wrap-end', wrapend)
|
|
48
|
-
.default('method-name', 'tpl')
|
|
49
|
-
.check(function (argv) {
|
|
50
|
-
if (argv.v) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!argv._.length) {
|
|
55
|
-
throw new Error('');
|
|
56
|
-
}
|
|
8
|
+
utils = require('../lib/utils');
|
|
57
9
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new Error('Unrecognized command "' + command + '". Use -h for help.');
|
|
61
|
-
}
|
|
10
|
+
var wrapstart = 'var tpl = ',
|
|
11
|
+
wrapend = ';';
|
|
62
12
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return true;
|
|
91
|
-
})
|
|
92
|
-
.argv,
|
|
13
|
+
/**
|
|
14
|
+
* CLI flag table. Each entry is keyed by the canonical name the rest of this
|
|
15
|
+
* file reads. <code>alias</code> is the long-form name folded onto that key,
|
|
16
|
+
* <code>boolean</code> marks flags that never consume the following token,
|
|
17
|
+
* and <code>default</code> seeds a value when the flag is absent.
|
|
18
|
+
*
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
var FLAGS = {
|
|
22
|
+
'v': { alias: 'version', boolean: true },
|
|
23
|
+
'o': { alias: 'output', default: 'stdout' },
|
|
24
|
+
'h': { alias: 'help', boolean: true },
|
|
25
|
+
'j': { alias: 'json' },
|
|
26
|
+
'c': { alias: 'context' },
|
|
27
|
+
'm': { alias: 'minify', boolean: true },
|
|
28
|
+
'r': { alias: 'recursive' },
|
|
29
|
+
'ext': {},
|
|
30
|
+
'filters': {},
|
|
31
|
+
'tags': {},
|
|
32
|
+
'options': {},
|
|
33
|
+
'wrap-start': { default: wrapstart },
|
|
34
|
+
'wrap-end': { default: wrapend },
|
|
35
|
+
'method-name': { default: 'tpl' }
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
var argv = parseArgs(process.argv.slice(2)),
|
|
39
|
+
command,
|
|
93
40
|
ctx = {},
|
|
94
41
|
out = function (file, str) {
|
|
95
42
|
console.log(str);
|
|
96
43
|
},
|
|
97
44
|
efn = function () {},
|
|
98
|
-
anonymous,
|
|
99
|
-
files,
|
|
100
45
|
fn;
|
|
101
46
|
|
|
47
|
+
// Show this help screen.
|
|
48
|
+
if (argv.h) {
|
|
49
|
+
console.log(usage());
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
102
53
|
// What version?
|
|
103
54
|
if (argv.v) {
|
|
104
55
|
console.log(require('../package').version);
|
|
105
56
|
process.exit(0);
|
|
106
57
|
}
|
|
107
58
|
|
|
59
|
+
// Validate the parsed arguments and resolve the subcommand. On any invalid
|
|
60
|
+
// combination, print the message (when there is one) plus the usage screen
|
|
61
|
+
// and exit non-zero — the behaviour the former yargs `.check()` gate had.
|
|
62
|
+
try {
|
|
63
|
+
command = validate(argv);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
if (e.message) {
|
|
66
|
+
console.error(e.message);
|
|
67
|
+
}
|
|
68
|
+
console.error(usage());
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
108
72
|
// Pull in any context data provided
|
|
109
73
|
if (argv.j) {
|
|
110
74
|
ctx = JSON.parse(fs.readFileSync(argv.j, 'utf8'));
|
|
@@ -119,7 +83,9 @@ if (argv.o !== 'stdout' && !argv.r) {
|
|
|
119
83
|
try {
|
|
120
84
|
fs.mkdirSync(argv.o);
|
|
121
85
|
} catch (e) {
|
|
122
|
-
|
|
86
|
+
// EEXIST (output dir already exists) is expected. Match on e.code; the
|
|
87
|
+
// legacy numeric errno 47 no longer identifies EEXIST on modern Node.
|
|
88
|
+
if (e.code !== 'EEXIST') {
|
|
123
89
|
throw e;
|
|
124
90
|
}
|
|
125
91
|
}
|
|
@@ -158,7 +124,7 @@ case 'compile':
|
|
|
158
124
|
r = argv['wrap-start'] + r + argv['wrap-end'];
|
|
159
125
|
|
|
160
126
|
if (argv.m) {
|
|
161
|
-
r =
|
|
127
|
+
r = loadTerser().minify_sync(r).code;
|
|
162
128
|
}
|
|
163
129
|
|
|
164
130
|
out(file, r);
|
|
@@ -191,6 +157,227 @@ if (argv.r) {
|
|
|
191
157
|
});
|
|
192
158
|
}
|
|
193
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Parse a <code>process.argv</code> tail into a flag / positional map.
|
|
162
|
+
* Supports <code>--name</code>, <code>--name=value</code>,
|
|
163
|
+
* <code>--name value</code>, <code>-x</code>, <code>-x value</code>,
|
|
164
|
+
* <code>-x=value</code>, and a <code>--</code> terminator after which every
|
|
165
|
+
* token is positional. Long-form aliases are folded onto their canonical
|
|
166
|
+
* {@link FLAGS} key; boolean flags never consume the following token. Bare
|
|
167
|
+
* tokens collect into <code>argv._</code>. Unknown flags are tolerated and
|
|
168
|
+
* kept verbatim, matching the former non-strict yargs behaviour.
|
|
169
|
+
*
|
|
170
|
+
* @param {string[]} args <code>process.argv.slice(2)</code>.
|
|
171
|
+
* @return {object} <code>{ _: [positionals], <flag>: value }</code>.
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
function parseArgs(args) {
|
|
175
|
+
var argv = { _: [] },
|
|
176
|
+
i,
|
|
177
|
+
token,
|
|
178
|
+
body,
|
|
179
|
+
eq,
|
|
180
|
+
name,
|
|
181
|
+
canonical,
|
|
182
|
+
value,
|
|
183
|
+
key;
|
|
184
|
+
|
|
185
|
+
for (i = 0; i < args.length; i += 1) {
|
|
186
|
+
token = args[i];
|
|
187
|
+
|
|
188
|
+
if (token === '--') {
|
|
189
|
+
argv._ = argv._.concat(args.slice(i + 1));
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (token.slice(0, 2) === '--') {
|
|
194
|
+
body = token.slice(2);
|
|
195
|
+
} else if (token.charAt(0) === '-' && token.length > 1) {
|
|
196
|
+
body = token.slice(1);
|
|
197
|
+
} else {
|
|
198
|
+
argv._.push(token);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
eq = body.indexOf('=');
|
|
203
|
+
if (eq !== -1) {
|
|
204
|
+
name = body.slice(0, eq);
|
|
205
|
+
value = body.slice(eq + 1);
|
|
206
|
+
} else {
|
|
207
|
+
name = body;
|
|
208
|
+
value = undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
canonical = aliasToCanonical(name);
|
|
212
|
+
|
|
213
|
+
if (value === undefined) {
|
|
214
|
+
if (FLAGS[canonical] && FLAGS[canonical].boolean) {
|
|
215
|
+
value = true;
|
|
216
|
+
} else if (i + 1 < args.length && !isFlagToken(args[i + 1])) {
|
|
217
|
+
i += 1;
|
|
218
|
+
value = args[i];
|
|
219
|
+
} else {
|
|
220
|
+
value = true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
argv[canonical] = value;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Seed defaults for any flag the caller did not pass.
|
|
228
|
+
for (key in FLAGS) {
|
|
229
|
+
if (FLAGS.hasOwnProperty(key) && FLAGS[key].hasOwnProperty('default') && !argv.hasOwnProperty(key)) {
|
|
230
|
+
argv[key] = FLAGS[key].default;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return argv;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Resolve a flag name to its canonical {@link FLAGS} key. A name that is
|
|
239
|
+
* already canonical, or that matches no known alias, is returned unchanged.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} name Flag name as written on the command line.
|
|
242
|
+
* @return {string} Canonical key.
|
|
243
|
+
* @private
|
|
244
|
+
*/
|
|
245
|
+
function aliasToCanonical(name) {
|
|
246
|
+
var key;
|
|
247
|
+
if (FLAGS.hasOwnProperty(name)) {
|
|
248
|
+
return name;
|
|
249
|
+
}
|
|
250
|
+
for (key in FLAGS) {
|
|
251
|
+
if (FLAGS.hasOwnProperty(key) && FLAGS[key].alias === name) {
|
|
252
|
+
return key;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return name;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Is <var>token</var> a flag rather than a value? Used to decide whether a
|
|
260
|
+
* value-taking flag should consume the next token.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} token Candidate token.
|
|
263
|
+
* @return {boolean} True when the token looks like a flag.
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
function isFlagToken(token) {
|
|
267
|
+
return token.charAt(0) === '-' && token.length > 1;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Validate the parsed arguments and resolve the subcommand. Mirrors the gate
|
|
272
|
+
* the former yargs <code>.check()</code> chain enforced — same checks, same
|
|
273
|
+
* messages, same <code>--method-name</code> → <code>--wrap-start</code>
|
|
274
|
+
* rewrite. Throws on any invalid combination; the caller prints the message
|
|
275
|
+
* plus the usage screen and exits non-zero.
|
|
276
|
+
*
|
|
277
|
+
* @param {object} argv Parsed argv from {@link parseArgs}.
|
|
278
|
+
* @return {string} The resolved subcommand: compile, render, or run.
|
|
279
|
+
* @private
|
|
280
|
+
*/
|
|
281
|
+
function validate(argv) {
|
|
282
|
+
var cmd;
|
|
283
|
+
|
|
284
|
+
if (!argv._.length) {
|
|
285
|
+
throw new Error('');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
cmd = argv._.shift();
|
|
289
|
+
if (cmd !== 'compile' && cmd !== 'render' && cmd !== 'run') {
|
|
290
|
+
throw new Error('Unrecognized command "' + cmd + '". Use -h for help.');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (argv['method-name'] !== 'tpl' && argv['wrap-start'] !== wrapstart) {
|
|
294
|
+
throw new Error('Cannot use arguments "--method-name" and "--wrap-start" together.');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (argv['method-name'] !== 'tpl') {
|
|
298
|
+
argv['wrap-start'] = 'var ' + argv['method-name'] + ' = ';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (argv.r) {
|
|
302
|
+
if (cmd !== 'compile') {
|
|
303
|
+
throw new Error('--recursive can only be used with "compile".');
|
|
304
|
+
}
|
|
305
|
+
if (argv._.length) {
|
|
306
|
+
throw new Error('--recursive does not accept positional file arguments; pass a single directory via --recursive <dir>.');
|
|
307
|
+
}
|
|
308
|
+
if (argv['method-name'] !== 'tpl') {
|
|
309
|
+
throw new Error('--recursive cannot be combined with --method-name; the bundle exports a map of templates, not a single named function.');
|
|
310
|
+
}
|
|
311
|
+
if (argv['wrap-start'] !== wrapstart || argv['wrap-end'] !== wrapend) {
|
|
312
|
+
throw new Error('--recursive cannot be combined with --wrap-start / --wrap-end; the bundle wrapper is fixed.');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (argv.ext && !argv.r) {
|
|
317
|
+
throw new Error('--ext is only meaningful with --recursive.');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return cmd;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Build the CLI usage screen — the command synopsis plus the option table.
|
|
325
|
+
*
|
|
326
|
+
* @return {string} The full usage text.
|
|
327
|
+
* @private
|
|
328
|
+
*/
|
|
329
|
+
function usage() {
|
|
330
|
+
return [
|
|
331
|
+
'',
|
|
332
|
+
' Usage:',
|
|
333
|
+
' swig compile [files] [options]',
|
|
334
|
+
' swig compile --recursive <dir> [options]',
|
|
335
|
+
' swig run [files] [options]',
|
|
336
|
+
' swig render [files] [options]',
|
|
337
|
+
'',
|
|
338
|
+
' Options:',
|
|
339
|
+
' -v, --version Show the Swig version number.',
|
|
340
|
+
' -o, --output Output location.',
|
|
341
|
+
' -h, --help Show this help screen.',
|
|
342
|
+
' -j, --json Variable context as a JSON file.',
|
|
343
|
+
' -c, --context Variable context as a CommonJS-style file. Used only if option `j` is not provided.',
|
|
344
|
+
' -m, --minify Minify compiled functions with terser',
|
|
345
|
+
' -r, --recursive Recursively compile every template in <dir> into a single AOT bundle module.',
|
|
346
|
+
' --ext Comma-separated list of file extensions to include when using --recursive (e.g. ".html,.swig"). Defaults to no filter.',
|
|
347
|
+
' --filters Custom filters as a CommonJS-style file',
|
|
348
|
+
' --tags Custom tags as a CommonJS-style file',
|
|
349
|
+
' --options Customize Swig\'s Options from a CommonJS-style file',
|
|
350
|
+
' --wrap-start Template wrapper beginning for "compile".',
|
|
351
|
+
' --wrap-end Template wrapper end for "compile".',
|
|
352
|
+
' --method-name Method name to set template to and run from.',
|
|
353
|
+
''
|
|
354
|
+
].join('\n');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Lazily load the optional <code>terser</code> package, used only by the
|
|
359
|
+
* <code>--minify</code> flag. terser is a CLI-only dependency — the library
|
|
360
|
+
* entry point never needs it — so it ships as a devDependency rather than a
|
|
361
|
+
* runtime one, and a plain <code>npm install @rhinostone/swig</code> does not
|
|
362
|
+
* pull it in. Print a friendly install hint and exit non-zero if a CLI user
|
|
363
|
+
* reaches <code>--minify</code> without it.
|
|
364
|
+
*
|
|
365
|
+
* @return {object} The terser module.
|
|
366
|
+
* @private
|
|
367
|
+
*/
|
|
368
|
+
function loadTerser() {
|
|
369
|
+
try {
|
|
370
|
+
return require('terser');
|
|
371
|
+
} catch (e) {
|
|
372
|
+
if (e.code !== 'MODULE_NOT_FOUND') {
|
|
373
|
+
throw e;
|
|
374
|
+
}
|
|
375
|
+
console.error('The --minify flag needs the "terser" package, which is not installed.');
|
|
376
|
+
console.error('Install it with: npm install terser');
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
194
381
|
/**
|
|
195
382
|
* Walk a directory recursively, returning every regular-file path found.
|
|
196
383
|
* Skips dotfile entries and dot-directories so platform metadata such as
|
|
@@ -268,7 +455,7 @@ function bundleRecursive(dir) {
|
|
|
268
455
|
output = 'module.exports = {\n' + parts.join(',\n') + '\n};\n';
|
|
269
456
|
|
|
270
457
|
if (argv.m) {
|
|
271
|
-
output =
|
|
458
|
+
output = loadTerser().minify_sync(output).code;
|
|
272
459
|
}
|
|
273
460
|
|
|
274
461
|
if (argv.o === 'stdout') {
|
package/dist/swig.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Swig v2.
|
|
1
|
+
/*! Swig v2.4.0 | https://github.com/gina-io/swig | @license https://github.com/gina-io/swig/blob/master/LICENSE */
|
|
2
2
|
/*! DateZ (c) 2011 Tomo Universalis | @license https://github.com/ocrybit/DateZ/blob/master/LISENCE */
|
|
3
3
|
(() => {
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -498,6 +498,8 @@
|
|
|
498
498
|
/** End of a method
|
|
499
499
|
* Currently unused
|
|
500
500
|
METHODEND: 26, */
|
|
501
|
+
/** Ternary question mark (?) */
|
|
502
|
+
QMARK: 27,
|
|
501
503
|
/** Unknown type */
|
|
502
504
|
UNKNOWN: 100
|
|
503
505
|
};
|
|
@@ -2002,6 +2004,12 @@
|
|
|
2002
2004
|
/^\d+(\.\d+)?/
|
|
2003
2005
|
]
|
|
2004
2006
|
},
|
|
2007
|
+
{
|
|
2008
|
+
type: TYPES.QMARK,
|
|
2009
|
+
regex: [
|
|
2010
|
+
/^\?/
|
|
2011
|
+
]
|
|
2012
|
+
},
|
|
2005
2013
|
{
|
|
2006
2014
|
type: TYPES.OPERATOR,
|
|
2007
2015
|
regex: [
|
|
@@ -2265,12 +2273,25 @@
|
|
|
2265
2273
|
self.out.push("{");
|
|
2266
2274
|
self.filterApplyIdx.push(self.out.length - 1);
|
|
2267
2275
|
break;
|
|
2276
|
+
case _t.QMARK:
|
|
2277
|
+
self.out.push(" ? ");
|
|
2278
|
+
self.state.push(token.type);
|
|
2279
|
+
self.filterApplyIdx.pop();
|
|
2280
|
+
break;
|
|
2268
2281
|
case _t.COLON:
|
|
2269
|
-
if (lastState
|
|
2282
|
+
if (lastState === _t.CURLYOPEN) {
|
|
2283
|
+
self.state.push(token.type);
|
|
2284
|
+
self.out.push(":");
|
|
2285
|
+
} else if (lastState === _t.QMARK) {
|
|
2286
|
+
self.state.pop();
|
|
2287
|
+
if (self.out[self.out.length - 1] === " ? ") {
|
|
2288
|
+
self.out[self.out.length - 1] = " || ";
|
|
2289
|
+
} else {
|
|
2290
|
+
self.out.push(" : ");
|
|
2291
|
+
}
|
|
2292
|
+
} else {
|
|
2270
2293
|
utils.throwError("Unexpected colon", self.line, self.filename);
|
|
2271
2294
|
}
|
|
2272
|
-
self.state.push(token.type);
|
|
2273
|
-
self.out.push(":");
|
|
2274
2295
|
self.filterApplyIdx.pop();
|
|
2275
2296
|
break;
|
|
2276
2297
|
case _t.CURLYCLOSE:
|
|
@@ -2610,6 +2631,27 @@
|
|
|
2610
2631
|
var right = parseExpression(info.prec + 1);
|
|
2611
2632
|
left = ir.binaryOp(info.op, left, right);
|
|
2612
2633
|
}
|
|
2634
|
+
if (minPrec === 0) {
|
|
2635
|
+
var qtok = peek();
|
|
2636
|
+
if (qtok && qtok.type === _t.QMARK) {
|
|
2637
|
+
consume();
|
|
2638
|
+
var afterQ = peek();
|
|
2639
|
+
var elseBranch;
|
|
2640
|
+
if (afterQ && afterQ.type === _t.COLON) {
|
|
2641
|
+
consume();
|
|
2642
|
+
elseBranch = parseExpression(0);
|
|
2643
|
+
left = ir.conditional(left, left, elseBranch);
|
|
2644
|
+
} else {
|
|
2645
|
+
var thenBranch = parseExpression(0);
|
|
2646
|
+
var colon = consume();
|
|
2647
|
+
if (!colon || colon.type !== _t.COLON) {
|
|
2648
|
+
bail("Expected colon in ternary expression");
|
|
2649
|
+
}
|
|
2650
|
+
elseBranch = parseExpression(0);
|
|
2651
|
+
left = ir.conditional(left, thenBranch, elseBranch);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2613
2655
|
return left;
|
|
2614
2656
|
}
|
|
2615
2657
|
var result = parseExpression(0);
|
|
@@ -2670,7 +2712,7 @@
|
|
|
2670
2712
|
if (typeof self.escape === "string") {
|
|
2671
2713
|
return legacyFallback();
|
|
2672
2714
|
}
|
|
2673
|
-
var depth = 0, hasTopOp = false, hasTopFilter = false, firstTopFilterIdx = -1;
|
|
2715
|
+
var depth = 0, hasTopOp = false, hasTopFilter = false, hasTopTernary = false, firstTopFilterIdx = -1;
|
|
2674
2716
|
for (i = 0; i < tokens.length; i += 1) {
|
|
2675
2717
|
t = tokens[i];
|
|
2676
2718
|
if (depth === 0) {
|
|
@@ -2683,6 +2725,9 @@
|
|
|
2683
2725
|
firstTopFilterIdx = i;
|
|
2684
2726
|
}
|
|
2685
2727
|
}
|
|
2728
|
+
if (t.type === _t.QMARK) {
|
|
2729
|
+
hasTopTernary = true;
|
|
2730
|
+
}
|
|
2686
2731
|
}
|
|
2687
2732
|
if (t.type === _t.PARENOPEN || t.type === _t.FUNCTION || t.type === _t.BRACKETOPEN || t.type === _t.CURLYOPEN || t.type === _t.FILTER) {
|
|
2688
2733
|
depth += 1;
|
|
@@ -2715,7 +2760,7 @@
|
|
|
2715
2760
|
}
|
|
2716
2761
|
}
|
|
2717
2762
|
}
|
|
2718
|
-
if (hasTopOp && hasTopFilter) {
|
|
2763
|
+
if (hasTopTernary || hasTopOp && hasTopFilter) {
|
|
2719
2764
|
var exprPO = self.parseExpr(tokens);
|
|
2720
2765
|
var fcallsPO = escape ? [ir.filterCall("e")] : [];
|
|
2721
2766
|
return ir.output(exprPO, fcallsPO.length > 0 ? fcallsPO : void 0);
|
|
@@ -2814,6 +2859,9 @@
|
|
|
2814
2859
|
}
|
|
2815
2860
|
return ir.output(expr, filterCalls.length > 0 ? filterCalls : void 0);
|
|
2816
2861
|
} catch (e) {
|
|
2862
|
+
if (hasTopTernary) {
|
|
2863
|
+
throw e;
|
|
2864
|
+
}
|
|
2817
2865
|
return legacyFallback();
|
|
2818
2866
|
}
|
|
2819
2867
|
},
|
|
@@ -4513,7 +4561,7 @@
|
|
|
4513
4561
|
var loaders = require_loaders2();
|
|
4514
4562
|
var preWalker = require_pre_walker();
|
|
4515
4563
|
var engine = require_engine();
|
|
4516
|
-
exports.version = "2.
|
|
4564
|
+
exports.version = "2.4.0";
|
|
4517
4565
|
var defaultOptions = {
|
|
4518
4566
|
autoescape: true,
|
|
4519
4567
|
varControls: ["{{", "}}"],
|