@fluixi/compiler 1.0.0-alpha.100 → 1.0.0-alpha.101
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.
- package/README.md +13 -13
- package/dist/analyze/props-destructure.d.cts +1 -1
- package/dist/analyze/props-destructure.d.ts +1 -1
- package/dist/analyze/props-destructure.js +1 -1
- package/dist/analyze/reactive-bindings.d.cts +1 -1
- package/dist/analyze/reactive-bindings.d.ts +1 -1
- package/dist/analyze/reactive-bindings.js +3 -3
- package/dist/analyze/static-graph.cjs +1 -1
- package/dist/analyze/static-graph.js +2 -2
- package/dist/analyze/static-graph.mjs +1 -1
- package/dist/{babel-I6BUWUCU.mjs → babel-4WLP2IRR.mjs} +23 -23
- package/dist/{chunk-SKAVY75W.mjs → chunk-46MIOLUB.mjs} +1 -1
- package/dist/{chunk-C3XPDB4Y.mjs → chunk-AIK66L2P.mjs} +1 -1
- package/dist/{chunk-3AICWNF7.mjs → chunk-B6GUZXJP.mjs} +1 -1
- package/dist/{chunk-HXEXPWJ2.mjs → chunk-BVY2ISKX.mjs} +2 -2
- package/dist/{chunk-6CWJWGE2.mjs → chunk-G2PQDYFN.mjs} +10 -10
- package/dist/{chunk-GX5XV6Z7.mjs → chunk-K4Q6GHV6.mjs} +1 -1
- package/dist/{chunk-NU3W67UZ.mjs → chunk-S3LT32LA.mjs} +2 -2
- package/dist/codegen/backends/imperative.js +2 -2
- package/dist/frontend/babel/{build-ir-lit.cjs → build-ir-html.cjs} +19 -19
- package/dist/frontend/babel/{build-ir-lit.d.cts → build-ir-html.d.cts} +5 -5
- package/dist/frontend/babel/{build-ir-lit.d.ts → build-ir-html.d.ts} +6 -6
- package/dist/frontend/babel/build-ir-html.d.ts.map +1 -0
- package/dist/frontend/babel/{build-ir-lit.js → build-ir-html.js} +4 -4
- package/dist/frontend/babel/{build-ir-lit.mjs → build-ir-html.mjs} +15 -15
- package/dist/frontend/babel/build-ir.cjs +14 -14
- package/dist/frontend/babel/build-ir.d.cts +1 -1
- package/dist/frontend/babel/build-ir.d.ts +1 -1
- package/dist/frontend/babel/build-ir.js +5 -5
- package/dist/frontend/babel/build-ir.mjs +14 -14
- package/dist/frontend/babel/index.cjs +28 -28
- package/dist/frontend/babel/index.d.cts +0 -11
- package/dist/frontend/babel/index.d.ts +0 -11
- package/dist/frontend/babel/index.d.ts.map +1 -1
- package/dist/frontend/babel/index.js +2 -14
- package/dist/frontend/babel/index.mjs +28 -28
- package/dist/frontend/babel/lower-template.cjs +15 -15
- package/dist/frontend/babel/lower-template.d.cts +3 -3
- package/dist/frontend/babel/lower-template.d.ts +3 -3
- package/dist/frontend/babel/lower-template.d.ts.map +1 -1
- package/dist/frontend/babel/lower-template.js +4 -4
- package/dist/frontend/babel/lower-template.mjs +15 -15
- package/dist/frontend/babel/plugin.cjs +25 -25
- package/dist/frontend/babel/plugin.d.cts +6 -22
- package/dist/frontend/babel/plugin.d.ts +6 -22
- package/dist/frontend/babel/plugin.d.ts.map +1 -1
- package/dist/frontend/babel/plugin.js +19 -42
- package/dist/frontend/babel/plugin.mjs +25 -25
- package/dist/frontend/babel/server-functions.js +2 -2
- package/dist/index.cjs +9 -9
- package/dist/index.mjs +9 -9
- package/dist/integrations.cjs +42 -42
- package/dist/integrations.d.cts +7 -22
- package/dist/integrations.d.ts +7 -22
- package/dist/integrations.d.ts.map +1 -1
- package/dist/integrations.js +3 -34
- package/dist/integrations.mjs +13 -13
- package/dist/ir/nodes.d.cts +1 -1
- package/dist/ir/nodes.d.ts +1 -1
- package/dist/lower/compile-template.cjs +9 -9
- package/dist/lower/compile-template.d.cts +2 -2
- package/dist/lower/compile-template.d.ts +2 -2
- package/dist/lower/compile-template.d.ts.map +1 -1
- package/dist/lower/compile-template.js +1 -1
- package/dist/lower/compile-template.mjs +9 -9
- package/dist/lower/jsx.js +2 -2
- package/dist/lower/template.cjs +8 -8
- package/dist/lower/template.d.cts +6 -6
- package/dist/lower/template.d.ts +6 -6
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +14 -14
- package/dist/lower/template.mjs +8 -8
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.d.cts +2 -2
- package/dist/resolve/builtins.d.ts +2 -2
- package/dist/resolve/builtins.js +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.d.cts +2 -2
- package/dist/resolve/component-globals.d.ts +2 -2
- package/dist/resolve/component-globals.js +1 -1
- package/dist/resolve/load-directive.d.cts +1 -1
- package/dist/resolve/load-directive.d.ts +1 -1
- package/dist/resolve/load-directive.js +1 -1
- package/dist/resolve/resolve-ir.d.cts +1 -1
- package/dist/resolve/resolve-ir.d.ts +1 -1
- package/dist/{server-fns-VZLHMQTC.mjs → server-fns-THMTJXV7.mjs} +5 -5
- package/dist/{server-functions-UAZKOLZW.mjs → server-functions-MP4I3DAX.mjs} +3 -3
- package/dist/transform/bindings.d.cts +1 -1
- package/dist/transform/bindings.d.ts +1 -1
- package/dist/transform/bindings.js +1 -1
- package/dist/transform/index.cjs +17 -17
- package/dist/transform/index.mjs +17 -17
- package/dist/transform/intrinsics.d.cts +1 -1
- package/dist/transform/intrinsics.d.ts +1 -1
- package/dist/transform/intrinsics.js +1 -1
- package/dist/transform/source-locations.d.cts +1 -1
- package/dist/transform/source-locations.d.ts +1 -1
- package/dist/transform/source-locations.d.ts.map +1 -1
- package/dist/transform/source-locations.js +6 -6
- package/dist/transform/styles.d.cts +2 -2
- package/dist/transform/styles.d.ts +2 -2
- package/dist/transform/styles.d.ts.map +1 -1
- package/dist/transform/styles.js +5 -5
- package/dist/transform/templates.cjs +16 -16
- package/dist/transform/templates.d.cts +2 -2
- package/dist/transform/templates.d.ts +2 -2
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +10 -10
- package/dist/transform/templates.mjs +16 -16
- package/dist/{transform-3W45L3EA.mjs → transform-27QH3X24.mjs} +15 -15
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/vite-styles.js +2 -2
- package/package.json +4 -4
- package/dist/frontend/babel/build-ir-lit.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# @fluixi/compiler
|
|
6
6
|
|
|
7
|
-
**The Fluixi compiler
|
|
7
|
+
**The Fluixi compiler: parses, lowers and emits. Not a Babel plugin.**
|
|
8
8
|
|
|
9
9
|
[](./LICENSE)
|
|
10
10
|

|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
## ✨ Overview
|
|
16
16
|
|
|
17
17
|
`@fluixi/compiler` turns Fluixi source into fine-grained reactive output. Both authoring
|
|
18
|
-
syntaxes
|
|
18
|
+
syntaxes, JSX and `` html`...` `` templates, lower to the **same host-agnostic IR**, and
|
|
19
19
|
the **imperative backend** compiles that straight into [`@fluixi/dom`](../dom) runtime
|
|
20
|
-
calls (`createNativeElement`/`spread`/`insert`/`createComponent
|
|
20
|
+
calls (`createNativeElement`/`spread`/`insert`/`createComponent`/...). No VDOM, and no
|
|
21
21
|
`jsx()` dispatch at runtime.
|
|
22
22
|
|
|
23
23
|
It owns the transform rather than running inside someone else's: parse once, splice the
|
|
@@ -25,7 +25,7 @@ emitted code over the spans it replaces. Nothing is reprinted, so a hole keeps t
|
|
|
25
25
|
author wrote and every edit carries a source map; types are left in place for the bundler
|
|
26
26
|
to strip.
|
|
27
27
|
|
|
28
|
-
`@babel/parser` is the only package it takes from that namespace, used as a parser
|
|
28
|
+
`@babel/parser` is the only package it takes from that namespace, used as a parser: the
|
|
29
29
|
same position Vue's compiler is in. `@babel/core` and the plugin machinery are optional
|
|
30
30
|
peers, needed only by `babel: true`.
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ The `@fluixi/jsx` automatic runtime is still what esbuild and TypeScript's
|
|
|
35
35
|
It also provides the **`"use server"`** transform (server functions): the client build
|
|
36
36
|
gets an RPC stub, the server build keeps and registers the body.
|
|
37
37
|
|
|
38
|
-
Most apps don't use this package directly
|
|
38
|
+
Most apps don't use this package directly: [`@fluixi/vite-plugin`](../vite-plugin) (and
|
|
39
39
|
[`@fluixi/start`](../start)) wrap it.
|
|
40
40
|
|
|
41
41
|
## What it does to your code
|
|
@@ -44,8 +44,8 @@ Beyond turning markup into DOM calls:
|
|
|
44
44
|
|
|
45
45
|
**Props you destructured stay reactive.** `function Card({ title })` compiles to
|
|
46
46
|
`props.title` at each use, so the binding keeps updating, and `...rest` becomes the
|
|
47
|
-
`splitProps` call you would have written. Anything it can't prove safe
|
|
48
|
-
binding, a computed key, a default it would have to re-run per read
|
|
47
|
+
`splitProps` call you would have written. Anything it can't prove safe: a reassigned
|
|
48
|
+
binding, a computed key, a default it would have to re-run per read: is left exactly as
|
|
49
49
|
written and reported. On by default; set `propsDestructure: false` for a package that ships
|
|
50
50
|
its **source** for consumers to compile, since the rewrite belongs to whoever compiles.
|
|
51
51
|
|
|
@@ -57,7 +57,7 @@ recognised, a local `function signal()` is not. A module that binds a `$` name k
|
|
|
57
57
|
own meaning and gets a diagnostic.
|
|
58
58
|
|
|
59
59
|
**Unimported tags resolve through rules.** `resolve` says where a capitalised tag comes
|
|
60
|
-
from
|
|
60
|
+
from: an entry per component, or one pattern for a family:
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
63
|
createVitePlugin({ resolve: [{ match: /^([A-Z]\w+)$/, module: '/src/components/$1.tsx' }] })
|
|
@@ -65,7 +65,7 @@ createVitePlugin({ resolve: [{ match: /^([A-Z]\w+)$/, module: '/src/components/$
|
|
|
65
65
|
|
|
66
66
|
**`load:` splits a component into its own chunk.** `load:visible`, `load:idle`,
|
|
67
67
|
`load:interaction`, `load:media` decide when its module is fetched. It works on a component
|
|
68
|
-
a rule supplies and on one you imported yourself
|
|
68
|
+
a rule supplies and on one you imported yourself: in the second case the static import is
|
|
69
69
|
removed, which only happens when the name is used nowhere but as a tag.
|
|
70
70
|
|
|
71
71
|
**It says when it couldn't do these things.** A tag nothing supplies and a `load:` that
|
|
@@ -104,15 +104,15 @@ import { transformTemplates } from '@fluixi/compiler/transform';
|
|
|
104
104
|
const { code, map } = transformTemplates(source, 'App.tsx', {})!;
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
`transformTemplates` returns `null` when there is nothing to do
|
|
108
|
-
`$` intrinsics and nothing to report
|
|
107
|
+
`transformTemplates` returns `null` when there is nothing to do: no JSX, no templates, no
|
|
108
|
+
`$` intrinsics and nothing to report: so a bundler can leave the file untouched.
|
|
109
109
|
|
|
110
110
|
### Compiling through Babel
|
|
111
111
|
|
|
112
112
|
`fluixi({ babel: true })` routes through the Babel front-end instead. It exists for a
|
|
113
113
|
project whose own Babel plugins have to see the compiled output; it needs `@babel/core`
|
|
114
|
-
and friends installed, and says so rather than degrading. Everything else
|
|
115
|
-
passes, the backend
|
|
114
|
+
and friends installed, and says so rather than degrading. Everything else: the IR, the
|
|
115
|
+
passes, the backend: is the same code either way.
|
|
116
116
|
|
|
117
117
|
## 🔌 Subpaths
|
|
118
118
|
|
|
@@ -26,7 +26,7 @@ interface Node {
|
|
|
26
26
|
export type DestructureBail =
|
|
27
27
|
/**
|
|
28
28
|
* A rest we can't serve: destructured further (`...{ a }`), or in a function with no
|
|
29
|
-
* block to put the splitProps call in (`({ a, ...rest }) =>
|
|
29
|
+
* block to put the splitProps call in (`({ a, ...rest }) => ...`).
|
|
30
30
|
*/
|
|
31
31
|
'rest'
|
|
32
32
|
/** `const { [key]: a } = props`: we don't know the property until it runs. */
|
|
@@ -26,7 +26,7 @@ interface Node {
|
|
|
26
26
|
export type DestructureBail =
|
|
27
27
|
/**
|
|
28
28
|
* A rest we can't serve: destructured further (`...{ a }`), or in a function with no
|
|
29
|
-
* block to put the splitProps call in (`({ a, ...rest }) =>
|
|
29
|
+
* block to put the splitProps call in (`({ a, ...rest }) => ...`).
|
|
30
30
|
*/
|
|
31
31
|
'rest'
|
|
32
32
|
/** `const { [key]: a } = props`: we don't know the property until it runs. */
|
|
@@ -94,7 +94,7 @@ function walk(root, visit) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
/** Anything written to in the body: `name = x`, `name++`, `[name] =
|
|
97
|
+
/** Anything written to in the body: `name = x`, `name++`, `[name] = ...`. */
|
|
98
98
|
function reassignedNames(body) {
|
|
99
99
|
const written = new Set();
|
|
100
100
|
walk(body, (node) => {
|
|
@@ -29,7 +29,7 @@ export interface ReactiveBindings {
|
|
|
29
29
|
*
|
|
30
30
|
* Deliberately scope-blind: it answers "does this file declare this name anywhere", which
|
|
31
31
|
* is the question a diagnostic needs. A component declared inside a function
|
|
32
|
-
* (`const Row = () =>
|
|
32
|
+
* (`const Row = () => ...`) is bound just as much as an import, and warning about it would
|
|
33
33
|
* be noise. Over-permissive on purpose: missing a warning beats inventing one.
|
|
34
34
|
*/
|
|
35
35
|
export declare function declaredNames(program: unknown): Set<string>;
|
|
@@ -29,7 +29,7 @@ export interface ReactiveBindings {
|
|
|
29
29
|
*
|
|
30
30
|
* Deliberately scope-blind: it answers "does this file declare this name anywhere", which
|
|
31
31
|
* is the question a diagnostic needs. A component declared inside a function
|
|
32
|
-
* (`const Row = () =>
|
|
32
|
+
* (`const Row = () => ...`) is bound just as much as an import, and warning about it would
|
|
33
33
|
* be noise. Over-permissive on purpose: missing a warning beats inventing one.
|
|
34
34
|
*/
|
|
35
35
|
export declare function declaredNames(program: unknown): Set<string>;
|
|
@@ -71,7 +71,7 @@ function calledPrimitive(node, bindings) {
|
|
|
71
71
|
const imported = bindings.primitives.get(name);
|
|
72
72
|
if (imported)
|
|
73
73
|
return imported;
|
|
74
|
-
// `$signal(
|
|
74
|
+
// `$signal(...)`, unless the module bound that name itself.
|
|
75
75
|
const intrinsic = INTRINSICS[name];
|
|
76
76
|
if (intrinsic && !bindings.shadowed.has(name))
|
|
77
77
|
return intrinsic.returns;
|
|
@@ -98,7 +98,7 @@ function classify(id, returns, kinds) {
|
|
|
98
98
|
*
|
|
99
99
|
* Deliberately scope-blind: it answers "does this file declare this name anywhere", which
|
|
100
100
|
* is the question a diagnostic needs. A component declared inside a function
|
|
101
|
-
* (`const Row = () =>
|
|
101
|
+
* (`const Row = () => ...`) is bound just as much as an import, and warning about it would
|
|
102
102
|
* be noise. Over-permissive on purpose: missing a warning beats inventing one.
|
|
103
103
|
*/
|
|
104
104
|
export function declaredNames(program) {
|
|
@@ -141,7 +141,7 @@ export function resolveReactiveBindings(program) {
|
|
|
141
141
|
const bindings = { kinds: new Map(), primitives: new Map(), shadowed: new Set() };
|
|
142
142
|
const root = program;
|
|
143
143
|
// Anything the module declares under a `$` name shadows the intrinsic. Checked across
|
|
144
|
-
// the whole module, not just the top level: a nested `const $signal =
|
|
144
|
+
// the whole module, not just the top level: a nested `const $signal = ...` still means
|
|
145
145
|
// the call at that point isn't ours, and refusing the rewrite there is the safe answer.
|
|
146
146
|
walk(root, (node) => {
|
|
147
147
|
const names = new Set();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -143,7 +143,7 @@ function mentions(node) {
|
|
|
143
143
|
/**
|
|
144
144
|
* A template literal as the parser wants it: static runs and hole slots, in order.
|
|
145
145
|
*
|
|
146
|
-
* The same shape the Babel front-end builds, so what the analyzer sees inside `` html
|
|
146
|
+
* The same shape the Babel front-end builds, so what the analyzer sees inside `` html`...` ``
|
|
147
147
|
* is what the compiler lowers.
|
|
148
148
|
*/
|
|
149
149
|
function templatePieces(quasi) {
|
|
@@ -527,7 +527,7 @@ export function analyzeReactiveGraph(files, options = {}) {
|
|
|
527
527
|
domFor.set(owner, [tree]);
|
|
528
528
|
});
|
|
529
529
|
/**
|
|
530
|
-
* The same again, for `` html
|
|
530
|
+
* The same again, for `` html`...` ``.
|
|
531
531
|
*
|
|
532
532
|
* The tags and holes are inside a string, so none of the walks above can see them: a
|
|
533
533
|
* `<Show>` in a template is not a JSXElement and a `${count()}` is not an expression
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
import {
|
|
3
3
|
analyzeStatic,
|
|
4
4
|
buildJsxIR,
|
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
lowerTemplate,
|
|
11
11
|
resolveComponentSources,
|
|
12
12
|
shapeOf
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-G2PQDYFN.mjs";
|
|
14
14
|
import {
|
|
15
15
|
CORE_CONTROL_FLOW,
|
|
16
16
|
ROUTER_COMPONENTS,
|
|
17
17
|
createComponentResolver
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-K4Q6GHV6.mjs";
|
|
19
|
+
import "./chunk-46MIOLUB.mjs";
|
|
20
20
|
|
|
21
21
|
// src/frontend/babel/plugin.ts
|
|
22
22
|
import { declare } from "@babel/helper-plugin-utils";
|
|
@@ -49,7 +49,7 @@ function bindHole(expr, used, onNested) {
|
|
|
49
49
|
if (t.isObjectExpression(expr)) hole.object = true;
|
|
50
50
|
return hole;
|
|
51
51
|
}
|
|
52
|
-
function
|
|
52
|
+
function lowerHtmlTemplate(node, used, opts = {}) {
|
|
53
53
|
const quasi = node.quasi;
|
|
54
54
|
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
55
55
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
@@ -67,9 +67,9 @@ function buildPieces(quasi) {
|
|
|
67
67
|
return pieces;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
// src/frontend/babel/build-ir-
|
|
71
|
-
function
|
|
72
|
-
const ir =
|
|
70
|
+
// src/frontend/babel/build-ir-html.ts
|
|
71
|
+
function buildIRFromHtml(node, used = /* @__PURE__ */ new Set(), opts = {}) {
|
|
72
|
+
const ir = lowerHtmlTemplate(node, used, opts);
|
|
73
73
|
analyzeStatic(ir);
|
|
74
74
|
return ir;
|
|
75
75
|
}
|
|
@@ -97,12 +97,12 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
97
97
|
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
|
|
98
98
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
99
99
|
},
|
|
100
|
-
// Nested
|
|
100
|
+
// Nested html templates compose: ${cond ? html`...` : html`...`},
|
|
101
101
|
// ${list.map(x => html`<li>${x}</li>`)}. Compile them the same way.
|
|
102
102
|
TaggedTemplateExpression(path) {
|
|
103
103
|
const tag = path.node.tag;
|
|
104
104
|
if (!t2.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
|
|
105
|
-
const ir =
|
|
105
|
+
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
|
|
106
106
|
onNested?.(ir);
|
|
107
107
|
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
108
108
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
@@ -145,11 +145,11 @@ var plugin_default = declare((api, options = {}) => {
|
|
|
145
145
|
// `fluixi()` needs no `format` option. Injected imports are deduped, so a
|
|
146
146
|
// file mixing the two styles won't redeclare a binding.
|
|
147
147
|
format = "both",
|
|
148
|
-
|
|
148
|
+
htmlTag = "html",
|
|
149
149
|
resolve: resolveRules = []
|
|
150
150
|
} = options;
|
|
151
151
|
const componentResolver = createComponentResolver(resolveRules);
|
|
152
|
-
const
|
|
152
|
+
const compileHtml = format === "html" || format === "both";
|
|
153
153
|
const pluginState = {
|
|
154
154
|
irImports: /* @__PURE__ */ new Set(),
|
|
155
155
|
libModule: "@fluixi/core",
|
|
@@ -158,7 +158,7 @@ var plugin_default = declare((api, options = {}) => {
|
|
|
158
158
|
templateClone,
|
|
159
159
|
partialTemplates,
|
|
160
160
|
hasJSX: false,
|
|
161
|
-
|
|
161
|
+
hasHtmlTag: false,
|
|
162
162
|
needsControlFlowImport: false,
|
|
163
163
|
needsCreateMemo: false,
|
|
164
164
|
controlFlowComponents: /* @__PURE__ */ new Set(),
|
|
@@ -182,8 +182,8 @@ var plugin_default = declare((api, options = {}) => {
|
|
|
182
182
|
enter(path, state) {
|
|
183
183
|
Object.assign(state, {
|
|
184
184
|
hasJSX: false,
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
hasHtmlTag: false,
|
|
186
|
+
hasHtmlIR: false,
|
|
187
187
|
needsControlFlowImport: false,
|
|
188
188
|
needsCreateMemo: false,
|
|
189
189
|
controlFlowComponents: /* @__PURE__ */ new Set(),
|
|
@@ -208,7 +208,7 @@ var plugin_default = declare((api, options = {}) => {
|
|
|
208
208
|
},
|
|
209
209
|
exit(path, state) {
|
|
210
210
|
const imports = [];
|
|
211
|
-
if (state.hasJSX || state.
|
|
211
|
+
if (state.hasJSX || state.hasHtmlIR) {
|
|
212
212
|
if (state.irImports.size > 0) {
|
|
213
213
|
const CF = /* @__PURE__ */ new Set([
|
|
214
214
|
"Show",
|
|
@@ -398,8 +398,8 @@ var plugin_default = declare((api, options = {}) => {
|
|
|
398
398
|
path.replaceWith(emitViaIR(path.node, state));
|
|
399
399
|
},
|
|
400
400
|
TaggedTemplateExpression(path, state) {
|
|
401
|
-
if (
|
|
402
|
-
path.replaceWith(
|
|
401
|
+
if (compileHtml && t3.isIdentifier(path.node.tag) && (path.node.tag.name === htmlTag || path.node.tag.name === "svg")) {
|
|
402
|
+
path.replaceWith(emitHtmlViaIR(path.node, state));
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
}
|
|
@@ -502,10 +502,10 @@ function dedupeImports(imports) {
|
|
|
502
502
|
}
|
|
503
503
|
return out;
|
|
504
504
|
}
|
|
505
|
-
function
|
|
505
|
+
function emitHtmlViaIR(node, state) {
|
|
506
506
|
const used = /* @__PURE__ */ new Set();
|
|
507
507
|
const svg = t3.isIdentifier(node.tag) && node.tag.name === "svg";
|
|
508
|
-
const ir =
|
|
508
|
+
const ir = buildIRFromHtml(node, used, { svg, onNested: nestedHook(state) });
|
|
509
509
|
resolveComponentSources(ir, {
|
|
510
510
|
resolver: state.componentResolver,
|
|
511
511
|
modules: {
|
|
@@ -520,7 +520,7 @@ function emitLitViaIR(node, state) {
|
|
|
520
520
|
for (const s of imports) used.add(s);
|
|
521
521
|
for (const s of used) state.irImports.add(s);
|
|
522
522
|
collectRuntimeNeeds(ir, state);
|
|
523
|
-
state.
|
|
523
|
+
state.hasHtmlIR = true;
|
|
524
524
|
const expression = template2.expression(adoptTemplates(code, templates, state), {
|
|
525
525
|
placeholderPattern: false,
|
|
526
526
|
plugins: ["typescript"]
|
|
@@ -596,7 +596,7 @@ function createPreset(options = {}) {
|
|
|
596
596
|
importSource: "@fluixi/jsx",
|
|
597
597
|
development: false,
|
|
598
598
|
detectReactivity: true,
|
|
599
|
-
|
|
599
|
+
useHtmlTag: true,
|
|
600
600
|
hoistStatics: true,
|
|
601
601
|
delegateEvents: true,
|
|
602
602
|
optimizeControlFlow: true,
|
|
@@ -621,7 +621,7 @@ var presets = {
|
|
|
621
621
|
}),
|
|
622
622
|
minimal: createPreset({
|
|
623
623
|
detectReactivity: false,
|
|
624
|
-
|
|
624
|
+
useHtmlTag: false,
|
|
625
625
|
hoistStatics: false,
|
|
626
626
|
delegateEvents: false,
|
|
627
627
|
optimizeControlFlow: false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
3
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
4
|
}) : x)(function(x) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.6.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
|
|
4
4
|
var comma = ",".charCodeAt(0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
import {
|
|
3
3
|
encode
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B6GUZXJP.mjs";
|
|
5
5
|
|
|
6
6
|
// ../../node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs
|
|
7
7
|
var BitSet = class _BitSet {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
import {
|
|
3
3
|
INTRINSICS,
|
|
4
4
|
RESERVED_COMPONENTS,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
builtinComponentMap,
|
|
8
8
|
isIntrinsicName,
|
|
9
9
|
isPrimitiveModule
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-K4Q6GHV6.mjs";
|
|
11
11
|
|
|
12
12
|
// src/analyze/expr-shape.ts
|
|
13
13
|
var CALL = /* @__PURE__ */ new Set(["CallExpression", "OptionalCallExpression"]);
|
|
@@ -1756,7 +1756,7 @@ var Lowerer = class {
|
|
|
1756
1756
|
for (const s of imports) this.used.add(s);
|
|
1757
1757
|
return inlineTemplates(code, templates);
|
|
1758
1758
|
}
|
|
1759
|
-
//
|
|
1759
|
+
// content
|
|
1760
1760
|
lowerRoot(children) {
|
|
1761
1761
|
const nodes = this.lowerChildren(children);
|
|
1762
1762
|
if (nodes.length === 1) return nodes[0];
|
|
@@ -1885,8 +1885,8 @@ var Lowerer = class {
|
|
|
1885
1885
|
}
|
|
1886
1886
|
return { slots, rest };
|
|
1887
1887
|
}
|
|
1888
|
-
//
|
|
1889
|
-
/** `<el if=${cond}
|
|
1888
|
+
// control-flow sugar
|
|
1889
|
+
/** `<el if=${cond}>...</el>` (+ optional `<el else>`) → `<Show when fallback>`. */
|
|
1890
1890
|
lowerIf(el, condHole, elseEl) {
|
|
1891
1891
|
const cond = this.hole(condHole);
|
|
1892
1892
|
const props = [
|
|
@@ -1901,8 +1901,8 @@ var Lowerer = class {
|
|
|
1901
1901
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1902
1902
|
}
|
|
1903
1903
|
/**
|
|
1904
|
-
* `<el each=${items} key?>${item =>
|
|
1905
|
-
* content is repeated per item. Content that is not a `${item =>
|
|
1904
|
+
* `<el each=${items} key?>${item => ...}</el>` → `<el><For each>{item => ...}</For></el>`: the
|
|
1905
|
+
* content is repeated per item. Content that is not a `${item => ...}` arrow is repeated as
|
|
1906
1906
|
* it is. Null when the element has no `each`.
|
|
1907
1907
|
*/
|
|
1908
1908
|
lowerEach(el) {
|
|
@@ -1939,7 +1939,7 @@ var Lowerer = class {
|
|
|
1939
1939
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1940
1940
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1941
1941
|
}
|
|
1942
|
-
//
|
|
1942
|
+
// attributes
|
|
1943
1943
|
/**
|
|
1944
1944
|
* `element`: a DOM element, where each class:/style: value that moves is its own accessor.
|
|
1945
1945
|
* A component gets the object as before, rebuilt whole when any value moves.
|
|
@@ -2080,7 +2080,7 @@ var Lowerer = class {
|
|
|
2080
2080
|
if (mods.includes("passive")) opts.push("passive: true");
|
|
2081
2081
|
return opts.length ? `{ ${opts.join(", ")} }` : null;
|
|
2082
2082
|
}
|
|
2083
|
-
/** `bind:value=${sig}` → `value={read()}` + `onInput={e => write(e
|
|
2083
|
+
/** `bind:value=${sig}` → `value={read()}` + `onInput={e => write(e...value)}`. */
|
|
2084
2084
|
bindProps(prop, h) {
|
|
2085
2085
|
const sigCode = h.code;
|
|
2086
2086
|
const checked = prop === "checked";
|
|
@@ -2106,7 +2106,7 @@ var Lowerer = class {
|
|
|
2106
2106
|
}
|
|
2107
2107
|
];
|
|
2108
2108
|
}
|
|
2109
|
-
/** A plain attribute (incl.
|
|
2109
|
+
/** A plain attribute (incl. html `?bool`, mixed values), old `mkProp` behavior. */
|
|
2110
2110
|
plainAttr(a) {
|
|
2111
2111
|
const v = a.value;
|
|
2112
2112
|
let name = a.name;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/resolve/component-resolver.ts
|
|
4
4
|
var isMapRule = (rule) => "components" in rule;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
import {
|
|
3
3
|
encode
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B6GUZXJP.mjs";
|
|
5
5
|
|
|
6
6
|
// src/transform/styles.ts
|
|
7
7
|
var SCOPE_ATTR = "data-fx-s";
|
|
@@ -55,7 +55,7 @@ function propValue(p) {
|
|
|
55
55
|
* The whole `createComponent(...)` call sits inside a `createMemo`, so reading a
|
|
56
56
|
* signal in a prop re-evaluates when the memo re-runs, no per-prop thunk needed.
|
|
57
57
|
* This matches the inline codegen; wrapping a prop like
|
|
58
|
-
* `history={createMemoryHistory()}` in `() =>
|
|
58
|
+
* `history={createMemoryHistory()}` in `() => ...` would hand the component a
|
|
59
59
|
* function instead of the value (e.g. `history.get is not a function`).
|
|
60
60
|
*/
|
|
61
61
|
function componentPropValue(p) {
|
|
@@ -152,7 +152,7 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
152
152
|
// The memo never recomputes: every prop is a getter, so this callback reads nothing
|
|
153
153
|
// reactive. It is here for what it makes `insert` do, and it cannot be elided.
|
|
154
154
|
//
|
|
155
|
-
// A component whose return value is *itself* bare control flow (`return <Show
|
|
155
|
+
// A component whose return value is *itself* bare control flow (`return <Show .../>`)
|
|
156
156
|
// produces a memo, which `createComponent` deliberately does not stamp as a component.
|
|
157
157
|
// Without this wrapper `insert` stops re-reading it, and the subtree goes stale when a
|
|
158
158
|
// sibling updates the context it depends on: see `show-context-insert.spec.ts`.
|
|
@@ -27,12 +27,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
|
-
// src/frontend/babel/build-ir-
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
33
|
-
|
|
30
|
+
// src/frontend/babel/build-ir-html.ts
|
|
31
|
+
var build_ir_html_exports = {};
|
|
32
|
+
__export(build_ir_html_exports, {
|
|
33
|
+
buildIRFromHtml: () => buildIRFromHtml
|
|
34
34
|
});
|
|
35
|
-
module.exports = __toCommonJS(
|
|
35
|
+
module.exports = __toCommonJS(build_ir_html_exports);
|
|
36
36
|
|
|
37
37
|
// src/frontend/babel/lower-template.ts
|
|
38
38
|
var import_core2 = require("@babel/core");
|
|
@@ -976,7 +976,7 @@ var Lowerer = class {
|
|
|
976
976
|
for (const s of imports) this.used.add(s);
|
|
977
977
|
return inlineTemplates(code, templates);
|
|
978
978
|
}
|
|
979
|
-
//
|
|
979
|
+
// content
|
|
980
980
|
lowerRoot(children) {
|
|
981
981
|
const nodes = this.lowerChildren(children);
|
|
982
982
|
if (nodes.length === 1) return nodes[0];
|
|
@@ -1105,8 +1105,8 @@ var Lowerer = class {
|
|
|
1105
1105
|
}
|
|
1106
1106
|
return { slots, rest };
|
|
1107
1107
|
}
|
|
1108
|
-
//
|
|
1109
|
-
/** `<el if=${cond}
|
|
1108
|
+
// control-flow sugar
|
|
1109
|
+
/** `<el if=${cond}>...</el>` (+ optional `<el else>`) → `<Show when fallback>`. */
|
|
1110
1110
|
lowerIf(el, condHole, elseEl) {
|
|
1111
1111
|
const cond = this.hole(condHole);
|
|
1112
1112
|
const props = [
|
|
@@ -1121,8 +1121,8 @@ var Lowerer = class {
|
|
|
1121
1121
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
1122
1122
|
}
|
|
1123
1123
|
/**
|
|
1124
|
-
* `<el each=${items} key?>${item =>
|
|
1125
|
-
* content is repeated per item. Content that is not a `${item =>
|
|
1124
|
+
* `<el each=${items} key?>${item => ...}</el>` → `<el><For each>{item => ...}</For></el>`: the
|
|
1125
|
+
* content is repeated per item. Content that is not a `${item => ...}` arrow is repeated as
|
|
1126
1126
|
* it is. Null when the element has no `each`.
|
|
1127
1127
|
*/
|
|
1128
1128
|
lowerEach(el) {
|
|
@@ -1159,7 +1159,7 @@ var Lowerer = class {
|
|
|
1159
1159
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
1160
1160
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
1161
1161
|
}
|
|
1162
|
-
//
|
|
1162
|
+
// attributes
|
|
1163
1163
|
/**
|
|
1164
1164
|
* `element`: a DOM element, where each class:/style: value that moves is its own accessor.
|
|
1165
1165
|
* A component gets the object as before, rebuilt whole when any value moves.
|
|
@@ -1300,7 +1300,7 @@ var Lowerer = class {
|
|
|
1300
1300
|
if (mods.includes("passive")) opts.push("passive: true");
|
|
1301
1301
|
return opts.length ? `{ ${opts.join(", ")} }` : null;
|
|
1302
1302
|
}
|
|
1303
|
-
/** `bind:value=${sig}` → `value={read()}` + `onInput={e => write(e
|
|
1303
|
+
/** `bind:value=${sig}` → `value={read()}` + `onInput={e => write(e...value)}`. */
|
|
1304
1304
|
bindProps(prop, h) {
|
|
1305
1305
|
const sigCode = h.code;
|
|
1306
1306
|
const checked = prop === "checked";
|
|
@@ -1326,7 +1326,7 @@ var Lowerer = class {
|
|
|
1326
1326
|
}
|
|
1327
1327
|
];
|
|
1328
1328
|
}
|
|
1329
|
-
/** A plain attribute (incl.
|
|
1329
|
+
/** A plain attribute (incl. html `?bool`, mixed values), old `mkProp` behavior. */
|
|
1330
1330
|
plainAttr(a) {
|
|
1331
1331
|
const v = a.value;
|
|
1332
1332
|
let name = a.name;
|
|
@@ -1855,12 +1855,12 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
1855
1855
|
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
|
|
1856
1856
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
1857
1857
|
},
|
|
1858
|
-
// Nested
|
|
1858
|
+
// Nested html templates compose: ${cond ? html`...` : html`...`},
|
|
1859
1859
|
// ${list.map(x => html`<li>${x}</li>`)}. Compile them the same way.
|
|
1860
1860
|
TaggedTemplateExpression(path) {
|
|
1861
1861
|
const tag = path.node.tag;
|
|
1862
1862
|
if (!import_core.types.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
|
|
1863
|
-
const ir =
|
|
1863
|
+
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
|
|
1864
1864
|
onNested?.(ir);
|
|
1865
1865
|
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
1866
1866
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
@@ -1907,7 +1907,7 @@ function bindHole(expr, used, onNested) {
|
|
|
1907
1907
|
if (import_core2.types.isObjectExpression(expr)) hole.object = true;
|
|
1908
1908
|
return hole;
|
|
1909
1909
|
}
|
|
1910
|
-
function
|
|
1910
|
+
function lowerHtmlTemplate(node, used, opts = {}) {
|
|
1911
1911
|
const quasi = node.quasi;
|
|
1912
1912
|
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
1913
1913
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
@@ -1925,9 +1925,9 @@ function buildPieces(quasi) {
|
|
|
1925
1925
|
return pieces;
|
|
1926
1926
|
}
|
|
1927
1927
|
|
|
1928
|
-
// src/frontend/babel/build-ir-
|
|
1929
|
-
function
|
|
1930
|
-
const ir =
|
|
1928
|
+
// src/frontend/babel/build-ir-html.ts
|
|
1929
|
+
function buildIRFromHtml(node, used = /* @__PURE__ */ new Set(), opts = {}) {
|
|
1930
|
+
const ir = lowerHtmlTemplate(node, used, opts);
|
|
1931
1931
|
analyzeStatic(ir);
|
|
1932
1932
|
return ir;
|
|
1933
1933
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Babel front-end entry for
|
|
2
|
+
* Babel front-end entry for `` html`...` `` templates.
|
|
3
3
|
*
|
|
4
4
|
* Historically this file held a single-pass scanner that went straight from
|
|
5
5
|
* template text to compiler IR. That fused parsing, lowering, and Babel
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
* `@fluixi/template-parser` : Babel-free tokenizer + parser → Template AST
|
|
10
10
|
* `./lower-template` : Babel-aware lowering: Template AST → IR
|
|
11
11
|
*
|
|
12
|
-
* `
|
|
13
|
-
* composition, plugin's `
|
|
12
|
+
* `buildIRFromHtml` keeps its exact signature so every caller (build-ir's nested
|
|
13
|
+
* composition, plugin's `emitHtmlViaIR`) is unchanged; the shared Template AST
|
|
14
14
|
* now also powers the language server, TS plugin, and Biome plugin.
|
|
15
15
|
*/
|
|
16
16
|
import type { types as t } from '@babel/core';
|
|
17
17
|
import type { IRNode } from '../../ir/nodes.cjs';
|
|
18
18
|
/**
|
|
19
|
-
* Build IR from
|
|
19
|
+
* Build IR from an `` html`...` `` (or `` svg`...` ``) TaggedTemplateExpression.
|
|
20
20
|
* `used` collects runtime symbols emitted for anything nested inside a hole.
|
|
21
21
|
* Pass `svg: true` for an `` svg`...` `` root.
|
|
22
22
|
*/
|
|
23
|
-
export declare function
|
|
23
|
+
export declare function buildIRFromHtml(node: t.TaggedTemplateExpression, used?: Set<string>, opts?: {
|
|
24
24
|
svg?: boolean;
|
|
25
25
|
onNested?: (ir: IRNode) => void;
|
|
26
26
|
}): IRNode;
|