@sigil-dev/compiler 0.7.3 → 0.7.5
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/package.json +1 -1
- package/src/babel/index.ts +86 -74
- package/src/bun-plugin.ts +28 -1
package/package.json
CHANGED
package/src/babel/index.ts
CHANGED
|
@@ -27,7 +27,6 @@ const SSR_HELPERS = template.statements.ast(`
|
|
|
27
27
|
return String(v).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
28
28
|
};
|
|
29
29
|
`);
|
|
30
|
-
|
|
31
30
|
/**
|
|
32
31
|
* Recursively convert key={expr} to data-key={expr} in JSX tree.
|
|
33
32
|
* Needed for SSR output so hydration can map items by key.
|
|
@@ -64,11 +63,12 @@ interface SigilOptions {
|
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
export default function sigilPlugin(): PluginObject {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const signals = new Set<string>();
|
|
67
|
+
const storeSignals = new Set<string>();
|
|
68
|
+
let scopedHash: string | undefined;
|
|
69
|
+
let isSSR = false;
|
|
70
|
+
let isHydrate = false;
|
|
71
|
+
let didTransform = false;
|
|
72
72
|
|
|
73
73
|
return {
|
|
74
74
|
name: "sigil",
|
|
@@ -79,88 +79,71 @@ export default function sigilPlugin(): PluginObject {
|
|
|
79
79
|
storeSignals.clear();
|
|
80
80
|
const opts = (state.opts || {}) as SigilOptions;
|
|
81
81
|
scopedHash = opts.hash;
|
|
82
|
+
didTransform = false;
|
|
82
83
|
isSSR = opts.mode === "ssr";
|
|
83
84
|
isHydrate = opts.mode === "hydrate";
|
|
84
85
|
|
|
85
|
-
// SSR doesn't need any runtime imports except the `escape` utility.
|
|
86
86
|
if (isSSR) {
|
|
87
87
|
path.unshiftContainer("body", SSR_HELPERS);
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
path.unshiftContainer(
|
|
92
|
-
"body",
|
|
93
|
-
t.importDeclaration(
|
|
94
|
-
[
|
|
95
|
-
t.importSpecifier(
|
|
96
|
-
t.identifier("createSignal"),
|
|
97
|
-
t.identifier("createSignal"),
|
|
98
|
-
),
|
|
99
|
-
t.importSpecifier(
|
|
100
|
-
t.identifier("createEffect"),
|
|
101
|
-
t.identifier("createEffect"),
|
|
102
|
-
),
|
|
103
|
-
t.importSpecifier(
|
|
104
|
-
t.identifier("createMemo"),
|
|
105
|
-
t.identifier("createMemo"),
|
|
106
|
-
),
|
|
107
|
-
t.importSpecifier(
|
|
108
|
-
t.identifier("reconcile"),
|
|
109
|
-
t.identifier("reconcile"),
|
|
110
|
-
),
|
|
111
|
-
t.importSpecifier(t.identifier("claim"), t.identifier("claim")),
|
|
112
|
-
t.importSpecifier(
|
|
113
|
-
t.identifier("claimText"),
|
|
114
|
-
t.identifier("claimText"),
|
|
115
|
-
),
|
|
116
|
-
t.importSpecifier(
|
|
117
|
-
t.identifier("claimComment"),
|
|
118
|
-
t.identifier("claimComment"),
|
|
119
|
-
),
|
|
120
|
-
t.importSpecifier(
|
|
121
|
-
t.identifier("hydrateKeyedList"),
|
|
122
|
-
t.identifier("hydrateKeyedList"),
|
|
123
|
-
),
|
|
124
|
-
t.importSpecifier(
|
|
125
|
-
t.identifier("insert"),
|
|
126
|
-
t.identifier("insert"),
|
|
127
|
-
),
|
|
128
91
|
|
|
129
|
-
|
|
130
|
-
t.identifier("getHydrationNodes"),
|
|
131
|
-
t.identifier("getHydrationNodes"),
|
|
132
|
-
),
|
|
133
|
-
],
|
|
134
|
-
t.stringLiteral("@sigil-dev/runtime"),
|
|
135
|
-
),
|
|
136
|
-
);
|
|
92
|
+
|
|
137
93
|
},
|
|
138
94
|
exit(path, state) {
|
|
139
|
-
warnDeadReactivity(path, signals, storeSignals, state.filename)
|
|
95
|
+
warnDeadReactivity(path, signals, storeSignals, state.filename);
|
|
96
|
+
|
|
97
|
+
// Only inject runtime import if this file actually used sigil transforms
|
|
98
|
+
if (!didTransform || isSSR) return;
|
|
99
|
+
|
|
100
|
+
const hasRuntime = path.node.body.some(
|
|
101
|
+
n => t.isImportDeclaration(n) && n.source.value === "@sigil-dev/runtime"
|
|
102
|
+
);
|
|
103
|
+
if (hasRuntime) return;
|
|
104
|
+
|
|
105
|
+
path.unshiftContainer("body", t.importDeclaration(
|
|
106
|
+
[
|
|
107
|
+
t.importSpecifier(t.identifier("createSignal"), t.identifier("createSignal")),
|
|
108
|
+
t.importSpecifier(t.identifier("createEffect"), t.identifier("createEffect")),
|
|
109
|
+
t.importSpecifier(t.identifier("createMemo"), t.identifier("createMemo")),
|
|
110
|
+
t.importSpecifier(t.identifier("reconcile"), t.identifier("reconcile")),
|
|
111
|
+
t.importSpecifier(t.identifier("claim"), t.identifier("claim")),
|
|
112
|
+
t.importSpecifier(t.identifier("claimText"), t.identifier("claimText")),
|
|
113
|
+
t.importSpecifier(t.identifier("claimComment"), t.identifier("claimComment")),
|
|
114
|
+
t.importSpecifier(t.identifier("hydrateKeyedList"), t.identifier("hydrateKeyedList")),
|
|
115
|
+
t.importSpecifier(t.identifier("insert"), t.identifier("insert")),
|
|
116
|
+
t.importSpecifier(t.identifier("getHydrationNodes"), t.identifier("getHydrationNodes")),
|
|
117
|
+
t.importSpecifier(t.identifier("pushHydrationNodes"), t.identifier("pushHydrationNodes")),
|
|
118
|
+
t.importSpecifier(t.identifier("popHydrationNodes"), t.identifier("popHydrationNodes")),
|
|
119
|
+
],
|
|
120
|
+
t.stringLiteral("@sigil-dev/runtime"),
|
|
121
|
+
));
|
|
140
122
|
}
|
|
141
123
|
},
|
|
142
124
|
VariableDeclaration(path) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
125
|
+
for (const declarator of path.get("declarations")) {
|
|
126
|
+
const macro = getMacro(declarator);
|
|
127
|
+
if (!macro) continue;
|
|
128
|
+
didTransform = true;
|
|
129
|
+
const { name, init } = macro;
|
|
130
|
+
const varName = t.isIdentifier(declarator.node.id)
|
|
131
|
+
? declarator.node.id.name
|
|
132
|
+
: null;
|
|
150
133
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
134
|
+
if (name === "$state" || name === "$store") {
|
|
135
|
+
isSSR
|
|
136
|
+
? handleStateSSR(path, declarator, init)
|
|
137
|
+
: handleState(path, declarator, init, signals);
|
|
138
|
+
if (name === "$store" && varName) {
|
|
139
|
+
storeSignals.add(varName);
|
|
140
|
+
}
|
|
141
|
+
} else if (name === "$derived") {
|
|
142
|
+
isSSR
|
|
143
|
+
? handleDerivedSSR(path, declarator, init)
|
|
144
|
+
: handleDerived(path, declarator, init, signals);
|
|
145
|
+
}
|
|
162
146
|
}
|
|
163
|
-
}
|
|
164
147
|
},
|
|
165
148
|
ExpressionStatement(path) {
|
|
166
149
|
if (isSSR) {
|
|
@@ -169,6 +152,7 @@ export default function sigilPlugin(): PluginObject {
|
|
|
169
152
|
if (expr.isCallExpression()) {
|
|
170
153
|
const callee = expr.get("callee");
|
|
171
154
|
if (callee.isIdentifier() && callee.node.name === "$effect") {
|
|
155
|
+
didTransform = true;
|
|
172
156
|
path.remove();
|
|
173
157
|
}
|
|
174
158
|
}
|
|
@@ -177,9 +161,29 @@ export default function sigilPlugin(): PluginObject {
|
|
|
177
161
|
handleEffect(path);
|
|
178
162
|
},
|
|
179
163
|
JSXElement(path) {
|
|
164
|
+
didTransform = true;
|
|
165
|
+
// console.log(
|
|
166
|
+
// "[sigil] JSXElement visitor fired, parent:",
|
|
167
|
+
// path.parentPath?.type,
|
|
168
|
+
// "isSSR:", isSSR
|
|
169
|
+
// );
|
|
180
170
|
if (path.parentPath?.isJSXElement() || path.parentPath?.isJSXFragment())
|
|
181
171
|
return;
|
|
182
|
-
if (isSSR) {
|
|
172
|
+
if (isSSR) {
|
|
173
|
+
try {
|
|
174
|
+
convertKeyToDataKey(path.node);
|
|
175
|
+
path.replaceWith(
|
|
176
|
+
t.callExpression(t.identifier("__h"), [
|
|
177
|
+
processElementSSR(path.node, signals),
|
|
178
|
+
]),
|
|
179
|
+
);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.error("[sigil] processElementSSR threw:", path.toString());
|
|
182
|
+
console.error(e);
|
|
183
|
+
throw e;
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
183
187
|
|
|
184
188
|
const statements: t.Statement[] = [];
|
|
185
189
|
const genId = (() => { let i = 0; return () => `_el${i++}`; })();
|
|
@@ -215,9 +219,17 @@ export default function sigilPlugin(): PluginObject {
|
|
|
215
219
|
);
|
|
216
220
|
},
|
|
217
221
|
JSXFragment(path) {
|
|
222
|
+
didTransform = true;
|
|
218
223
|
if (path.parentPath?.isJSXElement() || path.parentPath?.isJSXFragment())
|
|
219
224
|
return;
|
|
220
|
-
if (isSSR) {
|
|
225
|
+
if (isSSR) {
|
|
226
|
+
path.replaceWith(
|
|
227
|
+
t.callExpression(t.identifier("__h"), [
|
|
228
|
+
processFragmentSSR(path.node, signals),
|
|
229
|
+
]),
|
|
230
|
+
);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
221
233
|
|
|
222
234
|
const statements: t.Statement[] = [];
|
|
223
235
|
const genId = (() => { let i = 0; return () => `_el${i++}`; })();
|
package/src/bun-plugin.ts
CHANGED
|
@@ -4,6 +4,30 @@ import sigilPlugin from "./babel/index.ts";
|
|
|
4
4
|
import { computeHash, scopeCSS } from "./babel/util/css.ts";
|
|
5
5
|
|
|
6
6
|
const STYLE_RE = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
7
|
+
const STYLE_TEST_RE = /<style[^>]*>[\s\S]*?<\/style>/i;
|
|
8
|
+
const SOURCE_RE = /\.[jt]sx?$/;
|
|
9
|
+
const JSX_RE = /\.[jt]sx$/;
|
|
10
|
+
const SIGIL_MACRO_RE = /\$(?:state|store|derived|effect)\s*\(/;
|
|
11
|
+
|
|
12
|
+
function normalizePath(path: string): string {
|
|
13
|
+
return path.replace(/\\/g, "/");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function shouldTransform(path: string): boolean {
|
|
17
|
+
const normalized = normalizePath(path);
|
|
18
|
+
if (!SOURCE_RE.test(normalized)) return false;
|
|
19
|
+
if (normalized.includes("/.grimoire/")) return false;
|
|
20
|
+
if (normalized.includes("/node_modules/")) return false;
|
|
21
|
+
if (normalized.includes("/packages/runtime/")) return false;
|
|
22
|
+
if (normalized.includes("/packages/grimoire/")) return false;
|
|
23
|
+
if (normalized.includes("/packages/compiler/")) return false;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function shouldTransformSource(path: string, code: string): boolean {
|
|
28
|
+
const normalized = normalizePath(path);
|
|
29
|
+
return JSX_RE.test(normalized) || SIGIL_MACRO_RE.test(code) || STYLE_TEST_RE.test(code);
|
|
30
|
+
}
|
|
7
31
|
|
|
8
32
|
export function sigil(options?: {
|
|
9
33
|
mode?: "dom" | "ssr" | "hydrate";
|
|
@@ -12,8 +36,11 @@ export function sigil(options?: {
|
|
|
12
36
|
name: "sigil",
|
|
13
37
|
setup(build) {
|
|
14
38
|
const transpiler = new Bun.Transpiler({ loader: "tsx" });
|
|
15
|
-
build.onLoad({ filter:
|
|
39
|
+
build.onLoad({ filter: SOURCE_RE }, async ({ path }) => {
|
|
40
|
+
if (!shouldTransform(path)) return;
|
|
16
41
|
let code = await Bun.file(path).text();
|
|
42
|
+
if (!shouldTransformSource(path, code)) return;
|
|
43
|
+
// console.log("[bun-plugin] intercepting:", path);
|
|
17
44
|
const hash = computeHash(path);
|
|
18
45
|
let scopedCSS = "";
|
|
19
46
|
code = code.replace(STYLE_RE, (_, css) => {
|