@rhinostone/swig-twig 2.0.0-alpha.3 → 2.0.0-alpha.4
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/lib/index.js +171 -28
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -1,66 +1,209 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @rhinostone/swig-twig — Twig frontend for the @rhinostone/swig family.
|
|
3
3
|
*
|
|
4
|
-
* Phase 3
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* parse time rather than registering through the runtime setTag
|
|
9
|
-
* extension point.
|
|
4
|
+
* Phase 3 Session 17: end-to-end render wiring (Path A). The package now
|
|
5
|
+
* exposes a Twig constructor + default instance via `engine.install(self,
|
|
6
|
+
* frontend)` from @rhinostone/swig-core, so callers can `render(source,
|
|
7
|
+
* locals)` / `renderFile(path, locals, cb)` directly against Twig syntax.
|
|
10
8
|
*
|
|
11
|
-
* See .claude/architecture/multi-flavor-ir.md § Phase 3 for the
|
|
12
|
-
*
|
|
9
|
+
* See .claude/architecture/multi-flavor-ir.md § Phase 3 for the per-flavor
|
|
10
|
+
* split decision.
|
|
13
11
|
*/
|
|
14
12
|
|
|
13
|
+
var utils = require('@rhinostone/swig-core/lib/utils'),
|
|
14
|
+
engine = require('@rhinostone/swig-core/lib/engine'),
|
|
15
|
+
loaders = require('@rhinostone/swig-core/lib/loaders'),
|
|
16
|
+
dateformatter = require('@rhinostone/swig-core/lib/dateformatter'),
|
|
17
|
+
parser = require('./parser'),
|
|
18
|
+
_tags = require('./tags'),
|
|
19
|
+
_filters = require('./filters');
|
|
20
|
+
|
|
15
21
|
exports.name = 'twig';
|
|
16
22
|
|
|
17
23
|
/**
|
|
18
24
|
* Expression-level parser — Pratt-style recursive descent that consumes
|
|
19
25
|
* Twig lexer tokens and returns swig-core IRExpr nodes.
|
|
20
26
|
*
|
|
21
|
-
* Exposed here so callers can import it from the package entry-point;
|
|
22
|
-
* NOT wired into parse(source) yet (that still throws).
|
|
23
|
-
*
|
|
24
27
|
* @type {object}
|
|
25
28
|
*/
|
|
26
|
-
exports.parser =
|
|
29
|
+
exports.parser = parser;
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
|
-
* Built-in Twig tag registry.
|
|
32
|
+
* Built-in Twig tag registry.
|
|
30
33
|
*
|
|
31
34
|
* @type {object}
|
|
32
35
|
*/
|
|
33
|
-
exports.tags =
|
|
36
|
+
exports.tags = _tags;
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
|
-
* Built-in Twig filter catalog.
|
|
39
|
+
* Built-in Twig filter catalog.
|
|
37
40
|
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
* @type {object}
|
|
42
|
+
*/
|
|
43
|
+
exports.filters = _filters;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Template loaders re-exported from swig-core.
|
|
42
47
|
*
|
|
43
48
|
* @type {object}
|
|
44
49
|
*/
|
|
45
|
-
exports.
|
|
50
|
+
exports.loaders = loaders;
|
|
51
|
+
|
|
52
|
+
var defaultOptions = {
|
|
53
|
+
autoescape: true,
|
|
54
|
+
varControls: ['{{', '}}'],
|
|
55
|
+
tagControls: ['{%', '%}'],
|
|
56
|
+
cmtControls: ['{#', '#}'],
|
|
57
|
+
locals: {},
|
|
58
|
+
cache: 'memory',
|
|
59
|
+
loader: loaders.fs()
|
|
60
|
+
},
|
|
61
|
+
defaultInstance;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate the Twig options object.
|
|
65
|
+
*
|
|
66
|
+
* @param {?object} options Twig options object.
|
|
67
|
+
* @return {undefined} Throws on malformed input.
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
function validateOptions(options) {
|
|
71
|
+
if (!options) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
utils.each(['varControls', 'tagControls', 'cmtControls'], function (key) {
|
|
76
|
+
if (!options.hasOwnProperty(key)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!utils.isArray(options[key]) || options[key].length !== 2) {
|
|
80
|
+
throw new Error('Option "' + key + '" must be an array containing 2 different control strings.');
|
|
81
|
+
}
|
|
82
|
+
if (options[key][0] === options[key][1]) {
|
|
83
|
+
throw new Error('Option "' + key + '" open and close controls must not be the same.');
|
|
84
|
+
}
|
|
85
|
+
utils.each(options[key], function (a, i) {
|
|
86
|
+
if (a.length < 2) {
|
|
87
|
+
throw new Error('Option "' + key + '" ' + ((i) ? 'open ' : 'close ') + 'control must be at least 2 characters. Saw "' + a + '" instead.');
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (options.hasOwnProperty('cache')) {
|
|
93
|
+
if (options.cache && options.cache !== 'memory') {
|
|
94
|
+
if (!options.cache.get || !options.cache.set) {
|
|
95
|
+
throw new Error('Invalid cache option ' + JSON.stringify(options.cache) + ' found. Expected "memory" or { get: function (key) { ... }, set: function (key, value) { ... } }.');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (options.hasOwnProperty('loader')) {
|
|
100
|
+
if (options.loader) {
|
|
101
|
+
if (!options.loader.load || !options.loader.resolve) {
|
|
102
|
+
throw new Error('Invalid loader option ' + JSON.stringify(options.loader) + ' found. Expected { load: function (pathname, cb) { ... }, resolve: function (to, from) { ... } }.');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Set defaults for the base and all new Twig environments.
|
|
110
|
+
*
|
|
111
|
+
* @param {object} [options={}] Twig options object.
|
|
112
|
+
* @return {undefined}
|
|
113
|
+
*/
|
|
114
|
+
exports.setDefaults = function (options) {
|
|
115
|
+
validateOptions(options);
|
|
116
|
+
defaultInstance.options = utils.extend(defaultInstance.options, options);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Set the default TimeZone offset for date formatting via the date filter.
|
|
121
|
+
* Mutates the shared dateformatter's tzOffset — affects every frontend
|
|
122
|
+
* (native swig + swig-twig) because both consume the same module instance.
|
|
123
|
+
*
|
|
124
|
+
* @param {number} offset Offset from GMT, in minutes (west of GMT).
|
|
125
|
+
* @return {undefined}
|
|
126
|
+
*/
|
|
127
|
+
exports.setDefaultTZOffset = function (offset) {
|
|
128
|
+
dateformatter.tzOffset = offset;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Create a new, separate Twig compile/render environment.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* var twig = require('@rhinostone/swig-twig');
|
|
136
|
+
* var mytwig = new twig.Twig({ autoescape: false });
|
|
137
|
+
* mytwig.render('Hello {{ name }}', { locals: { name: 'world' }});
|
|
138
|
+
*
|
|
139
|
+
* @param {object} [opts={}] Twig options object.
|
|
140
|
+
* @return {object} New Twig environment.
|
|
141
|
+
*/
|
|
142
|
+
exports.Twig = function (opts) {
|
|
143
|
+
validateOptions(opts);
|
|
144
|
+
this.options = utils.extend({}, defaultOptions, opts || {});
|
|
145
|
+
this.cache = {};
|
|
146
|
+
this.extensions = {};
|
|
147
|
+
|
|
148
|
+
engine.install(this, {
|
|
149
|
+
parser: parser,
|
|
150
|
+
tags: _tags,
|
|
151
|
+
filters: _filters,
|
|
152
|
+
validateOptions: validateOptions,
|
|
153
|
+
onCompileError: function (err, options) {
|
|
154
|
+
utils.throwError(err, null, options.filename);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/*!
|
|
160
|
+
* Export methods publicly via the default instance.
|
|
161
|
+
*/
|
|
162
|
+
defaultInstance = new exports.Twig();
|
|
163
|
+
exports.setFilter = defaultInstance.setFilter;
|
|
164
|
+
exports.setTag = defaultInstance.setTag;
|
|
165
|
+
exports.setExtension = defaultInstance.setExtension;
|
|
166
|
+
exports.parseFile = defaultInstance.parseFile;
|
|
167
|
+
exports.precompile = defaultInstance.precompile;
|
|
168
|
+
exports.compile = defaultInstance.compile;
|
|
169
|
+
exports.compileFile = defaultInstance.compileFile;
|
|
170
|
+
exports.render = defaultInstance.render;
|
|
171
|
+
exports.renderFile = defaultInstance.renderFile;
|
|
172
|
+
exports.run = defaultInstance.run;
|
|
173
|
+
exports.invalidateCache = defaultInstance.invalidateCache;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Express 3/4 compatibility alias.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* app.engine('twig', require('@rhinostone/swig-twig').__express);
|
|
180
|
+
* app.set('view engine', 'twig');
|
|
181
|
+
*/
|
|
182
|
+
exports.__express = defaultInstance.renderFile;
|
|
183
|
+
|
|
184
|
+
var _parseDeprecationWarned = false;
|
|
46
185
|
|
|
47
186
|
/**
|
|
48
187
|
* Parse a Twig source string into the parse-tree shape consumed by
|
|
49
188
|
* swig-core's `engine.compile`: `{ name, parent, tokens, blocks }`.
|
|
50
189
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `
|
|
54
|
-
*
|
|
55
|
-
*
|
|
190
|
+
* @deprecated since 2.0.0-alpha.4 — use `new exports.Twig(opts)` and the
|
|
191
|
+
* per-instance `precompile` / `compile` / `render` surface installed by
|
|
192
|
+
* `engine.install`. Slated for removal in `2.0.0` stable. The full-instance
|
|
193
|
+
* path uses closure-captured tag/filter maps and honors `setFilter` /
|
|
194
|
+
* `setTag` overrides; this Path B wrapper does not.
|
|
56
195
|
*
|
|
57
196
|
* @param {string} source Twig template source.
|
|
58
|
-
* @param {object} [options] Per-call
|
|
59
|
-
* (`autoescape`, `varControls`, `tagControls`,
|
|
60
|
-
* `cmtControls`, `filename`, `tags`, `filters`).
|
|
197
|
+
* @param {object} [options] Per-call options.
|
|
61
198
|
* @return {object} `{ name, parent, tokens, blocks }`.
|
|
62
199
|
*/
|
|
63
200
|
exports.parse = function (source, options) {
|
|
201
|
+
if (!_parseDeprecationWarned) {
|
|
202
|
+
_parseDeprecationWarned = true;
|
|
203
|
+
if (typeof console !== 'undefined' && console.warn) {
|
|
204
|
+
console.warn('[@rhinostone/swig-twig] exports.parse is deprecated and will be removed in 2.0.0. Use `new twig.Twig(opts)` and the per-instance precompile/compile/render API instead.');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
64
207
|
options = options || {};
|
|
65
208
|
var tags = options.tags || exports.tags;
|
|
66
209
|
var filters = options.filters || exports.filters;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhinostone/swig-twig",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.4",
|
|
4
4
|
"description": "Twig frontend for the @rhinostone/swig-core template engine. Phase 3 of the multi-flavor architecture (see @rhinostone/swig #T16).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"template",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=12"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@rhinostone/swig-core": "2.0.0-alpha.
|
|
25
|
+
"@rhinostone/swig-core": "2.0.0-alpha.4"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|