@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.94

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 (102) hide show
  1. package/dist/analyze/intrinsics.cjs +31 -1
  2. package/dist/analyze/intrinsics.d.cts +14 -0
  3. package/dist/analyze/intrinsics.d.ts +14 -0
  4. package/dist/analyze/intrinsics.d.ts.map +1 -1
  5. package/dist/analyze/intrinsics.js +35 -0
  6. package/dist/analyze/intrinsics.mjs +31 -1
  7. package/dist/analyze/reactive-bindings.cjs +18 -0
  8. package/dist/analyze/reactive-bindings.mjs +18 -0
  9. package/dist/analyze/static-graph.cjs +22 -4
  10. package/dist/analyze/static-graph.d.ts.map +1 -1
  11. package/dist/analyze/static-graph.js +6 -4
  12. package/dist/analyze/static-graph.mjs +22 -4
  13. package/dist/{babel-GI375YZ2.mjs → babel-RJFQVFJW.mjs} +4 -4
  14. package/dist/{chunk-QZ4CJPYP.mjs → chunk-DP2QDWWH.mjs} +1 -1
  15. package/dist/{chunk-DWNWN5RH.mjs → chunk-E2RWTIYE.mjs} +21 -15
  16. package/dist/{chunk-XJ5H2MXY.mjs → chunk-HCCBEJEZ.mjs} +31 -1
  17. package/dist/{chunk-VOUI6XPV.mjs → chunk-HIZU7WI2.mjs} +1 -1
  18. package/dist/{chunk-VC6X44FS.mjs → chunk-ZR2QHTEX.mjs} +505 -68
  19. package/dist/codegen/backends/imperative.cjs +24 -6
  20. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  21. package/dist/codegen/backends/imperative.js +20 -4
  22. package/dist/codegen/backends/imperative.mjs +24 -6
  23. package/dist/frontend/babel/build-ir-lit.cjs +470 -63
  24. package/dist/frontend/babel/build-ir-lit.mjs +470 -63
  25. package/dist/frontend/babel/build-ir.cjs +470 -63
  26. package/dist/frontend/babel/build-ir.mjs +470 -63
  27. package/dist/frontend/babel/index.cjs +471 -64
  28. package/dist/frontend/babel/index.mjs +471 -64
  29. package/dist/frontend/babel/lower-template.cjs +470 -63
  30. package/dist/frontend/babel/lower-template.mjs +470 -63
  31. package/dist/frontend/babel/plugin.cjs +470 -63
  32. package/dist/frontend/babel/plugin.mjs +470 -63
  33. package/dist/index.cjs +360 -58
  34. package/dist/index.mjs +372 -58
  35. package/dist/integrations.cjs +1347 -98
  36. package/dist/integrations.d.ts.map +1 -1
  37. package/dist/integrations.js +44 -2
  38. package/dist/integrations.mjs +205 -19
  39. package/dist/lower/compile-template.cjs +354 -57
  40. package/dist/lower/compile-template.d.cts +13 -0
  41. package/dist/lower/compile-template.d.ts +13 -0
  42. package/dist/lower/compile-template.d.ts.map +1 -1
  43. package/dist/lower/compile-template.js +3 -0
  44. package/dist/lower/compile-template.mjs +366 -57
  45. package/dist/lower/entities.cjs +288 -0
  46. package/dist/lower/entities.d.cts +1 -0
  47. package/dist/lower/entities.d.ts +2 -0
  48. package/dist/lower/entities.d.ts.map +1 -0
  49. package/dist/lower/entities.js +54 -0
  50. package/dist/lower/entities.mjs +267 -0
  51. package/dist/lower/jsx.cjs +567 -7
  52. package/dist/lower/jsx.d.cts +8 -0
  53. package/dist/lower/jsx.d.ts +8 -0
  54. package/dist/lower/jsx.d.ts.map +1 -1
  55. package/dist/lower/jsx.js +205 -7
  56. package/dist/lower/jsx.mjs +567 -7
  57. package/dist/lower/template.cjs +332 -56
  58. package/dist/lower/template.d.ts.map +1 -1
  59. package/dist/lower/template.js +57 -49
  60. package/dist/lower/template.mjs +332 -56
  61. package/dist/resolve/builtins.cjs +1 -1
  62. package/dist/resolve/builtins.mjs +1 -1
  63. package/dist/resolve/component-globals.cjs +20 -1
  64. package/dist/resolve/component-globals.js +2 -2
  65. package/dist/resolve/component-globals.mjs +20 -1
  66. package/dist/resolve/write-globals.cjs +20 -1
  67. package/dist/resolve/write-globals.mjs +20 -1
  68. package/dist/{server-fns-BBZRZUPP.mjs → server-fns-JLSTKWPS.mjs} +4 -4
  69. package/dist/{server-functions-VEGLIFCE.mjs → server-functions-BGY3RD4R.mjs} +3 -3
  70. package/dist/transform/index.cjs +1123 -77
  71. package/dist/transform/index.mjs +1141 -91
  72. package/dist/transform/intrinsics.cjs +18 -0
  73. package/dist/transform/intrinsics.mjs +18 -0
  74. package/dist/transform/props.mjs +19 -14
  75. package/dist/transform/server-fns.mjs +19 -14
  76. package/dist/transform/source-locations.cjs +64 -3
  77. package/dist/transform/source-locations.d.cts +7 -2
  78. package/dist/transform/source-locations.d.ts +7 -2
  79. package/dist/transform/source-locations.d.ts.map +1 -1
  80. package/dist/transform/source-locations.js +68 -3
  81. package/dist/transform/source-locations.mjs +64 -3
  82. package/dist/transform/styles.cjs +588 -0
  83. package/dist/transform/styles.d.cts +211 -0
  84. package/dist/transform/styles.d.ts +212 -0
  85. package/dist/transform/styles.d.ts.map +1 -0
  86. package/dist/transform/styles.js +714 -0
  87. package/dist/transform/styles.mjs +653 -0
  88. package/dist/transform/templates.cjs +1122 -76
  89. package/dist/transform/templates.d.cts +13 -0
  90. package/dist/transform/templates.d.ts +13 -0
  91. package/dist/transform/templates.d.ts.map +1 -1
  92. package/dist/transform/templates.js +51 -14
  93. package/dist/transform/templates.mjs +1140 -90
  94. package/dist/{transform-UWZ7ZGJ2.mjs → transform-GBP6Y4UI.mjs} +603 -19
  95. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  96. package/dist/vite-styles.cjs +180 -0
  97. package/dist/vite-styles.d.cts +59 -0
  98. package/dist/vite-styles.d.ts +60 -0
  99. package/dist/vite-styles.d.ts.map +1 -0
  100. package/dist/vite-styles.js +198 -0
  101. package/dist/vite-styles.mjs +159 -0
  102. package/package.json +10 -3
package/dist/lower/jsx.js CHANGED
@@ -4,6 +4,8 @@ import { shapeOf } from '../analyze/expr-shape.js';
4
4
  import { analyzeStatic } from '../optimize/analyze-static.js';
5
5
  import { cleanTemplateText } from './text.js';
6
6
  import { paren } from '../analyze/paren.js';
7
+ import { markHole } from '../transform/source-locations.js';
8
+ import { imperativeBackend, inlineTemplates } from '../codegen/backends/imperative.js';
7
9
  export const SVG_TAGS = new Set([
8
10
  'svg', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline',
9
11
  'ellipse', 'g', 'defs', 'clipPath', 'text',
@@ -14,6 +16,29 @@ const DELEGATED = new Set([
14
16
  'keydown', 'keyup', 'keypress', 'mousedown', 'mouseup',
15
17
  ]);
16
18
  const isReactive = (node) => isReactiveShape(shapeOf(node));
19
+ /** The tag that carries directives and renders only its content. Never a custom element: no dash. */
20
+ const CONTAINER = 'container';
21
+ /** An intrinsic element's `if`, `else`, `each` or `key` attribute, as written. */
22
+ function directive(node, name) {
23
+ if (node.type !== 'JSXElement')
24
+ return undefined;
25
+ const opening = node.openingElement;
26
+ if (opening.name.type !== 'JSXIdentifier' || opening.name.name[0] !== opening.name.name[0].toLowerCase())
27
+ return undefined;
28
+ return opening.attributes.find((a) => a.type === 'JSXAttribute' && a.name.type === 'JSXIdentifier' && a.name.name === name);
29
+ }
30
+ /** The expression a directive holds: `if={cond}`. */
31
+ function directiveExpr(attr) {
32
+ const v = attr.value;
33
+ return v?.type === 'JSXExpressionContainer' && v.expression?.type !== 'JSXEmptyExpression' ? v.expression : undefined;
34
+ }
35
+ /** A nested subtree as code, for `Show`'s fallback and `For`'s render function. */
36
+ function emitIR(ir, ctx) {
37
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
38
+ for (const s of imports)
39
+ ctx.used.add(s);
40
+ return inlineTemplates(code, templates);
41
+ }
17
42
  /** Only when the host asked for source locations, the flag that records usage sites too. */
18
43
  const posOf = (node, ctx) => {
19
44
  if (!ctx.sourceFile)
@@ -108,9 +133,18 @@ function attrExprCode(attr, ctx) {
108
133
  }
109
134
  return null;
110
135
  }
111
- function buildProps(attrs, ctx) {
136
+ /**
137
+ * `element`: the tag is a DOM element. Only there are class:/style: and a style object taken
138
+ * apart; a component gets its props as written.
139
+ */
140
+ function buildProps(attrs, ctx, element = false) {
112
141
  const props = [];
113
142
  const usePairs = []; // use:name directives collapse into one `use` prop
143
+ // class:x and style:x collapse into one classList / style object, as in templates; each
144
+ // value that moves is an accessor in it, so the runtime updates that one key.
145
+ const classEntries = [];
146
+ const styleEntries = [];
147
+ let styleAt;
114
148
  for (const a of attrs) {
115
149
  if (a.type === 'JSXSpreadAttribute') {
116
150
  props.push({ name: '', kind: 'spread', expr: ctx.code(a.argument) });
@@ -131,6 +165,33 @@ function buildProps(attrs, ctx) {
131
165
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, 'arrowBody')}]` : `[${dir}]`);
132
166
  continue;
133
167
  }
168
+ if (element && (ns === 'class' || ns === 'style')) {
169
+ const key = JSON.stringify(a.name.name.name);
170
+ const v = a.value;
171
+ let value;
172
+ if (v == null)
173
+ value = 'true';
174
+ else if (isString(v))
175
+ value = JSON.stringify(literalOf(v));
176
+ else if (v.type === 'JSXExpressionContainer' && v.expression?.type !== 'JSXEmptyExpression') {
177
+ value = keyValue(v.expression, ctx);
178
+ if (ns === 'style')
179
+ styleAt ??= posOf(v.expression, ctx);
180
+ }
181
+ else
182
+ continue;
183
+ (ns === 'class' ? classEntries : styleEntries).push(`${key}: ${value}`);
184
+ continue;
185
+ }
186
+ // style={{ … }}: one entry per property, so `--gap` moving does not rewrite `margin`.
187
+ if (element && a.name.type === 'JSXIdentifier' && a.name.name === 'style' && a.value?.type === 'JSXExpressionContainer') {
188
+ const entries = objectEntries(a.value.expression, ctx);
189
+ if (entries) {
190
+ styleEntries.unshift(...entries);
191
+ styleAt = posOf(a.value.expression, ctx);
192
+ continue;
193
+ }
194
+ }
134
195
  // oncapture:name -> native on:name with { capture: true }
135
196
  if (ns === 'oncapture') {
136
197
  const evt = a.name.name.name.toLowerCase();
@@ -140,13 +201,66 @@ function buildProps(attrs, ctx) {
140
201
  }
141
202
  props.push(buildAttr(a, ctx));
142
203
  }
204
+ if (classEntries.length > 0)
205
+ props.push({ name: 'classList', kind: 'attr', expr: `{ ${classEntries.join(', ')} }`, reactive: false });
206
+ if (styleEntries.length > 0) {
207
+ const entries = `{ ${styleEntries.join(', ')} }`;
208
+ // A style the object form could not take apart (a string, a variable) goes first, and
209
+ // the keys after it: the runtime applies an array in order.
210
+ const base = props.findIndex((p) => p.name === 'style' && p.kind === 'attr');
211
+ if (base === -1)
212
+ props.push({ name: 'style', kind: 'attr', expr: entries, reactive: false, ...(styleAt ? { at: styleAt } : {}) });
213
+ else {
214
+ const prop = props[base];
215
+ const value = prop.literal !== undefined ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, 'arrowBody')}` : prop.expr;
216
+ props[base] = { name: 'style', kind: 'attr', expr: `[${value}, ${entries}]`, reactive: false, ...(prop.at ? { at: prop.at } : {}) };
217
+ }
218
+ }
143
219
  if (usePairs.length > 0)
144
220
  props.push({ name: 'use', kind: 'attr', expr: `[${usePairs.join(', ')}]` });
145
221
  return props;
146
222
  }
223
+ /** A value inside a classList / style object: an accessor when it moves. */
224
+ function keyValue(expr, ctx) {
225
+ const code = ctx.code(expr);
226
+ if (!isReactive(expr))
227
+ return code;
228
+ const accessor = `() => ${paren(code, 'arrowBody')}`;
229
+ const at = posOf(expr, ctx);
230
+ return at ? markHole(accessor, at.line, at.column) : accessor;
231
+ }
232
+ /** `{ a: x, '--b': y }` as object entries; null for anything with a spread or a method. */
233
+ function objectEntries(expr, ctx) {
234
+ if (expr?.type !== 'ObjectExpression')
235
+ return null;
236
+ const entries = [];
237
+ for (const p of expr.properties) {
238
+ if (p.type !== 'ObjectProperty')
239
+ return null;
240
+ const key = p.computed
241
+ ? `[${ctx.code(p.key)}]`
242
+ : p.key.type === 'Identifier'
243
+ ? JSON.stringify(p.key.name)
244
+ : JSON.stringify(String(p.key.value));
245
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
246
+ }
247
+ return entries;
248
+ }
147
249
  function buildChildren(children, ctx) {
148
250
  const out = [];
149
- for (const child of children) {
251
+ for (let i = 0; i < children.length; i++) {
252
+ const child = children[i];
253
+ if (directive(child, 'if')) {
254
+ // The next element, past whitespace, is the fallback when it says `else`.
255
+ let next = i + 1;
256
+ while (next < children.length && children[next].type === 'JSXText' && !cleanTemplateText(children[next].value))
257
+ next++;
258
+ const fallback = directive(children[next], 'else') ? children[next] : null;
259
+ out.push(buildIf(child, fallback, ctx));
260
+ if (fallback)
261
+ i = next;
262
+ continue;
263
+ }
150
264
  if (child.type === 'JSXText') {
151
265
  // Drop whitespace-only text spanning a newline so a lone child stays lone ,
152
266
  // an array insert will not reactively bind a memo/component inside it.
@@ -167,6 +281,9 @@ function buildChildren(children, ctx) {
167
281
  }
168
282
  }
169
283
  else if (child.type === 'JSXElement' || child.type === 'JSXFragment') {
284
+ // A `<style>` the compiler took out leaves no child behind, not even an empty insert.
285
+ if (ctx.dropStyles?.has(child.start))
286
+ continue;
170
287
  out.push(buildNode(child, ctx));
171
288
  }
172
289
  else if (child.type === 'JSXSpreadChild') {
@@ -190,21 +307,102 @@ function readLoad(attrs) {
190
307
  }
191
308
  return undefined;
192
309
  }
310
+ /** `<el if={cond}>` and an optional `<el else>` after it → `<Show when fallback>`. */
311
+ function buildIf(node, fallback, ctx) {
312
+ const cond = directiveExpr(directive(node, 'if'));
313
+ const props = [
314
+ cond
315
+ ? { name: 'when', kind: 'attr', expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) }
316
+ : { name: 'when', kind: 'attr', literal: true },
317
+ ];
318
+ if (fallback) {
319
+ props.push({ name: 'fallback', kind: 'attr', expr: emitIR(buildElement(fallback, ctx, ['else']), ctx), jsxElement: true });
320
+ }
321
+ return { kind: 'component', name: 'Show', props, children: [buildElement(node, ctx, ['if'])] };
322
+ }
323
+ /**
324
+ * `<el each={items} key?>{(item) => …}</el>` → `<el><For each by>{…}</For></el>`: the
325
+ * content repeats, the element does not. Null when there is no `each`.
326
+ */
327
+ function buildEach(node, ctx) {
328
+ const attr = directive(node, 'each');
329
+ if (!attr)
330
+ return null;
331
+ const list = directiveExpr(attr);
332
+ const props = [
333
+ list
334
+ ? { name: 'each', kind: 'attr', expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) }
335
+ : { name: 'each', kind: 'attr', expr: '[]' },
336
+ ];
337
+ const key = directive(node, 'key');
338
+ if (key) {
339
+ const keyExpr = directiveExpr(key);
340
+ const by = isString(key.value)
341
+ ? `(item) => item[${JSON.stringify(literalOf(key.value))}]`
342
+ : keyExpr
343
+ ? ctx.code(keyExpr)
344
+ : undefined;
345
+ if (by)
346
+ props.push({ name: 'by', kind: 'attr', expr: by });
347
+ }
348
+ const content = node.children.filter((c) => !(c.type === 'JSXText' && !cleanTemplateText(c.value)));
349
+ const only = content.length === 1 && content[0].type === 'JSXExpressionContainer' ? content[0].expression : null;
350
+ let render;
351
+ if (only && (only.type === 'ArrowFunctionExpression' || only.type === 'FunctionExpression')) {
352
+ const fn = ctx.code(only);
353
+ // A body that reads signals, `{(it) => it.label + count()}`, is handed back as an
354
+ // accessor so the runtime binds it; a body that builds elements needs nothing.
355
+ const live = only.type === 'ArrowFunctionExpression' && only.body.type !== 'BlockStatement' && isReactive(only.body);
356
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
357
+ }
358
+ else {
359
+ const built = buildChildren(node.children, ctx);
360
+ const ir = built.length === 1 ? built[0] : { kind: 'fragment', children: built };
361
+ render = `() => ${paren(emitIR(ir, ctx), 'arrowBody')}`;
362
+ }
363
+ return { kind: 'component', name: 'For', props, children: [{ kind: 'expr', code: render, reactive: false }] };
364
+ }
365
+ /** An element, with the directives already handled by the caller left out. */
366
+ function buildElement(node, ctx, handled) {
367
+ const skip = new Set(handled);
368
+ // `key` is only a directive beside `each`; anywhere else it is an ordinary attribute.
369
+ const directives = new Set(['if', 'else', 'each', ...(directive(node, 'each') ? ['key'] : [])]);
370
+ const attributes = node.openingElement.attributes.filter((a) => !(a.type === 'JSXAttribute' && a.name.type === 'JSXIdentifier' && (skip.has(a.name.name) || directives.has(a.name.name))));
371
+ const { tag } = tagInfo(node.openingElement.name, ctx);
372
+ const each = buildEach(node, ctx);
373
+ const children = each ? [each] : buildChildren(node.children, ctx);
374
+ if (tag === CONTAINER)
375
+ return children.length === 1 ? children[0] : { kind: 'fragment', children };
376
+ const at = ctx.sourceFile && node.loc?.start
377
+ ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column }
378
+ : undefined;
379
+ const props = buildProps(attributes, ctx, true);
380
+ // A scoped style's mark: a static attribute, so it is part of the template itself and
381
+ // every render carries it, on the client, from the server and through hydration.
382
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
383
+ if (scope)
384
+ props.unshift({ name: 'data-fx-s', kind: 'attr', literal: scope.value });
385
+ return { kind: 'element', tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...(at ? { at } : {}) };
386
+ }
193
387
  function buildNode(node, ctx) {
194
388
  if (node.type === 'JSXFragment') {
195
389
  return { kind: 'fragment', children: buildChildren(node.children, ctx) };
196
390
  }
391
+ if (directive(node, 'if'))
392
+ return buildIf(node, null, ctx);
393
+ // Its CSS went to the host; nothing of the element is rendered.
394
+ if (ctx.dropStyles?.has(node.start))
395
+ return { kind: 'fragment', children: [] };
197
396
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
397
+ if (!component)
398
+ return buildElement(node, ctx, []);
198
399
  const props = buildProps(node.openingElement.attributes, ctx);
199
400
  const children = buildChildren(node.children, ctx);
200
401
  const at = ctx.sourceFile && node.loc?.start
201
402
  ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column }
202
403
  : undefined;
203
- if (component) {
204
- const load = readLoad(node.openingElement.attributes);
205
- return { kind: 'component', name: tag, props, children, ...(load ? { load } : {}), ...(at ? { at } : {}) };
206
- }
207
- return { kind: 'element', tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...(at ? { at } : {}) };
404
+ const load = readLoad(node.openingElement.attributes);
405
+ return { kind: 'component', name: tag, props, children, ...(load ? { load } : {}), ...(at ? { at } : {}) };
208
406
  }
209
407
  /** Build IR from a JSXElement or JSXFragment. */
210
408
  export function buildJsxIR(node, ctx) {