@sigil-dev/compiler 0.6.11 → 0.7.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.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@sigil-dev/compiler",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.6.11",
5
+ "version": "0.7.0",
6
6
  "private": false,
7
7
  "description": "Compiler for the Sigil framework",
8
8
  "peerDependencies": {
@@ -7,6 +7,7 @@ import { handleStateSSR } from "./handlers/ssr/state.ts";
7
7
  import { processElement, processFragment } from "./jsx/index.ts";
8
8
  import { processElementSSR, processFragmentSSR } from "./jsx/ssr.ts";
9
9
  import { getMacro } from "./util/helpers.ts";
10
+ import { warnDeadReactivity } from "./util/dead-code.ts";
10
11
 
11
12
  const SSR_HELPERS = template.statements.ast(`
12
13
  const __SAFE = Symbol.for('sigil.safe');
@@ -32,208 +33,222 @@ const SSR_HELPERS = template.statements.ast(`
32
33
  * Needed for SSR output so hydration can map items by key.
33
34
  */
34
35
  function convertKeyToDataKey(node: t.JSXElement): void {
35
- for (let i = 0; i < node.openingElement.attributes.length; i++) {
36
- const attr = node.openingElement.attributes[i];
37
- if (
38
- t.isJSXAttribute(attr) &&
39
- t.isJSXIdentifier(attr.name) &&
40
- attr.name.name === "key"
41
- ) {
42
- node.openingElement.attributes[i] = t.jsxAttribute(
43
- t.jsxIdentifier("data-key"),
44
- attr.value,
45
- );
46
- }
47
- }
48
- for (const child of node.children) {
49
- if (t.isJSXElement(child)) {
50
- convertKeyToDataKey(child);
51
- } else if (
52
- t.isJSXExpressionContainer(child) &&
53
- t.isJSXElement(child.expression)
54
- ) {
55
- convertKeyToDataKey(child.expression);
56
- }
57
- }
36
+ for (let i = 0; i < node.openingElement.attributes.length; i++) {
37
+ const attr = node.openingElement.attributes[i];
38
+ if (
39
+ t.isJSXAttribute(attr) &&
40
+ t.isJSXIdentifier(attr.name) &&
41
+ attr.name.name === "key"
42
+ ) {
43
+ node.openingElement.attributes[i] = t.jsxAttribute(
44
+ t.jsxIdentifier("data-key"),
45
+ attr.value,
46
+ );
47
+ }
48
+ }
49
+ for (const child of node.children) {
50
+ if (t.isJSXElement(child)) {
51
+ convertKeyToDataKey(child);
52
+ } else if (
53
+ t.isJSXExpressionContainer(child) &&
54
+ t.isJSXElement(child.expression)
55
+ ) {
56
+ convertKeyToDataKey(child.expression);
57
+ }
58
+ }
58
59
  }
59
60
 
60
61
  interface SigilOptions {
61
- hash?: string;
62
- mode?: "dom" | "ssr" | "hydrate";
62
+ hash?: string;
63
+ mode?: "dom" | "ssr" | "hydrate";
63
64
  }
64
65
 
65
66
  export default function sigilPlugin(): PluginObject {
66
- const signals = new Set<string>();
67
- let scopedHash: string | undefined;
68
- let isSSR = false;
69
- let isHydrate = false;
67
+ const signals = new Set<string>();
68
+ const storeSignals = new Set<string>();
69
+ let scopedHash: string | undefined;
70
+ let isSSR = false;
71
+ let isHydrate = false;
70
72
 
71
- return {
72
- name: "sigil",
73
- visitor: {
74
- Program: {
75
- enter(path, state) {
76
- const opts = (state.opts || {}) as SigilOptions;
77
- scopedHash = opts.hash;
78
- isSSR = opts.mode === "ssr";
79
- isHydrate = opts.mode === "hydrate";
73
+ return {
74
+ name: "sigil",
75
+ visitor: {
76
+ Program: {
77
+ enter(path, state) {
78
+ signals.clear();
79
+ storeSignals.clear();
80
+ const opts = (state.opts || {}) as SigilOptions;
81
+ scopedHash = opts.hash;
82
+ isSSR = opts.mode === "ssr";
83
+ isHydrate = opts.mode === "hydrate";
80
84
 
81
- // SSR doesn't need any runtime imports except the `escape` utility.
82
- if (isSSR) {
83
- path.unshiftContainer("body", SSR_HELPERS);
84
- return;
85
- }
85
+ // SSR doesn't need any runtime imports except the `escape` utility.
86
+ if (isSSR) {
87
+ path.unshiftContainer("body", SSR_HELPERS);
88
+ return;
89
+ }
86
90
 
87
- path.unshiftContainer(
88
- "body",
89
- t.importDeclaration(
90
- [
91
- t.importSpecifier(
92
- t.identifier("createSignal"),
93
- t.identifier("createSignal"),
94
- ),
95
- t.importSpecifier(
96
- t.identifier("createEffect"),
97
- t.identifier("createEffect"),
98
- ),
99
- t.importSpecifier(
100
- t.identifier("createMemo"),
101
- t.identifier("createMemo"),
102
- ),
103
- t.importSpecifier(
104
- t.identifier("reconcile"),
105
- t.identifier("reconcile"),
106
- ),
107
- t.importSpecifier(t.identifier("claim"), t.identifier("claim")),
108
- t.importSpecifier(
109
- t.identifier("claimText"),
110
- t.identifier("claimText"),
111
- ),
112
- t.importSpecifier(
113
- t.identifier("claimComment"),
114
- t.identifier("claimComment"),
115
- ),
116
- t.importSpecifier(
117
- t.identifier("hydrateKeyedList"),
118
- t.identifier("hydrateKeyedList"),
119
- ),
120
- t.importSpecifier(
121
- t.identifier("insert"),
122
- t.identifier("insert"),
123
- ),
124
- ],
125
- t.stringLiteral("@sigil-dev/runtime"),
126
- ),
127
- );
128
- },
129
- },
130
- VariableDeclaration(path) {
131
- for (const declarator of path.get("declarations")) {
132
- const macro = getMacro(declarator);
133
- if (!macro) continue;
134
- const { name, init } = macro;
135
- if (name === "$state") {
136
- isSSR
137
- ? handleStateSSR(path, declarator, init)
138
- : handleState(path, declarator, init, signals);
139
- } else if (name === "$derived") {
140
- isSSR
141
- ? handleDerivedSSR(path, declarator, init)
142
- : handleDerived(path, declarator, init, signals);
143
- }
144
- }
145
- },
146
- ExpressionStatement(path) {
147
- if (isSSR) {
148
- // drop $effect entirely in SSR
149
- const expr = path.get("expression");
150
- if (expr.isCallExpression()) {
151
- const callee = expr.get("callee");
152
- if (callee.isIdentifier() && callee.node.name === "$effect") {
153
- path.remove();
154
- }
155
- }
156
- return;
157
- }
158
- handleEffect(path);
159
- },
160
- JSXElement(path) {
161
- if (path.parentPath?.isJSXElement() || path.parentPath?.isJSXFragment())
162
- return;
163
- if (isSSR) {
164
- convertKeyToDataKey(path.node);
165
- path.replaceWith(
166
- t.callExpression(t.identifier("__h"), [
167
- processElementSSR(path.node, signals),
168
- ]),
169
- );
170
- return;
171
- }
172
- const statements: t.Statement[] = [];
173
- const genId = (() => {
174
- let i = 0;
175
- return () => `_el${i++}`;
176
- })();
177
- const root = processElement(
178
- path.node,
179
- statements,
180
- genId,
181
- signals,
182
- scopedHash,
183
- isHydrate,
184
- isHydrate ? "__nodes" : undefined,
185
- );
186
- path.replaceWith(
187
- t.callExpression(
188
- t.arrowFunctionExpression(
189
- [],
190
- t.blockStatement([
191
- ...statements,
192
- t.returnStatement(t.identifier(root)),
193
- ]),
194
- ),
195
- [],
196
- ),
197
- );
198
- },
199
- JSXFragment(path) {
200
- if (path.parentPath?.isJSXElement() || path.parentPath?.isJSXFragment())
201
- return;
202
- if (isSSR) {
203
- path.replaceWith(
204
- t.callExpression(t.identifier("__h"), [
205
- processFragmentSSR(path.node, signals),
206
- ]),
207
- );
208
- return;
209
- }
210
- const statements: t.Statement[] = [];
211
- const genId = (() => {
212
- let i = 0;
213
- return () => `_el${i++}`;
214
- })();
215
- const root = processFragment(
216
- path.node,
217
- statements,
218
- genId,
219
- signals,
220
- scopedHash,
221
- isHydrate,
222
- isHydrate ? "__nodes" : undefined,
223
- );
224
- path.replaceWith(
225
- t.callExpression(
226
- t.arrowFunctionExpression(
227
- [],
228
- t.blockStatement([
229
- ...statements,
230
- t.returnStatement(t.identifier(root)),
231
- ]),
232
- ),
233
- [],
234
- ),
235
- );
236
- },
237
- },
238
- };
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
+ ],
129
+ t.stringLiteral("@sigil-dev/runtime"),
130
+ ),
131
+ );
132
+ },
133
+ exit(path, state) {
134
+ console.log("signals at exit:", [...signals]);
135
+ warnDeadReactivity(path, signals, storeSignals, state.filename)
136
+ }
137
+ },
138
+ VariableDeclaration(path) {
139
+ for (const declarator of path.get("declarations")) {
140
+ const macro = getMacro(declarator);
141
+ if (!macro) continue;
142
+ const { name, init } = macro;
143
+ const varName = t.isIdentifier(declarator.node.id)
144
+ ? declarator.node.id.name
145
+ : null;
146
+
147
+ if (name === "$state" || name === "$store") {
148
+ isSSR
149
+ ? handleStateSSR(path, declarator, init)
150
+ : handleState(path, declarator, init, signals);
151
+ if (name === "$store" && varName) {
152
+ storeSignals.add(varName);
153
+ }
154
+ } else if (name === "$derived") {
155
+ isSSR
156
+ ? handleDerivedSSR(path, declarator, init)
157
+ : handleDerived(path, declarator, init, signals);
158
+ }
159
+ }
160
+ },
161
+ ExpressionStatement(path) {
162
+ if (isSSR) {
163
+ // drop $effect entirely in SSR
164
+ const expr = path.get("expression");
165
+ if (expr.isCallExpression()) {
166
+ const callee = expr.get("callee");
167
+ if (callee.isIdentifier() && callee.node.name === "$effect") {
168
+ path.remove();
169
+ }
170
+ }
171
+ return;
172
+ }
173
+ handleEffect(path);
174
+ },
175
+ JSXElement(path) {
176
+ if (path.parentPath?.isJSXElement() || path.parentPath?.isJSXFragment())
177
+ return;
178
+ if (isSSR) {
179
+ convertKeyToDataKey(path.node);
180
+ path.replaceWith(
181
+ t.callExpression(t.identifier("__h"), [
182
+ processElementSSR(path.node, signals),
183
+ ]),
184
+ );
185
+ return;
186
+ }
187
+ const statements: t.Statement[] = [];
188
+ const genId = (() => {
189
+ let i = 0;
190
+ return () => `_el${i++}`;
191
+ })();
192
+ const root = processElement(
193
+ path.node,
194
+ statements,
195
+ genId,
196
+ signals,
197
+ scopedHash,
198
+ isHydrate,
199
+ isHydrate ? "__nodes" : undefined,
200
+ );
201
+ path.replaceWith(
202
+ t.callExpression(
203
+ t.arrowFunctionExpression(
204
+ [],
205
+ t.blockStatement([
206
+ ...statements,
207
+ t.returnStatement(t.identifier(root)),
208
+ ]),
209
+ ),
210
+ [],
211
+ ),
212
+ );
213
+ },
214
+ JSXFragment(path) {
215
+ if (path.parentPath?.isJSXElement() || path.parentPath?.isJSXFragment())
216
+ return;
217
+ if (isSSR) {
218
+ path.replaceWith(
219
+ t.callExpression(t.identifier("__h"), [
220
+ processFragmentSSR(path.node, signals),
221
+ ]),
222
+ );
223
+ return;
224
+ }
225
+ const statements: t.Statement[] = [];
226
+ const genId = (() => {
227
+ let i = 0;
228
+ return () => `_el${i++}`;
229
+ })();
230
+ const root = processFragment(
231
+ path.node,
232
+ statements,
233
+ genId,
234
+ signals,
235
+ scopedHash,
236
+ isHydrate,
237
+ isHydrate ? "__nodes" : undefined,
238
+ );
239
+ path.replaceWith(
240
+ t.callExpression(
241
+ t.arrowFunctionExpression(
242
+ [],
243
+ t.blockStatement([
244
+ ...statements,
245
+ t.returnStatement(t.identifier(root)),
246
+ ]),
247
+ ),
248
+ [],
249
+ ),
250
+ );
251
+ },
252
+ },
253
+ };
239
254
  }