fusee-framework 1.8.0 → 1.9.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.
Files changed (49) 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/framework/auto-import-preset.js +3 -1
  5. package/framework/core/async-context.js +27 -0
  6. package/framework/core/compiler/ast.js +167 -0
  7. package/framework/core/compiler/errors-list.js +47 -0
  8. package/framework/core/compiler/errors.js +107 -0
  9. package/framework/core/compiler/generator.js +363 -0
  10. package/framework/core/compiler/lexer.js +347 -0
  11. package/framework/core/compiler/main-compiler.js +115 -0
  12. package/framework/core/compiler/parser.js +414 -0
  13. package/framework/core/compiler/plugins/actions-plugin.js +263 -0
  14. package/framework/core/compiler/plugins/compiler-plugin.d.ts +5 -0
  15. package/framework/core/compiler/plugins/compiler-plugin.js +45 -0
  16. package/framework/core/compiler/plugins/file-router-plugin.js +301 -0
  17. package/framework/core/compiler/plugins/router-plugin.js +196 -0
  18. package/framework/core/compiler/transformer.js +398 -0
  19. package/framework/core/component.js +34 -10
  20. package/framework/core/evaluator.js +1 -1
  21. package/framework/core/h.js +430 -0
  22. package/framework/core/hydration.js +83 -0
  23. package/framework/core/signal.js +17 -15
  24. package/framework/server/actions.js +173 -0
  25. package/framework/server/actions.server.js +88 -0
  26. package/framework/server/index.js +18 -0
  27. package/framework/types/actions.d.ts +50 -0
  28. package/framework/types/actions.server.d.ts +36 -0
  29. package/framework/types/compiler/ast.d.ts +166 -0
  30. package/framework/types/compiler/errors-codes.d.ts +23 -0
  31. package/framework/types/compiler/errors.d.ts +43 -0
  32. package/framework/types/compiler/generator.d.ts +8 -0
  33. package/framework/types/compiler/h.d.ts +46 -0
  34. package/framework/types/compiler/lexer.d.ts +27 -0
  35. package/framework/types/compiler/main-compiler.d.ts +60 -0
  36. package/framework/types/compiler/parser.d.ts +4 -0
  37. package/framework/types/compiler/plugins/actions.d.ts +30 -0
  38. package/framework/types/compiler/plugins/compiler-plugin.d.ts +5 -0
  39. package/framework/types/compiler/plugins/file-router-plugins.d.ts +32 -0
  40. package/framework/types/compiler/plugins/router-plugin.d.ts +24 -0
  41. package/framework/types/compiler/transformer.d.ts +28 -0
  42. package/framework/types/hydration.d.ts +30 -0
  43. package/framework/types/index.d.ts +6 -0
  44. package/framework/types/server.d.ts +11 -0
  45. package/framework/types/signal.d.ts +2 -7
  46. package/ghid_go_fusee_integrat_go1_26.txt +276 -0
  47. package/ghid_rust_fusee_rust_1_95_0.txt +1118 -0
  48. package/package.json +44 -36
  49. package/vite.config.js +65 -53
@@ -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
+ }