@firsthandjs/compiler 0.6.3 → 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/dist/{chunk-UYA7HB5G.js → chunk-7DU5USN7.js} +110 -1
- package/dist/index.js +1 -1
- package/dist/vite.js +1 -1
- package/package.json +1 -1
|
@@ -178,7 +178,9 @@ function annotateComponent(path, state, options) {
|
|
|
178
178
|
const name = declaredName(path);
|
|
179
179
|
rewritePropsDestructuring(path, name, state);
|
|
180
180
|
if (options.strictReactivity !== false) {
|
|
181
|
-
|
|
181
|
+
const setupPath = path.get("arguments.0");
|
|
182
|
+
checkKeptReads(setupPath, name);
|
|
183
|
+
checkDecidedOnce(setupPath, name);
|
|
182
184
|
}
|
|
183
185
|
path.node.arguments = [
|
|
184
186
|
setup,
|
|
@@ -213,6 +215,113 @@ Move the read into the part, handler, effect or computed that should re-read it
|
|
|
213
215
|
}
|
|
214
216
|
});
|
|
215
217
|
}
|
|
218
|
+
function checkDecidedOnce(setup, name) {
|
|
219
|
+
const report = (at) => {
|
|
220
|
+
throw at.buildCodeFrameError(
|
|
221
|
+
`${name}: this view is chosen once, here, from a value that changes. A setup runs one time per instance, so the other branch will never appear.
|
|
222
|
+
|
|
223
|
+
Put the choice inside the markup, where it is a part that can run again:
|
|
224
|
+
|
|
225
|
+
return <>{open.value ? <A /> : <B />}</>;`
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
const check = (returned, at) => {
|
|
229
|
+
if (t.isConditionalExpression(returned)) {
|
|
230
|
+
if (!hasView(returned.consequent) && !hasView(returned.alternate)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (readsSignal(returned.test)) {
|
|
234
|
+
report(at);
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (t.isLogicalExpression(returned) && returned.operator !== "??") {
|
|
239
|
+
if (!hasView(returned.right) && !hasView(returned.left)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (readsSignal(returned.left)) {
|
|
243
|
+
report(at);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
const body = setup.get("body");
|
|
248
|
+
if (!body.isBlockStatement()) {
|
|
249
|
+
check(setup.node.body, body);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
body.traverse({
|
|
253
|
+
Function(nested) {
|
|
254
|
+
nested.skip();
|
|
255
|
+
},
|
|
256
|
+
ReturnStatement(statement) {
|
|
257
|
+
const returned = statement.node.argument;
|
|
258
|
+
if (returned !== null && returned !== void 0) {
|
|
259
|
+
check(returned, statement);
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
/**
|
|
263
|
+
* The same mistake spelled with a keyword, and the one that actually
|
|
264
|
+
* shipped: a route guard that returned `<Navigate />` early left the page
|
|
265
|
+
* it was meant to hide on the screen after a sign-out.
|
|
266
|
+
*/
|
|
267
|
+
IfStatement(statement) {
|
|
268
|
+
if (!readsSignal(statement.node.test)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (returnsView(statement.node.consequent) || returnsView(statement.node.alternate)) {
|
|
272
|
+
report(statement.get("test"));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function returnsView(node) {
|
|
278
|
+
if (node === null || node === void 0) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
if (t.isReturnStatement(node)) {
|
|
282
|
+
return node.argument !== null && node.argument !== void 0 && hasView(node.argument);
|
|
283
|
+
}
|
|
284
|
+
if (t.isBlockStatement(node)) {
|
|
285
|
+
return node.body.some((inner) => returnsView(inner));
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
function hasView(node) {
|
|
290
|
+
if (t.isConditionalExpression(node)) {
|
|
291
|
+
return hasView(node.consequent) || hasView(node.alternate);
|
|
292
|
+
}
|
|
293
|
+
return t.isJSXElement(node) || t.isJSXFragment(node);
|
|
294
|
+
}
|
|
295
|
+
function readsSignal(node) {
|
|
296
|
+
let found = false;
|
|
297
|
+
const walk = (current) => {
|
|
298
|
+
if (current === null || current === void 0 || found) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (t.isMemberExpression(current)) {
|
|
302
|
+
if (!current.computed && t.isIdentifier(current.property, { name: "value" })) {
|
|
303
|
+
found = true;
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
walk(current.object);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (t.isUnaryExpression(current)) {
|
|
310
|
+
walk(current.argument);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (t.isBinaryExpression(current) || t.isLogicalExpression(current)) {
|
|
314
|
+
walk(current.left);
|
|
315
|
+
walk(current.right);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (t.isConditionalExpression(current)) {
|
|
319
|
+
walk(current.test);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
walk(node);
|
|
323
|
+
return found;
|
|
324
|
+
}
|
|
216
325
|
function readsOnly(node, propsName) {
|
|
217
326
|
let read = false;
|
|
218
327
|
const walk = (current) => {
|
package/dist/index.js
CHANGED
package/dist/vite.js
CHANGED
package/package.json
CHANGED