@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.
Files changed (2) hide show
  1. package/lib/index.js +171 -28
  2. 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 scaffold. Subsequent commits add the Twig lexer + parser
5
- * (source IR), the Twig filter parity catalog, and the per-flavor
6
- * tag set. Source-to-IR lowering targets the swig-core IR schema
7
- * defined in @rhinostone/swig-core/lib/ir; built-in Twig tags lower at
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
- * per-flavor split decision and migration sequence.
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 = require('./parser');
29
+ exports.parser = parser;
27
30
 
28
31
  /**
29
- * Built-in Twig tag registry. See `./tags/index.js` for the per-tag shape.
32
+ * Built-in Twig tag registry.
30
33
  *
31
34
  * @type {object}
32
35
  */
33
- exports.tags = require('./tags');
36
+ exports.tags = _tags;
34
37
 
35
38
  /**
36
- * Built-in Twig filter catalog. See `./filters.js` for the per-filter shape.
39
+ * Built-in Twig filter catalog.
37
40
  *
38
- * Shipped as the Twig frontend's `_filters` runtime map and `setFilter`
39
- * mutation target via `engine.install(self, frontend)`. Filters marked
40
- * `.safe = true` suppress the autoescape `e` tail injected by
41
- * `parseVariable`. Only `raw` carries `.safe = true` this session.
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.filters = require('./filters');
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
- * Convenience wrapper around `exports.parser.parse(swig, source, options,
52
- * tags, filters)` defaults `tags` to the built-in Twig registry and
53
- * `filters` to an empty map. Callers wiring Twig as a frontend through
54
- * `engine.install(self, frontend)` should call `exports.parser.parse`
55
- * directly so the engine's own filter and tag maps flow through.
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 frontend options
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",
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.3"
25
+ "@rhinostone/swig-core": "2.0.0-alpha.4"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"