fusee-framework 1.8.0 → 1.9.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.
Files changed (109) hide show
  1. package/README.md +64 -3
  2. package/app/benchmark/vite.config.js +23 -0
  3. package/auto-imports.d.ts +4 -0
  4. package/bin/create-fusee-linux +0 -0
  5. package/bin/create-fusee-mac-arm64 +0 -0
  6. package/bin/create-fusee-mac-intel +0 -0
  7. package/bin/create-fusee-win.exe +0 -0
  8. package/bin/create-fusee.exe +0 -0
  9. package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +15 -9
  10. package/create-fusee-go/internal/assets/embed/framework/core/async-context.js +27 -0
  11. package/create-fusee-go/internal/assets/embed/framework/core/compiler/ast.js +167 -0
  12. package/create-fusee-go/internal/assets/embed/framework/core/compiler/errors-list.js +47 -0
  13. package/create-fusee-go/internal/assets/embed/framework/core/compiler/errors.js +107 -0
  14. package/create-fusee-go/internal/assets/embed/framework/core/compiler/generator.js +363 -0
  15. package/create-fusee-go/internal/assets/embed/framework/core/compiler/lexer.js +347 -0
  16. package/create-fusee-go/internal/assets/embed/framework/core/compiler/main-compiler.js +115 -0
  17. package/create-fusee-go/internal/assets/embed/framework/core/compiler/parser.js +414 -0
  18. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/actions-plugin.js +263 -0
  19. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/compiler-plugin.d.ts +5 -0
  20. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/compiler-plugin.js +45 -0
  21. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/file-router-plugin.js +301 -0
  22. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/router-plugin.js +196 -0
  23. package/create-fusee-go/internal/assets/embed/framework/core/compiler/transformer.js +398 -0
  24. package/create-fusee-go/internal/assets/embed/framework/core/component.js +61 -23
  25. package/create-fusee-go/internal/assets/embed/framework/core/di.js +187 -0
  26. package/create-fusee-go/internal/assets/embed/framework/core/directives.js +40 -96
  27. package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +1 -1
  28. package/create-fusee-go/internal/assets/embed/framework/core/h.js +430 -0
  29. package/create-fusee-go/internal/assets/embed/framework/core/hydration.js +83 -0
  30. package/create-fusee-go/internal/assets/embed/framework/core/reconcile.js +151 -0
  31. package/create-fusee-go/internal/assets/embed/framework/core/signal.js +269 -0
  32. package/create-fusee-go/internal/assets/embed/framework/core/store.js +322 -81
  33. package/create-fusee-go/internal/assets/embed/framework/index.js +5 -3
  34. package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +1 -1
  35. package/create-fusee-go/internal/assets/embed/framework/router/router.js +113 -21
  36. package/create-fusee-go/internal/assets/embed/framework/server/actions.js +173 -0
  37. package/create-fusee-go/internal/assets/embed/framework/server/actions.server.js +88 -0
  38. package/create-fusee-go/internal/assets/embed/framework/server/index.js +18 -0
  39. package/create-fusee-go/internal/assets/embed/framework/types/actions.d.ts +50 -0
  40. package/create-fusee-go/internal/assets/embed/framework/types/actions.server.d.ts +36 -0
  41. package/create-fusee-go/internal/assets/embed/framework/types/compiler/ast.d.ts +166 -0
  42. package/create-fusee-go/internal/assets/embed/framework/types/compiler/errors-codes.d.ts +23 -0
  43. package/create-fusee-go/internal/assets/embed/framework/types/compiler/errors.d.ts +43 -0
  44. package/create-fusee-go/internal/assets/embed/framework/types/compiler/generator.d.ts +8 -0
  45. package/create-fusee-go/internal/assets/embed/framework/types/compiler/h.d.ts +46 -0
  46. package/create-fusee-go/internal/assets/embed/framework/types/compiler/lexer.d.ts +27 -0
  47. package/create-fusee-go/internal/assets/embed/framework/types/compiler/main-compiler.d.ts +60 -0
  48. package/create-fusee-go/internal/assets/embed/framework/types/compiler/parser.d.ts +4 -0
  49. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/actions.d.ts +30 -0
  50. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/compiler-plugin.d.ts +5 -0
  51. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/file-router-plugins.d.ts +32 -0
  52. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/router-plugin.d.ts +24 -0
  53. package/create-fusee-go/internal/assets/embed/framework/types/compiler/transformer.d.ts +28 -0
  54. package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +89 -88
  55. package/create-fusee-go/internal/assets/embed/framework/types/di.d.ts +43 -0
  56. package/create-fusee-go/internal/assets/embed/framework/types/hydration.d.ts +30 -0
  57. package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +38 -22
  58. package/create-fusee-go/internal/assets/embed/framework/types/reconcile.d.ts +7 -0
  59. package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +11 -1
  60. package/create-fusee-go/internal/assets/embed/framework/types/server.d.ts +11 -0
  61. package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +142 -79
  62. package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +66 -8
  63. package/framework/auto-import-preset.js +3 -1
  64. package/framework/core/async-context.js +27 -0
  65. package/framework/core/compiler/ast.js +167 -0
  66. package/framework/core/compiler/errors-list.js +47 -0
  67. package/framework/core/compiler/errors.js +107 -0
  68. package/framework/core/compiler/generator.js +363 -0
  69. package/framework/core/compiler/lexer.js +347 -0
  70. package/framework/core/compiler/main-compiler.js +115 -0
  71. package/framework/core/compiler/parser.js +414 -0
  72. package/framework/core/compiler/plugins/actions-plugin.js +263 -0
  73. package/framework/core/compiler/plugins/compiler-plugin.d.ts +5 -0
  74. package/framework/core/compiler/plugins/compiler-plugin.js +45 -0
  75. package/framework/core/compiler/plugins/file-router-plugin.js +301 -0
  76. package/framework/core/compiler/plugins/router-plugin.js +196 -0
  77. package/framework/core/compiler/transformer.js +398 -0
  78. package/framework/core/component.js +34 -10
  79. package/framework/core/evaluator.js +1 -1
  80. package/framework/core/h.js +430 -0
  81. package/framework/core/hydration.js +83 -0
  82. package/framework/core/signal.js +17 -15
  83. package/framework/server/actions.js +173 -0
  84. package/framework/server/actions.server.js +88 -0
  85. package/framework/server/index.js +18 -0
  86. package/framework/types/actions.d.ts +50 -0
  87. package/framework/types/actions.server.d.ts +36 -0
  88. package/framework/types/compiler/ast.d.ts +166 -0
  89. package/framework/types/compiler/errors-codes.d.ts +23 -0
  90. package/framework/types/compiler/errors.d.ts +43 -0
  91. package/framework/types/compiler/generator.d.ts +8 -0
  92. package/framework/types/compiler/h.d.ts +46 -0
  93. package/framework/types/compiler/lexer.d.ts +27 -0
  94. package/framework/types/compiler/main-compiler.d.ts +60 -0
  95. package/framework/types/compiler/parser.d.ts +4 -0
  96. package/framework/types/compiler/plugins/actions.d.ts +30 -0
  97. package/framework/types/compiler/plugins/compiler-plugin.d.ts +5 -0
  98. package/framework/types/compiler/plugins/file-router-plugins.d.ts +32 -0
  99. package/framework/types/compiler/plugins/router-plugin.d.ts +24 -0
  100. package/framework/types/compiler/transformer.d.ts +28 -0
  101. package/framework/types/hydration.d.ts +30 -0
  102. package/framework/types/index.d.ts +6 -0
  103. package/framework/types/server.d.ts +11 -0
  104. package/framework/types/signal.d.ts +2 -7
  105. package/ghid_go_fusee_integrat_go1_26.txt +276 -0
  106. package/ghid_rust_fusee_rust_1_95_0.txt +1118 -0
  107. package/package.json +44 -36
  108. package/vite.config.js +65 -53
  109. package/create-fusee-go/internal/assets/embed/framework/bin/create-fusee.js +0 -199
@@ -0,0 +1,115 @@
1
+ import { tokenize } from './lexer.js'
2
+ import { parse } from './parser.js'
3
+ import { transform } from './transformer.js'
4
+ import { generate } from './generator.js'
5
+ import { CompileError, ErrorCollector } from './errors.js'
6
+
7
+ export function compile(source, options = {}) {
8
+ const filename = options.filename ?? '<template>'
9
+ const components = new Set(options.components ?? [])
10
+ const collector = new ErrorCollector(source)
11
+
12
+ let tokens
13
+ try {
14
+ tokens = tokenize(source)
15
+ } catch (err) {
16
+ _rethrow(err, filename)
17
+ }
18
+
19
+ let ast
20
+ try {
21
+ ast = parse(tokens, source, components)
22
+ } catch (err) {
23
+ _rethrow(err, filename)
24
+ }
25
+
26
+ let warnings = []
27
+ try {
28
+ const result = transform(ast, { components, source, scope: options.scope })
29
+ ast = result.ast
30
+ warnings = result.warnings
31
+ } catch (err) {
32
+ _rethrow(err, filename)
33
+ }
34
+
35
+ if (options.throwOnWarning && warnings.length > 0) {
36
+ const w = warnings[0]
37
+ throw new CompileError(w.code, w.loc, source)
38
+ }
39
+
40
+ let code
41
+ try {
42
+ code = generate(ast, {
43
+ source,
44
+ runtimePath: options.runtimePath,
45
+ })
46
+ } catch (err) {
47
+ _rethrow(err, filename)
48
+ }
49
+
50
+ return { code, ast, tokens, warnings }
51
+ }
52
+
53
+ export function parseOnly(source, options = {}) {
54
+ const components = new Set(options.components ?? [])
55
+ const tokens = tokenize(source)
56
+ const ast = parse(tokens, source, components)
57
+ return { ast, tokens }
58
+ }
59
+
60
+ export function transformOnly(ast, options = {}) {
61
+ return transform(ast, {
62
+ components: new Set(options.components ?? []),
63
+ source: options.source ?? '',
64
+ scope: options.scope,
65
+ })
66
+ }
67
+
68
+ export function compileBatch(templates, sharedOptions = {}) {
69
+ return templates.map(({ id, source }) => {
70
+ try {
71
+ const { code, warnings } = compile(source, { ...sharedOptions, filename: id })
72
+ return { id, code, warnings, error: null }
73
+ } catch (err) {
74
+ return { id, code: null, warnings: [], error: err }
75
+ }
76
+ })
77
+ }
78
+
79
+ export function fuseePlugin(pluginOptions = {}) {
80
+ return {
81
+ name: 'vite-plugin-fusee',
82
+
83
+ transform(src, id) {
84
+ if (!id.endsWith('.fusee') && !id.endsWith('.fhtml')) return null
85
+
86
+ const { code, warnings } = compile(src, {
87
+ filename: id,
88
+ runtimePath: pluginOptions.runtimePath,
89
+ components: pluginOptions.components,
90
+ scope: pluginOptions.scope,
91
+ })
92
+
93
+ for (const w of warnings) {
94
+ this.warn(w.format(id))
95
+ }
96
+
97
+ return { code, map: null }
98
+ }
99
+ }
100
+ }
101
+
102
+ export { fileRouterPlugin } from './plugins/file-router-plugin.js'
103
+ export { actionsPlugin } from './plugins/actions-plugin.js'
104
+ export { routerPlugin } from './plugins/router-plugin.js'
105
+ export { compileFileRoutes, generateRoutesModule, generateRouteTypes, validateRoutes as validateFileRoutes,} from './plugins/file-router-plugin.js'
106
+ export { validateActions, generateClientStubs, generateServerRoutes, generateActionTypes,} from './plugins/actions-plugin.js'
107
+ export { validateRoutes, generateRouterTypes,} from './plugins/router-plugin.js'
108
+
109
+ function _rethrow(err, filename) {
110
+ if (err instanceof CompileError) {
111
+ err.message = err.format(filename)
112
+ throw err
113
+ }
114
+ throw err
115
+ }
@@ -0,0 +1,414 @@
1
+ import { TokenType } from "./lexer.js";
2
+ import { ErrorCode } from "./errors-list.js";
3
+ import { throwError } from "./errors.js";
4
+ import {
5
+ NodeType,
6
+ createRoot,
7
+ createElement,
8
+ createComponent,
9
+ createText,
10
+ createInterpolation,
11
+ createExpression,
12
+ createAttribute,
13
+ createBinding,
14
+ createEvent,
15
+ createDirective,
16
+ createSlotOutlet,
17
+ createSlotContent,
18
+ createForArg,
19
+ createLoc,
20
+ createPos,
21
+ } from "./ast.js";
22
+
23
+ const VOID_ELEMENTS = new Set([
24
+ "area",
25
+ "base",
26
+ "br",
27
+ "col",
28
+ "embed",
29
+ "hr",
30
+ "img",
31
+ "input",
32
+ "link",
33
+ "meta",
34
+ "param",
35
+ "source",
36
+ "track",
37
+ "wbr",
38
+ ]);
39
+ const FOR_RE =
40
+ /^(?:\(\s*([^,)]+?)\s*(?:,\s*([^)]+?)\s*)?\))\s+in\s+(.+)$|^([^\s,]+)\s+in\s+(.+)$/;
41
+
42
+ export function parse(tokens, source, components = new Set()) {
43
+ const parser = new Parser(tokens, source, components);
44
+ return parser.parse();
45
+ }
46
+
47
+ class Parser {
48
+ constructor(tokens, source, components) {
49
+ this.tokens = tokens;
50
+ this.source = source;
51
+ this.components = components;
52
+ this.pos = 0;
53
+ this.stack = [];
54
+ }
55
+
56
+ parse() {
57
+ const start = this._startLoc();
58
+ const children = this._parseChildren(null);
59
+ return createRoot(
60
+ children,
61
+ createLoc(start, this._startLoc(), this.source),
62
+ );
63
+ }
64
+
65
+ _parseChildren(parentTag) {
66
+ const children = [];
67
+
68
+ while (!this._eof()) {
69
+ const tok = this._peek();
70
+
71
+ if (tok.type === TokenType.EOF) break;
72
+ if (tok.type === TokenType.TAG_OPEN_CLOSE) break;
73
+
74
+ if (tok.type === TokenType.COMMENT) {
75
+ this._advance();
76
+ continue;
77
+ }
78
+
79
+ if (tok.type === TokenType.TEXT) {
80
+ children.push(this._parseText());
81
+ continue;
82
+ }
83
+
84
+ if (tok.type === TokenType.MUSTACHE_OPEN) {
85
+ children.push(this._parseMustache());
86
+ continue;
87
+ }
88
+
89
+ if (tok.type === TokenType.TAG_OPEN) {
90
+ const node = this._parseElement();
91
+ if (node) children.push(node);
92
+ continue;
93
+ }
94
+
95
+ this._advance();
96
+ }
97
+
98
+ return children;
99
+ }
100
+
101
+ _parseText() {
102
+ const tok = this._consume(TokenType.TEXT);
103
+ return createText(tok.value, tok.loc);
104
+ }
105
+
106
+ _parseMustache() {
107
+ const open = this._consume(TokenType.MUSTACHE_OPEN);
108
+ const expr = this._consume(TokenType.MUSTACHE_EXPR);
109
+ const close = this._consume(TokenType.MUSTACHE_CLOSE);
110
+
111
+ if (!expr.value.trim()) {
112
+ throwError(
113
+ ErrorCode.EMPTY_EXPRESSION,
114
+ expr.loc,
115
+ this.source,
116
+ "{{ }}",
117
+ );
118
+ }
119
+
120
+ const loc = createLoc(open.loc.start, close.loc.end);
121
+ return createInterpolation(
122
+ createExpression(expr.value, false, expr.loc),
123
+ loc,
124
+ );
125
+ }
126
+
127
+ _parseElement() {
128
+ const openTok = this._consume(TokenType.TAG_OPEN);
129
+ const tag = openTok.value;
130
+ const startLoc = openTok.loc.start;
131
+ const props = this._parseAttrs(tag);
132
+
133
+ let selfClosing = false;
134
+ if (this._peek()?.type === TokenType.TAG_SELF_CLOSE) {
135
+ this._advance();
136
+ selfClosing = true;
137
+ } else {
138
+ this._consume(TokenType.TAG_CLOSE);
139
+ }
140
+
141
+ if (tag === "slot") {
142
+ return this._buildSlotOutlet(props, startLoc, selfClosing);
143
+ }
144
+ const slotAttr = props.find(
145
+ (p) => p.type === NodeType.ATTRIBUTE && p.name === "slot",
146
+ );
147
+ if (tag === "template" && slotAttr) {
148
+ return this._buildSlotContent(
149
+ slotAttr,
150
+ props,
151
+ startLoc,
152
+ selfClosing,
153
+ );
154
+ }
155
+
156
+ if (selfClosing || VOID_ELEMENTS.has(tag.toLowerCase())) {
157
+ return this._buildNode(tag, props, [], true, startLoc);
158
+ }
159
+ this.stack.push(tag);
160
+ const children = this._parseChildren(tag);
161
+ this.stack.pop();
162
+
163
+ if (this._peek()?.type === TokenType.TAG_OPEN_CLOSE) {
164
+ const closeTok = this._advance();
165
+ if (closeTok.value.toLowerCase() !== tag.toLowerCase()) {
166
+ throwError(
167
+ ErrorCode.UNEXPECTED_CLOSING_TAG,
168
+ closeTok.loc,
169
+ this.source,
170
+ closeTok.value,
171
+ );
172
+ }
173
+ if (this._peek()?.type === TokenType.TAG_CLOSE) this._advance();
174
+ } else {
175
+ throwError(
176
+ ErrorCode.MISSING_CLOSING_TAG,
177
+ createLoc(startLoc, startLoc),
178
+ this.source,
179
+ tag,
180
+ );
181
+ }
182
+
183
+ const endLoc = this._startLoc();
184
+ return this._buildNode(tag, props, children, false, startLoc, endLoc);
185
+ }
186
+
187
+ _buildNode(tag, props, children, selfClosing, startLoc, endLoc = null) {
188
+ const loc = createLoc(startLoc, endLoc ?? startLoc);
189
+
190
+ if (this.components.has(tag) || /^[A-Z]/.test(tag)) {
191
+ const slots = this._extractSlots(children);
192
+ return createComponent(tag, props, slots, loc);
193
+ }
194
+
195
+ return createElement(tag, props, children, selfClosing, loc);
196
+ }
197
+
198
+ _buildSlotOutlet(props, startLoc, selfClosing) {
199
+ const nameAttr = props.find(
200
+ (p) => p.type === NodeType.ATTRIBUTE && p.name === "name",
201
+ );
202
+ const slotName = nameAttr?.value ?? "default";
203
+ let fallback = [];
204
+ if (!selfClosing) {
205
+ this.stack.push("slot");
206
+ fallback = this._parseChildren("slot");
207
+ this.stack.pop();
208
+ if (this._peek()?.type === TokenType.TAG_OPEN_CLOSE) {
209
+ this._advance();
210
+ if (this._peek()?.type === TokenType.TAG_CLOSE) this._advance();
211
+ }
212
+ }
213
+
214
+ return createSlotOutlet(
215
+ slotName,
216
+ fallback,
217
+ createLoc(startLoc, this._startLoc()),
218
+ );
219
+ }
220
+
221
+ _buildSlotContent(slotAttr, props, startLoc, selfClosing) {
222
+ const slotName = slotAttr.value ?? "default";
223
+ let children = [];
224
+
225
+ if (!selfClosing) {
226
+ this.stack.push("template");
227
+ children = this._parseChildren("template");
228
+ this.stack.pop();
229
+ if (this._peek()?.type === TokenType.TAG_OPEN_CLOSE) {
230
+ this._advance();
231
+ if (this._peek()?.type === TokenType.TAG_CLOSE) this._advance();
232
+ }
233
+ }
234
+
235
+ return createSlotContent(
236
+ slotName,
237
+ children,
238
+ createLoc(startLoc, this._startLoc()),
239
+ );
240
+ }
241
+
242
+ _extractSlots(children) {
243
+ const slots = { default: [] };
244
+
245
+ for (const child of children) {
246
+ if (child.type === NodeType.SLOT_CONTENT) {
247
+ slots[child.slotName] = child.children;
248
+ } else {
249
+ slots.default.push(child);
250
+ }
251
+ }
252
+
253
+ return slots;
254
+ }
255
+
256
+ _parseAttrs(tag) {
257
+ const props = [];
258
+ const seen = new Set();
259
+
260
+ while (!this._eof()) {
261
+ const tok = this._peek();
262
+ if (
263
+ tok.type === TokenType.TAG_CLOSE ||
264
+ tok.type === TokenType.TAG_SELF_CLOSE ||
265
+ tok.type === TokenType.EOF
266
+ )
267
+ break;
268
+
269
+ if (tok.type !== TokenType.ATTR_NAME) {
270
+ this._advance();
271
+ continue;
272
+ }
273
+
274
+ const nameTok = this._advance();
275
+ const rawName = nameTok.value;
276
+ if (seen.has(rawName)) {
277
+ throwError(
278
+ ErrorCode.DUPLICATE_KEY,
279
+ nameTok.loc,
280
+ this.source,
281
+ rawName,
282
+ tag,
283
+ );
284
+ }
285
+ seen.add(rawName);
286
+
287
+ let value = null;
288
+ if (this._peek()?.type === TokenType.ATTR_EQUALS) {
289
+ this._advance();
290
+ const valTok = this._consume(TokenType.ATTR_VALUE);
291
+ value = valTok.value;
292
+ }
293
+
294
+ const loc = nameTok.loc;
295
+
296
+ if (rawName.startsWith("@")) {
297
+ props.push(this._parseEventAttr(rawName, value, loc));
298
+ } else if (rawName.startsWith(":")) {
299
+ props.push(this._parseBindingAttr(rawName, value, loc));
300
+ } else if (rawName.startsWith("f-")) {
301
+ props.push(this._parseDirectiveAttr(rawName, value, loc, tag));
302
+ } else {
303
+ props.push(createAttribute(rawName, value, loc));
304
+ }
305
+ }
306
+
307
+ return props;
308
+ }
309
+
310
+ _parseEventAttr(rawName, value, loc) {
311
+ const rest = rawName.slice(1);
312
+ const parts = rest.split(".");
313
+ const eventName = parts[0];
314
+ const modifiers = parts.slice(1);
315
+
316
+ if (!value || !value.trim()) {
317
+ throwError(ErrorCode.EMPTY_EXPRESSION, loc, this.source, rawName);
318
+ }
319
+
320
+ return createEvent(
321
+ eventName,
322
+ createExpression(value.trim(), false, loc),
323
+ modifiers,
324
+ loc,
325
+ );
326
+ }
327
+
328
+ _parseBindingAttr(rawName, value, loc) {
329
+ const name = rawName.slice(1);
330
+ const isProp = name.startsWith("prop-");
331
+
332
+ if (!value || !value.trim()) {
333
+ throwError(ErrorCode.EMPTY_EXPRESSION, loc, this.source, rawName);
334
+ }
335
+
336
+ return createBinding(
337
+ isProp ? name.slice(5) : name,
338
+ createExpression(value.trim(), false, loc),
339
+ isProp,
340
+ loc,
341
+ );
342
+ }
343
+
344
+ _parseDirectiveAttr(rawName, value, loc, tag) {
345
+ const name = rawName.slice(2);
346
+ const needsExpr = !["else", "once"].includes(name);
347
+
348
+ if (needsExpr && (!value || !value.trim())) {
349
+ throwError(ErrorCode.EMPTY_EXPRESSION, loc, this.source, rawName);
350
+ }
351
+
352
+ let expression = null;
353
+ let arg = null;
354
+
355
+ if (value) {
356
+ if (name === "for") {
357
+ arg = this._parseForExpression(value.trim(), loc);
358
+ } else {
359
+ expression = createExpression(value.trim(), false, loc);
360
+ }
361
+ }
362
+
363
+ return createDirective(name, expression, arg, loc);
364
+ }
365
+
366
+ _parseForExpression(raw, loc) {
367
+ const m = FOR_RE.exec(raw);
368
+ if (!m) {
369
+ throwError(ErrorCode.INVALID_FOR_SYNTAX, loc, this.source, raw);
370
+ }
371
+
372
+ const item = (m[1] ?? m[4]).trim();
373
+ const index = m[2] ? m[2].trim() : null;
374
+ const source = (m[3] ?? m[5]).trim();
375
+
376
+ return createForArg(item, source, index, loc);
377
+ }
378
+
379
+ _eof() {
380
+ return (
381
+ this.pos >= this.tokens.length ||
382
+ this._peek()?.type === TokenType.EOF
383
+ );
384
+ }
385
+
386
+ _peek() {
387
+ return this.tokens[this.pos];
388
+ }
389
+
390
+ _advance() {
391
+ const tok = this.tokens[this.pos];
392
+ if (tok?.type !== TokenType.EOF) this.pos++;
393
+ return tok;
394
+ }
395
+
396
+ _consume(expectedType) {
397
+ const tok = this._peek();
398
+ if (!tok || tok.type !== expectedType) {
399
+ throwError(
400
+ ErrorCode.UNEXPECTED_TOKEN,
401
+ tok?.loc ?? createLoc(this._startLoc(), this._startLoc()),
402
+ this.source,
403
+ tok?.type ?? "EOF",
404
+ expectedType,
405
+ );
406
+ }
407
+ return this._advance();
408
+ }
409
+
410
+ _startLoc() {
411
+ const tok = this._peek();
412
+ return tok?.loc?.start ?? createPos(0, 0, 0);
413
+ }
414
+ }