sygnal 6.4.0 → 6.5.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/CHANGELOG.md +40 -0
- package/dist/astro/server.cjs.js +3 -2
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs +3 -2
- package/dist/astro/server.mjs.map +1 -1
- package/dist/diagnostics.cjs.js +58 -11
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +58 -11
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/index.cjs.js +45 -22
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +18 -4
- package/dist/index.esm.js +45 -22
- package/dist/index.esm.js.map +1 -1
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/vike/config/package.json +1 -1
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/core/cell.ts +25 -7
- package/src/core/hosts/collection.ts +6 -11
- package/src/core/hosts/switchable.ts +2 -2
- package/src/core/instance.ts +2 -2
- package/src/extra/diagnostics/checks/collections.ts +31 -25
- package/src/extra/diagnostics/checks/next.ts +22 -0
- package/src/extra/diagnostics/codes.ts +4 -0
- package/src/index.d.ts +17 -3
package/dist/diagnostics.cjs.js
CHANGED
|
@@ -272,6 +272,8 @@ const CODE_TITLES = {
|
|
|
272
272
|
// checks/next.ts and sygnal-check: a component reference that is not a function (an import of
|
|
273
273
|
// a name the module doesn't export is undefined under Vitest)
|
|
274
274
|
SYG427: 'CHILD.select() of something that is not a component',
|
|
275
|
+
// 6.5.0 (checks/next.ts): state="a.b" / from="a.b" is a path; the parent state has a literal "a.b" key
|
|
276
|
+
SYG428: 'A state path that names a key containing a dot',
|
|
275
277
|
// PLAN-9 (D470): checks/next.ts; dev entry
|
|
276
278
|
SYG453: 'Two children with the same identity share one instance',
|
|
277
279
|
SYG601: "Retired in 6.0: Missing or invalid sources",
|
|
@@ -480,6 +482,8 @@ const DEV_CODE_SEVERITY = {
|
|
|
480
482
|
SYG426: 'warn',
|
|
481
483
|
// checks/next.ts: CHILD.select(undefined), usually a missing export
|
|
482
484
|
SYG427: 'error',
|
|
485
|
+
// checks/next.ts: a path designator that 6.4 read as one key with a dot
|
|
486
|
+
SYG428: 'warn',
|
|
483
487
|
// PLAN-9 (checks/next.ts): D470 key identity, two children with one identity
|
|
484
488
|
SYG453: 'warn',
|
|
485
489
|
SYG612: 'error',
|
|
@@ -1153,25 +1157,44 @@ function checkCollection(component, el) {
|
|
|
1153
1157
|
state = component.addCalculated(state);
|
|
1154
1158
|
}
|
|
1155
1159
|
catch (_) { }
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
//
|
|
1159
|
-
// the core itself (SYG401/SYG412, retrofitted in 1E) in every diagnostics mode (D22), so
|
|
1160
|
-
// this check only covers a missing `from`.
|
|
1160
|
+
if (typeof from === 'string')
|
|
1161
|
+
return checkFrom(component, name, from, ofName, state);
|
|
1162
|
+
// a lens or an invalid value: the core's (SYG401 / SYG412)
|
|
1161
1163
|
if (from !== undefined)
|
|
1162
1164
|
return;
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1165
|
-
}
|
|
1166
|
-
if (!problem || !once(`SYG401:${name}:undefined`))
|
|
1165
|
+
if (Array.isArray(state) || (state && Array.isArray(state.value)))
|
|
1166
|
+
return;
|
|
1167
|
+
if (!once(`SYG401:${name}:undefined`))
|
|
1167
1168
|
return;
|
|
1168
1169
|
reportSafely('SYG401', {
|
|
1169
1170
|
component,
|
|
1170
|
-
message: `<Collection of={${ofName}}> ${
|
|
1171
|
-
fix,
|
|
1171
|
+
message: `<Collection of={${ofName}}> has no 'from' prop, so it uses ${name}'s whole state, which is ${describe$1(state)}, not an array, so it renders no items`,
|
|
1172
|
+
fix: `Pass the name of an array field in ${name}'s state, e.g. <Collection of={${ofName}} from="items" />, and initialize that field to [] in initialState`,
|
|
1172
1173
|
data: { from: 'undefined', of: ofName },
|
|
1173
1174
|
});
|
|
1174
1175
|
}
|
|
1176
|
+
/** `from` a key or a path ('board.cards', 'boards[0].cards'), as the core reads it (cell.ts pathOf) */
|
|
1177
|
+
function checkFrom(component, name, from, ofName, state) {
|
|
1178
|
+
const ks = from.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
1179
|
+
const calc = component._calculatedFieldNames;
|
|
1180
|
+
if (calc?.has?.(ks[0]))
|
|
1181
|
+
return;
|
|
1182
|
+
const at = ks.slice(0, -1).reduce((o, k) => (o == null ? o : o[k]), state), last = ks[ks.length - 1];
|
|
1183
|
+
if (!isPlainObject(at) && !Array.isArray(at))
|
|
1184
|
+
return; // a level still missing: renders once it appears (D178)
|
|
1185
|
+
let message;
|
|
1186
|
+
if (!(last in at)) {
|
|
1187
|
+
const arrays = Object.keys(at).filter(k => Array.isArray(at[k]));
|
|
1188
|
+
message = `Collection from="${from}" is not in state${arrays.length ? ` (array fields: '${arrays.join("', '")}')` : ''}; it renders nothing until it exists`;
|
|
1189
|
+
}
|
|
1190
|
+
else if (!Array.isArray(at[last]))
|
|
1191
|
+
message = `Collection 'from' field '${from}' is not an array; it renders nothing`;
|
|
1192
|
+
else
|
|
1193
|
+
return;
|
|
1194
|
+
if (!once(`SYG401:${name}:${from}`))
|
|
1195
|
+
return;
|
|
1196
|
+
reportSafely('SYG401', { component, message, fix: 'Set it to an array in initialState', data: { from, of: ofName } });
|
|
1197
|
+
}
|
|
1175
1198
|
const WRAPPED = '__sygnalDiagWrapped';
|
|
1176
1199
|
const collectionsCheck = {
|
|
1177
1200
|
id: 'collections',
|
|
@@ -3736,6 +3759,22 @@ function removed(component, name, anchor, detail) {
|
|
|
3736
3759
|
data: { form: anchor, guide: `${MIGRATE}#${anchor}`, ...(detail ? { detail } : {}) },
|
|
3737
3760
|
});
|
|
3738
3761
|
}
|
|
3762
|
+
/**
|
|
3763
|
+
* SYG428 (6.5.0): `state="a.b"` / `from="a.b"` is a path, read as `state.a.b`. Before 6.5.0 it was
|
|
3764
|
+
* one key named "a.b"; when the owner's state still has such a key, the designator now misses it.
|
|
3765
|
+
*/
|
|
3766
|
+
function dottedKey(f, what, designator, ownerState) {
|
|
3767
|
+
if (typeof designator != 'string' || !/[.[]/.test(designator) || !isPlainObject(ownerState))
|
|
3768
|
+
return;
|
|
3769
|
+
if (!Object.prototype.hasOwnProperty.call(ownerState, designator) || !once(`SYG428:${f.name}:${designator}`))
|
|
3770
|
+
return;
|
|
3771
|
+
devReport('SYG428', {
|
|
3772
|
+
component: f.name,
|
|
3773
|
+
message: `${what}="${designator}" is a path (since 6.5.0), read as ${designator.replace(/\[(\d+)\]/g, '.$1').split('.').map((k) => `[${JSON.stringify(k)}]`).join('')}, but the state has a key named "${designator}"`,
|
|
3774
|
+
fix: `Rename the key so it has no dot (and point ${what} at it), or use a lens: ${what}={{ get: (s) => s['${designator}'], set: (s, v) => ({ ...s, '${designator}': v }) }}`,
|
|
3775
|
+
data: { designator, what },
|
|
3776
|
+
});
|
|
3777
|
+
}
|
|
3739
3778
|
/** SYG427: CHILD.select(x) with no component (an import of a name the module doesn't export) */
|
|
3740
3779
|
function notComponent(f, x) {
|
|
3741
3780
|
if (!once(`SYG427:${f.name}`))
|
|
@@ -4102,12 +4141,18 @@ function nextHooks(_api) {
|
|
|
4102
4141
|
// G-527: one chain of SYG149 checks over the latest patches (G-501: it stops once every instance
|
|
4103
4142
|
// that renders a named Collection is disposed, the app's dispose included)
|
|
4104
4143
|
const vtCheck = vtChecker(() => vtp.size && on());
|
|
4144
|
+
// SYG428: the live instances by id, to read a child's parent state
|
|
4145
|
+
const live = new Map();
|
|
4105
4146
|
return {
|
|
4106
4147
|
onCreate(iv) {
|
|
4148
|
+
live.set(iv.id, iv);
|
|
4107
4149
|
if (!on())
|
|
4108
4150
|
return;
|
|
4109
4151
|
log.onCreate(iv);
|
|
4110
4152
|
const f = fac(iv);
|
|
4153
|
+
const parent = live.get(iv.parentId);
|
|
4154
|
+
if (parent)
|
|
4155
|
+
dottedKey(f, `<${f.name} state`, iv.props?.state, parent.state);
|
|
4111
4156
|
removedStatics(f);
|
|
4112
4157
|
if (!iv.def.intent)
|
|
4113
4158
|
wire(iv, []);
|
|
@@ -4166,6 +4211,7 @@ function nextHooks(_api) {
|
|
|
4166
4211
|
},
|
|
4167
4212
|
onDispose(iv) {
|
|
4168
4213
|
vtp.delete(iv);
|
|
4214
|
+
live.delete(iv.id);
|
|
4169
4215
|
H.onDispose(fac(iv));
|
|
4170
4216
|
},
|
|
4171
4217
|
onAction(iv, a) { log.onAction(iv, a); },
|
|
@@ -4213,6 +4259,7 @@ function nextHooks(_api) {
|
|
|
4213
4259
|
checkCollection(f, { data: { props } });
|
|
4214
4260
|
}
|
|
4215
4261
|
catch (_) { /* ignore */ }
|
|
4262
|
+
dottedKey(f, `<${sel == 'collection' ? 'Collection' : 'VirtualCollection'} from`, props.from, owner.state);
|
|
4216
4263
|
// G-419: SYG148 for a prefix that isn't an identifier; a valid one is checked for
|
|
4217
4264
|
// duplicate names after each patch (SYG149). G-460: a falsy one names nothing (as the host)
|
|
4218
4265
|
const p = props.viewTransitionName;
|