fusee-framework 1.9.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 (62) hide show
  1. package/README.md +1 -1
  2. package/bin/create-fusee-linux +0 -0
  3. package/bin/create-fusee-mac-arm64 +0 -0
  4. package/bin/create-fusee-mac-intel +0 -0
  5. package/bin/create-fusee-win.exe +0 -0
  6. package/bin/create-fusee.exe +0 -0
  7. package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +15 -9
  8. package/create-fusee-go/internal/assets/embed/framework/core/async-context.js +27 -0
  9. package/create-fusee-go/internal/assets/embed/framework/core/compiler/ast.js +167 -0
  10. package/create-fusee-go/internal/assets/embed/framework/core/compiler/errors-list.js +47 -0
  11. package/create-fusee-go/internal/assets/embed/framework/core/compiler/errors.js +107 -0
  12. package/create-fusee-go/internal/assets/embed/framework/core/compiler/generator.js +363 -0
  13. package/create-fusee-go/internal/assets/embed/framework/core/compiler/lexer.js +347 -0
  14. package/create-fusee-go/internal/assets/embed/framework/core/compiler/main-compiler.js +115 -0
  15. package/create-fusee-go/internal/assets/embed/framework/core/compiler/parser.js +414 -0
  16. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/actions-plugin.js +263 -0
  17. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/compiler-plugin.d.ts +5 -0
  18. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/compiler-plugin.js +45 -0
  19. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/file-router-plugin.js +301 -0
  20. package/create-fusee-go/internal/assets/embed/framework/core/compiler/plugins/router-plugin.js +196 -0
  21. package/create-fusee-go/internal/assets/embed/framework/core/compiler/transformer.js +398 -0
  22. package/create-fusee-go/internal/assets/embed/framework/core/component.js +61 -23
  23. package/create-fusee-go/internal/assets/embed/framework/core/di.js +187 -0
  24. package/create-fusee-go/internal/assets/embed/framework/core/directives.js +40 -96
  25. package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +1 -1
  26. package/create-fusee-go/internal/assets/embed/framework/core/h.js +430 -0
  27. package/create-fusee-go/internal/assets/embed/framework/core/hydration.js +83 -0
  28. package/create-fusee-go/internal/assets/embed/framework/core/reconcile.js +151 -0
  29. package/create-fusee-go/internal/assets/embed/framework/core/signal.js +269 -0
  30. package/create-fusee-go/internal/assets/embed/framework/core/store.js +322 -81
  31. package/create-fusee-go/internal/assets/embed/framework/index.js +5 -3
  32. package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +1 -1
  33. package/create-fusee-go/internal/assets/embed/framework/router/router.js +113 -21
  34. package/create-fusee-go/internal/assets/embed/framework/server/actions.js +173 -0
  35. package/create-fusee-go/internal/assets/embed/framework/server/actions.server.js +88 -0
  36. package/create-fusee-go/internal/assets/embed/framework/server/index.js +18 -0
  37. package/create-fusee-go/internal/assets/embed/framework/types/actions.d.ts +50 -0
  38. package/create-fusee-go/internal/assets/embed/framework/types/actions.server.d.ts +36 -0
  39. package/create-fusee-go/internal/assets/embed/framework/types/compiler/ast.d.ts +166 -0
  40. package/create-fusee-go/internal/assets/embed/framework/types/compiler/errors-codes.d.ts +23 -0
  41. package/create-fusee-go/internal/assets/embed/framework/types/compiler/errors.d.ts +43 -0
  42. package/create-fusee-go/internal/assets/embed/framework/types/compiler/generator.d.ts +8 -0
  43. package/create-fusee-go/internal/assets/embed/framework/types/compiler/h.d.ts +46 -0
  44. package/create-fusee-go/internal/assets/embed/framework/types/compiler/lexer.d.ts +27 -0
  45. package/create-fusee-go/internal/assets/embed/framework/types/compiler/main-compiler.d.ts +60 -0
  46. package/create-fusee-go/internal/assets/embed/framework/types/compiler/parser.d.ts +4 -0
  47. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/actions.d.ts +30 -0
  48. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/compiler-plugin.d.ts +5 -0
  49. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/file-router-plugins.d.ts +32 -0
  50. package/create-fusee-go/internal/assets/embed/framework/types/compiler/plugins/router-plugin.d.ts +24 -0
  51. package/create-fusee-go/internal/assets/embed/framework/types/compiler/transformer.d.ts +28 -0
  52. package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +89 -88
  53. package/create-fusee-go/internal/assets/embed/framework/types/di.d.ts +43 -0
  54. package/create-fusee-go/internal/assets/embed/framework/types/hydration.d.ts +30 -0
  55. package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +38 -22
  56. package/create-fusee-go/internal/assets/embed/framework/types/reconcile.d.ts +7 -0
  57. package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +11 -1
  58. package/create-fusee-go/internal/assets/embed/framework/types/server.d.ts +11 -0
  59. package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +142 -79
  60. package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +66 -8
  61. package/package.json +1 -1
  62. package/create-fusee-go/internal/assets/embed/framework/bin/create-fusee.js +0 -199
@@ -0,0 +1,363 @@
1
+ import { NodeType } from "./ast.js";
2
+ import { ErrorCode } from "./errors-list.js";
3
+ import { throwError } from "./errors.js";
4
+
5
+ export function generate(ast, options = {}) {
6
+ const gen = new Generator(ast, options);
7
+ return gen.generate();
8
+ }
9
+
10
+ const RUNTIME = "fusee/runtime/h.js";
11
+
12
+ class Generator {
13
+ constructor(ast, options) {
14
+ this.ast = ast;
15
+ this.source = options.source ?? "";
16
+ this.runtimePath = options.runtimePath ?? RUNTIME;
17
+ this.indent = 0;
18
+ this.lines = [];
19
+ this._hoistedIdx = 0;
20
+ this._hoistedMap = new Map();
21
+ this._imports = new Set([
22
+ "h",
23
+ "hText",
24
+ "createComponent",
25
+ "_effect",
26
+ "_batch",
27
+ ]);
28
+ this.localScopes = [];
29
+ }
30
+
31
+ generate() {
32
+ if (this.ast._hoisted) {
33
+ for (const node of this.ast._hoisted) {
34
+ const name = `_s${this._hoistedIdx++}`;
35
+ this._hoistedMap.set(node, name);
36
+ }
37
+ }
38
+
39
+ const bodyLines = [];
40
+ const children = this._genChildren(this.ast.children);
41
+
42
+ const hoistedDecls = [];
43
+ for (const [node, name] of this._hoistedMap) {
44
+ hoistedDecls.push(`const ${name} = ${this._genNode(node, true)}`);
45
+ }
46
+
47
+ const importList = [...this._imports].join(", ");
48
+ const output = [
49
+ `import { ${importList} } from '${this.runtimePath}'`,
50
+ "",
51
+ ...(hoistedDecls.length ? [...hoistedDecls, ""] : []),
52
+ `export function render(_ctx, _components) {`,
53
+ ` return [`,
54
+ children.map((l) => ` ${l}`).join(",\n"),
55
+ ` ]`,
56
+ `}`,
57
+ ];
58
+
59
+ return output.join("\n");
60
+ }
61
+
62
+ _genChildren(children) {
63
+ if (!children || children.length === 0) return [];
64
+ return children.flatMap((child) => {
65
+ const forDir = child.props?.find?.(
66
+ (p) => p.type === NodeType.DIRECTIVE && p.name === "for",
67
+ );
68
+ if (forDir) return [this._genForNode(child, forDir)];
69
+ return [this._genNode(child)];
70
+ });
71
+ }
72
+
73
+ _genNode(node, inHoist = false) {
74
+ if (!inHoist && this._hoistedMap.has(node)) {
75
+ return this._hoistedMap.get(node);
76
+ }
77
+
78
+ switch (node.type) {
79
+ case NodeType.TEXT:
80
+ return this._genText(node);
81
+ case NodeType.INTERPOLATION:
82
+ return this._genInterpolation(node);
83
+ case NodeType.ELEMENT:
84
+ return this._genElement(node);
85
+ case NodeType.COMPONENT:
86
+ return this._genComponent(node);
87
+ case NodeType.SLOT_OUTLET:
88
+ return this._genSlotOutlet(node);
89
+ case "If":
90
+ return this._genIf(node);
91
+ default:
92
+ throwError(
93
+ ErrorCode.UNKNOWN_NODE_TYPE,
94
+ node.loc,
95
+ this.source,
96
+ node.type,
97
+ );
98
+ }
99
+ }
100
+
101
+ _genText(node) {
102
+ return `hText(${JSON.stringify(node.content)})`;
103
+ }
104
+
105
+ _genInterpolation(node) {
106
+ this._imports.add("_effect");
107
+ const expr = node.expression.content;
108
+ if (node.expression.isStatic) {
109
+ return `hText(String(${expr}))`;
110
+ }
111
+ return `hText(() => String(${this._wrapExpr(expr)}))`;
112
+ }
113
+
114
+ _genElement(node) {
115
+ const tag = JSON.stringify(node.tag);
116
+ const props = this._genProps(node.props);
117
+ const children = this._genChildrenArray(node.children);
118
+ const args = [tag, props, children];
119
+
120
+ if (node.isStatic) args.push("true");
121
+
122
+ return `h(${args.join(", ")})`;
123
+ }
124
+
125
+ _genComponent(node) {
126
+ this._imports.add("createComponent");
127
+
128
+ const name = JSON.stringify(node.name);
129
+ const props = this._genComponentProps(node.props);
130
+ const slots = this._genSlots(node.slots);
131
+ return `createComponent(${name}, _components[${name}], ${props}, ${slots})`;
132
+ }
133
+
134
+ _genSlotOutlet(node) {
135
+ this._imports.add("hSlot");
136
+ const name = JSON.stringify(node.slotName);
137
+ const fallback = this._genChildrenArray(node.fallback);
138
+ return `hSlot(_ctx._slots, ${name}, ${fallback})`;
139
+ }
140
+
141
+ _genIf(node) {
142
+ this._imports.add("hIf");
143
+
144
+ const branches = node.branches.map((branch) => {
145
+ const cond = branch.condition
146
+ ? this._wrapExpr(branch.condition.content)
147
+ : "true";
148
+ const children = this._genChildrenArray(branch.node.children ?? []);
149
+ return `[() => ${cond}, () => ${children}]`;
150
+ });
151
+
152
+ return `hIf([\n ${branches.join(",\n ")}\n ])`;
153
+ }
154
+
155
+ _genProps(props) {
156
+ if (!props || props.length === 0) return "{}";
157
+
158
+ const entries = [];
159
+
160
+ for (const prop of props) {
161
+ switch (prop.type) {
162
+ case NodeType.ATTRIBUTE:
163
+ entries.push(this._genAttrEntry(prop));
164
+ break;
165
+ case NodeType.BINDING:
166
+ entries.push(this._genBindingEntry(prop));
167
+ break;
168
+ case NodeType.EVENT:
169
+ entries.push(this._genEventEntry(prop));
170
+ break;
171
+ case NodeType.DIRECTIVE:
172
+ this._genDirectiveEntries(prop, entries);
173
+ break;
174
+ }
175
+ }
176
+
177
+ return `{\n ${entries.join(",\n ")}\n }`;
178
+ }
179
+
180
+ _genAttrEntry(prop) {
181
+ return `${JSON.stringify(prop.name)}: ${JSON.stringify(prop.value ?? true)}`;
182
+ }
183
+
184
+ _genBindingEntry(prop) {
185
+ const expr = this._wrapExpr(prop.expression.content);
186
+ if (prop.isProp) {
187
+ return `${JSON.stringify("prop:" + prop.name)}: () => ${expr}`;
188
+ }
189
+ return `${JSON.stringify(prop.name)}: () => ${expr}`;
190
+ }
191
+
192
+ _genEventEntry(prop) {
193
+ const key = JSON.stringify("@" + prop.name);
194
+ const expr = prop.expression.content;
195
+ const mods = JSON.stringify(prop.modifiers);
196
+ const handlerJs = this._genHandlerExpr(expr);
197
+ return `${key}: { handler: ${handlerJs}, modifiers: ${mods} }`;
198
+ }
199
+
200
+ _genHandlerExpr(expr) {
201
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(expr.trim())) {
202
+ return `_ctx.${expr.trim()}`;
203
+ }
204
+ return `($event) => { ${expr} }`;
205
+ }
206
+
207
+ _genDirectiveEntries(dir, entries) {
208
+ switch (dir.name) {
209
+ case "if":
210
+ case "else-if":
211
+ case "else":
212
+ break;
213
+ case "for":
214
+ break;
215
+ case "show": {
216
+ const expr = this._wrapExpr(dir.expression.content);
217
+ entries.push(`'f-show': () => ${expr}`);
218
+ break;
219
+ }
220
+
221
+ case "model": {
222
+ const expr = dir.expression.content;
223
+ entries.push(`'f-model': () => _ctx.${expr}`);
224
+ entries.push(
225
+ `'@input': { handler: ($e) => { _ctx.${expr}($e.target.value) }, modifiers: [] }`,
226
+ );
227
+ break;
228
+ }
229
+
230
+ case "html": {
231
+ const expr = this._wrapExpr(dir.expression.content);
232
+ entries.push(`'f-html': () => ${expr}`);
233
+ break;
234
+ }
235
+
236
+ case "ref": {
237
+ const name = dir.expression.content.trim();
238
+ entries.push(`'f-ref': ${JSON.stringify(name)}`);
239
+ break;
240
+ }
241
+
242
+ case "once":
243
+ entries.push(`'f-once': true`);
244
+ break;
245
+
246
+ default:
247
+ entries.push(
248
+ `${JSON.stringify("f-" + dir.name)}: ${dir.expression ? `() => ${this._wrapExpr(dir.expression.content)}` : "true"}`,
249
+ );
250
+ }
251
+ }
252
+
253
+ _genComponentProps(props) {
254
+ if (!props || props.length === 0) return "{}";
255
+
256
+ const entries = [];
257
+
258
+ for (const prop of props) {
259
+ if (prop.type === NodeType.ATTRIBUTE) {
260
+ entries.push(
261
+ `${JSON.stringify(prop.name)}: ${JSON.stringify(prop.value ?? true)}`,
262
+ );
263
+ } else if (prop.type === NodeType.BINDING) {
264
+ entries.push(
265
+ `${JSON.stringify(prop.name)}: () => ${this._wrapExpr(prop.expression.content)}`,
266
+ );
267
+ } else if (prop.type === NodeType.EVENT) {
268
+ const handlerJs = this._genHandlerExpr(prop.expression.content);
269
+ entries.push(`${JSON.stringify("on:" + prop.name)}: ${handlerJs}`);
270
+ }
271
+ }
272
+
273
+ return `{ ${entries.join(", ")} }`;
274
+ }
275
+
276
+ _genSlots(slots) {
277
+ if (!slots || Object.keys(slots).length === 0) return "{}";
278
+
279
+ const entries = Object.entries(slots).map(([name, children]) => {
280
+ const childrenJs = this._genChildrenArray(children);
281
+ return `${JSON.stringify(name)}: () => ${childrenJs}`;
282
+ });
283
+
284
+ return `{ ${entries.join(", ")} }`;
285
+ }
286
+
287
+ _genChildrenArray(children) {
288
+ if (!children || children.length === 0) return "[]";
289
+ const expanded = children.flatMap((child) => {
290
+ const forDir = child.props?.find?.(
291
+ (p) => p.type === NodeType.DIRECTIVE && p.name === "for",
292
+ );
293
+ if (forDir) return [this._genForNode(child, forDir)];
294
+ return [this._genNode(child)];
295
+ });
296
+
297
+ if (expanded.length === 1) return `[${expanded[0]}]`;
298
+ return `[\n ${expanded.join(",\n ")}\n ]`;
299
+ }
300
+
301
+ _genForNode(node, forDir) {
302
+ this._imports.add("hFor");
303
+
304
+ const { item, source, index } = forDir.arg;
305
+ const sourceJs = this._wrapExpr(source);
306
+
307
+ this.localScopes.push(new Set([item, index].filter(Boolean)));
308
+
309
+ const innerProps = node.props.filter(
310
+ (p) => !(p.type === NodeType.DIRECTIVE && p.name === "for"),
311
+ );
312
+ const innerNode = { ...node, props: innerProps };
313
+ const keyBinding = innerProps.find(
314
+ (p) => p.type === NodeType.BINDING && p.name === "key",
315
+ );
316
+ const keyExpr = keyBinding ? keyBinding.expression.content : "undefined";
317
+ const itemParam = index ? `${item}, ${index}` : item;
318
+ const itemParamStr = `(${itemParam})`;
319
+ const innerJs = this._genNode(innerNode);
320
+ const keyJs = this._wrapExpr(keyExpr);
321
+
322
+ this.localScopes.pop();
323
+
324
+ return `hFor(\n () => ${sourceJs},\n ${itemParamStr} => ${innerJs},\n ${itemParamStr} => ${keyJs}\n )`;
325
+ }
326
+
327
+ isLocal(id) {
328
+ return this.localScopes.some((scope) => scope.has(id));
329
+ }
330
+
331
+ _wrapExpr(expr) {
332
+ if (expr.includes("_ctx.")) return expr;
333
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(expr.trim())) {
334
+ const id = expr.trim();
335
+ if (this.isLocal(id)) return id;
336
+ return `(typeof _ctx.${id} === 'function' && _ctx.${id}.isSignal ? _ctx.${id}() : _ctx.${id})`;
337
+ }
338
+
339
+ return this._rewriteExpr(expr);
340
+ }
341
+
342
+ _rewriteExpr(expr) {
343
+ const GLOBALS = new Set([
344
+ 'true', 'false', 'null', 'undefined', 'NaN', 'Infinity',
345
+ 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date',
346
+ 'Math', 'JSON', 'Promise', 'Map', 'Set', 'Symbol',
347
+ 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'console',
348
+ 'typeof', 'instanceof', 'void', 'delete', 'new', 'return',
349
+ 'if', 'else', 'for', 'while', 'do', 'switch', 'case',
350
+ 'break', 'continue', 'function', 'class', 'const', 'let',
351
+ 'var', 'import', 'export', 'default', 'this',
352
+ ]);
353
+
354
+ return expr.replace(
355
+ /(?<![.\w$])([a-zA-Z_$][a-zA-Z0-9_$]*)(?!\s*:)(?=\s*[\(\.\,\)\]\s+\|\&\!\?\+\-\*\/\%\=\<\>]|$)/g,
356
+ (match, id) => {
357
+ if (GLOBALS.has(id)) return match;
358
+ if (this.isLocal(id)) return match;
359
+ return `_ctx.${id}`;
360
+ }
361
+ );
362
+ }
363
+ }
@@ -0,0 +1,347 @@
1
+ import { CompileError, throwError } from "./errors.js";
2
+ import { ErrorCode } from "./errors-list.js";
3
+ import { createPos, createLoc } from "./ast.js";
4
+
5
+ export const TokenType = {
6
+ TAG_OPEN: "TAG_OPEN",
7
+ TAG_OPEN_CLOSE: "TAG_OPEN_CLOSE",
8
+ TAG_CLOSE: "TAG_CLOSE",
9
+ TAG_SELF_CLOSE: "TAG_SELF_CLOSE",
10
+ COMMENT: "COMMENT",
11
+ ATTR_NAME: "ATTR_NAME",
12
+ ATTR_EQUALS: "ATTR_EQUALS",
13
+ ATTR_VALUE: "ATTR_VALUE",
14
+ TEXT: "TEXT",
15
+ MUSTACHE_OPEN: "MUSTACHE_OPEN",
16
+ MUSTACHE_EXPR: "MUSTACHE_EXPR",
17
+ MUSTACHE_CLOSE: "MUSTACHE_CLOSE",
18
+ EOF: "EOF",
19
+ };
20
+
21
+ export function tokenize(source) {
22
+ const lexer = new Lexer(source);
23
+ return lexer.tokenize();
24
+ }
25
+
26
+ class Lexer {
27
+ constructor(source) {
28
+ this.source = source;
29
+ this.tokens = [];
30
+ this.pos = 0;
31
+ this.line = 1;
32
+ this.col = 1;
33
+ this._mode = "text";
34
+ }
35
+
36
+ tokenize() {
37
+ while (!this._eof()) {
38
+ if (this._mode === "text") {
39
+ this._readText();
40
+ } else if (this._mode === "tag") {
41
+ this._readInTag();
42
+ }
43
+ }
44
+ this._push(TokenType.EOF, "", this._startPos());
45
+ return this.tokens;
46
+ }
47
+
48
+ _readText() {
49
+ if (this._lookingAt("<!--")) {
50
+ this._readComment();
51
+ return;
52
+ }
53
+
54
+ if (this._lookingAt("</")) {
55
+ this._readClosingTag();
56
+ return;
57
+ }
58
+
59
+ if (this._peek() === "<" && this._isTagNameStart(this._peekAt(1))) {
60
+ this._readOpenTag();
61
+ return;
62
+ }
63
+
64
+ if (this._lookingAt("{{")) {
65
+ this._readMustache();
66
+ return;
67
+ }
68
+
69
+ const start = this._startPos();
70
+ let text = "";
71
+ while (!this._eof()) {
72
+ if (this._peek() === "<" || this._lookingAt("{{")) break;
73
+ text += this._advance();
74
+ }
75
+
76
+ if (text) this._push(TokenType.TEXT, text, start);
77
+ }
78
+
79
+ _readOpenTag() {
80
+ const start = this._startPos();
81
+ this._consume("<");
82
+ const name = this._readTagName();
83
+ this._pushAt(TokenType.TAG_OPEN, name, start);
84
+ this._mode = "tag";
85
+ }
86
+
87
+ _readClosingTag() {
88
+ const start = this._startPos();
89
+ this._consume("</");
90
+ const name = this._readTagName();
91
+ this._pushAt(TokenType.TAG_OPEN_CLOSE, name, start);
92
+ this._skipWhitespace();
93
+ if (!this._eof() && this._peek() === ">") {
94
+ const closeStart = this._startPos();
95
+ this._consume(">");
96
+ this._push(TokenType.TAG_CLOSE, ">", closeStart);
97
+ }
98
+ }
99
+
100
+ _readComment() {
101
+ const start = this._startPos();
102
+ this._consume("<!--");
103
+ let content = "";
104
+ while (!this._eof() && !this._lookingAt("-->")) {
105
+ content += this._advance();
106
+ }
107
+ if (this._eof()) {
108
+ throwError(
109
+ ErrorCode.MALFORMED_COMMENT,
110
+ createLoc(start, this._startPos()),
111
+ this.source,
112
+ );
113
+ }
114
+ this._consume("-->");
115
+ this._push(TokenType.COMMENT, content.trim(), start);
116
+ }
117
+
118
+ _readMustache() {
119
+ const openStart = this._startPos();
120
+ this._consume("{{");
121
+ this._push(TokenType.MUSTACHE_OPEN, "{{", openStart);
122
+
123
+ const exprStart = this._startPos();
124
+ let expr = "";
125
+ let depth = 0;
126
+ let inStr = false;
127
+ let strChar = null;
128
+
129
+ while (!this._eof()) {
130
+ const ch = this._peek();
131
+
132
+ if (inStr) {
133
+ if (ch === "\\") {
134
+ expr += this._advance();
135
+ expr += this._advance();
136
+ continue;
137
+ }
138
+ if (ch === strChar) {
139
+ inStr = false;
140
+ }
141
+ expr += this._advance();
142
+ continue;
143
+ }
144
+
145
+ if (ch === '"' || ch === "'" || ch === "`") {
146
+ inStr = true;
147
+ strChar = ch;
148
+ expr += this._advance();
149
+ continue;
150
+ }
151
+ if (ch === "{") {
152
+ depth++;
153
+ expr += this._advance();
154
+ continue;
155
+ }
156
+ if (ch === "}") {
157
+ if (depth > 0) {
158
+ depth--;
159
+ expr += this._advance();
160
+ continue;
161
+ }
162
+ if (this._peekAt(1) === "}") {
163
+ this._push(TokenType.MUSTACHE_EXPR, expr.trim(), exprStart);
164
+ const closeStart = this._startPos();
165
+ this._consume("}}");
166
+ this._push(TokenType.MUSTACHE_CLOSE, "}}", closeStart);
167
+ return;
168
+ }
169
+ }
170
+
171
+ expr += this._advance();
172
+ }
173
+
174
+ throwError(
175
+ ErrorCode.UNTERMINATED_MUSTACHE,
176
+ createLoc(openStart, this._startPos()),
177
+ this.source,
178
+ expr,
179
+ );
180
+ }
181
+
182
+ _readInTag() {
183
+ this._skipWhitespace();
184
+ if (this._eof()) return;
185
+
186
+ if (this._match("/>")) {
187
+ this._push(TokenType.TAG_SELF_CLOSE, "/>", this._startPos());
188
+ this._mode = "text";
189
+ return;
190
+ }
191
+
192
+ if (this._peek() === ">") {
193
+ this._consume(">");
194
+ this._push(TokenType.TAG_CLOSE, ">", this._startPos());
195
+ this._mode = "text";
196
+ return;
197
+ }
198
+
199
+ const start = this._startPos();
200
+ const name = this._readAttrName();
201
+ if (!name) return;
202
+
203
+ this._push(TokenType.ATTR_NAME, name, start);
204
+ this._skipWhitespace();
205
+
206
+ if (this._peek() === "=") {
207
+ this._consume("=");
208
+ this._push(TokenType.ATTR_EQUALS, "=", this._startPos());
209
+ this._skipWhitespace();
210
+ this._readAttrValue();
211
+ }
212
+ }
213
+
214
+ _readAttrName() {
215
+ let name = "";
216
+
217
+ if (this._peek() === "@") {
218
+ name += this._advance();
219
+ } else if (this._peek() === ":") {
220
+ name += this._advance();
221
+ }
222
+
223
+ while (!this._eof()) {
224
+ const ch = this._peek();
225
+ if (/[a-zA-Z0-9\-_.:]/.test(ch)) {
226
+ name += this._advance();
227
+ } else break;
228
+ }
229
+
230
+ return name || null;
231
+ }
232
+
233
+ _readAttrValue() {
234
+ if (this._eof()) return;
235
+ const quote = this._peek();
236
+ if (quote !== '"' && quote !== "'") {
237
+ const start = this._startPos();
238
+ let val = "";
239
+ while (!this._eof() && !/[\s>]/.test(this._peek())) {
240
+ val += this._advance();
241
+ }
242
+ this._push(TokenType.ATTR_VALUE, val, start);
243
+ return;
244
+ }
245
+
246
+ this._consume(quote);
247
+ const start = this._startPos();
248
+ let val = "";
249
+
250
+ while (!this._eof()) {
251
+ const ch = this._peek();
252
+ if (ch === quote) {
253
+ this._consume(quote);
254
+ break;
255
+ }
256
+ if (ch === "\\") {
257
+ this._advance();
258
+ val += this._advance();
259
+ continue;
260
+ }
261
+ val += this._advance();
262
+ }
263
+
264
+ this._push(TokenType.ATTR_VALUE, val, start);
265
+ }
266
+
267
+ _readTagName() {
268
+ let name = "";
269
+ while (!this._eof() && /[a-zA-Z0-9\-_.]/.test(this._peek())) {
270
+ name += this._advance();
271
+ }
272
+ return name;
273
+ }
274
+
275
+ _isTagNameStart(ch) {
276
+ return ch != null && /[a-zA-Z_]/.test(ch);
277
+ }
278
+
279
+ _eof() {
280
+ return this.pos >= this.source.length;
281
+ }
282
+
283
+ _peek() {
284
+ return this.source[this.pos];
285
+ }
286
+
287
+ _peekAt(n) {
288
+ return this.source[this.pos + n];
289
+ }
290
+
291
+ _lookingAt(str) {
292
+ return this.source.startsWith(str, this.pos);
293
+ }
294
+
295
+ _match(str) {
296
+ if (this._lookingAt(str)) {
297
+ for (const ch of str) this._advance();
298
+ return true;
299
+ }
300
+ return false;
301
+ }
302
+
303
+ _consume(expected) {
304
+ for (const ch of expected) {
305
+ if (this.source[this.pos] !== ch) {
306
+ throwError(
307
+ ErrorCode.UNEXPECTED_CHAR,
308
+ createLoc(this._startPos(), this._startPos()),
309
+ this.source,
310
+ this.source[this.pos],
311
+ );
312
+ }
313
+ this._advance();
314
+ }
315
+ }
316
+
317
+ _advance() {
318
+ const ch = this.source[this.pos++];
319
+ if (ch === "\n") {
320
+ this.line++;
321
+ this.col = 1;
322
+ } else {
323
+ this.col++;
324
+ }
325
+ return ch;
326
+ }
327
+
328
+ _skipWhitespace() {
329
+ while (!this._eof() && /\s/.test(this._peek())) this._advance();
330
+ }
331
+
332
+ _startPos() {
333
+ return createPos(this.line, this.col, this.pos);
334
+ }
335
+
336
+ _push(type, value, start) {
337
+ this.tokens.push({
338
+ type,
339
+ value,
340
+ loc: createLoc(start, this._startPos(), value),
341
+ });
342
+ }
343
+
344
+ _pushAt(type, value, start) {
345
+ this._push(type, value, start);
346
+ }
347
+ }