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.esm.js
CHANGED
|
@@ -252,6 +252,8 @@ const CODE_TITLES = {
|
|
|
252
252
|
// checks/next.ts and sygnal-check: a component reference that is not a function (an import of
|
|
253
253
|
// a name the module doesn't export is undefined under Vitest)
|
|
254
254
|
SYG427: 'CHILD.select() of something that is not a component',
|
|
255
|
+
// 6.5.0 (checks/next.ts): state="a.b" / from="a.b" is a path; the parent state has a literal "a.b" key
|
|
256
|
+
SYG428: 'A state path that names a key containing a dot',
|
|
255
257
|
// PLAN-9 (D470): checks/next.ts; dev entry
|
|
256
258
|
SYG453: 'Two children with the same identity share one instance',
|
|
257
259
|
SYG601: "Retired in 6.0: Missing or invalid sources",
|
|
@@ -460,6 +462,8 @@ const DEV_CODE_SEVERITY = {
|
|
|
460
462
|
SYG426: 'warn',
|
|
461
463
|
// checks/next.ts: CHILD.select(undefined), usually a missing export
|
|
462
464
|
SYG427: 'error',
|
|
465
|
+
// checks/next.ts: a path designator that 6.4 read as one key with a dot
|
|
466
|
+
SYG428: 'warn',
|
|
463
467
|
// PLAN-9 (checks/next.ts): D470 key identity, two children with one identity
|
|
464
468
|
SYG453: 'warn',
|
|
465
469
|
SYG612: 'error',
|
|
@@ -1133,25 +1137,44 @@ function checkCollection(component, el) {
|
|
|
1133
1137
|
state = component.addCalculated(state);
|
|
1134
1138
|
}
|
|
1135
1139
|
catch (_) { }
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
//
|
|
1139
|
-
// the core itself (SYG401/SYG412, retrofitted in 1E) in every diagnostics mode (D22), so
|
|
1140
|
-
// this check only covers a missing `from`.
|
|
1140
|
+
if (typeof from === 'string')
|
|
1141
|
+
return checkFrom(component, name, from, ofName, state);
|
|
1142
|
+
// a lens or an invalid value: the core's (SYG401 / SYG412)
|
|
1141
1143
|
if (from !== undefined)
|
|
1142
1144
|
return;
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1146
|
-
if (!problem || !once(`SYG401:${name}:undefined`))
|
|
1145
|
+
if (Array.isArray(state) || (state && Array.isArray(state.value)))
|
|
1146
|
+
return;
|
|
1147
|
+
if (!once(`SYG401:${name}:undefined`))
|
|
1147
1148
|
return;
|
|
1148
1149
|
reportSafely('SYG401', {
|
|
1149
1150
|
component,
|
|
1150
|
-
message: `<Collection of={${ofName}}> ${
|
|
1151
|
-
fix,
|
|
1151
|
+
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`,
|
|
1152
|
+
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`,
|
|
1152
1153
|
data: { from: 'undefined', of: ofName },
|
|
1153
1154
|
});
|
|
1154
1155
|
}
|
|
1156
|
+
/** `from` a key or a path ('board.cards', 'boards[0].cards'), as the core reads it (cell.ts pathOf) */
|
|
1157
|
+
function checkFrom(component, name, from, ofName, state) {
|
|
1158
|
+
const ks = from.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
1159
|
+
const calc = component._calculatedFieldNames;
|
|
1160
|
+
if (calc?.has?.(ks[0]))
|
|
1161
|
+
return;
|
|
1162
|
+
const at = ks.slice(0, -1).reduce((o, k) => (o == null ? o : o[k]), state), last = ks[ks.length - 1];
|
|
1163
|
+
if (!isPlainObject(at) && !Array.isArray(at))
|
|
1164
|
+
return; // a level still missing: renders once it appears (D178)
|
|
1165
|
+
let message;
|
|
1166
|
+
if (!(last in at)) {
|
|
1167
|
+
const arrays = Object.keys(at).filter(k => Array.isArray(at[k]));
|
|
1168
|
+
message = `Collection from="${from}" is not in state${arrays.length ? ` (array fields: '${arrays.join("', '")}')` : ''}; it renders nothing until it exists`;
|
|
1169
|
+
}
|
|
1170
|
+
else if (!Array.isArray(at[last]))
|
|
1171
|
+
message = `Collection 'from' field '${from}' is not an array; it renders nothing`;
|
|
1172
|
+
else
|
|
1173
|
+
return;
|
|
1174
|
+
if (!once(`SYG401:${name}:${from}`))
|
|
1175
|
+
return;
|
|
1176
|
+
reportSafely('SYG401', { component, message, fix: 'Set it to an array in initialState', data: { from, of: ofName } });
|
|
1177
|
+
}
|
|
1155
1178
|
const WRAPPED = '__sygnalDiagWrapped';
|
|
1156
1179
|
const collectionsCheck = {
|
|
1157
1180
|
id: 'collections',
|
|
@@ -3716,6 +3739,22 @@ function removed(component, name, anchor, detail) {
|
|
|
3716
3739
|
data: { form: anchor, guide: `${MIGRATE}#${anchor}`, ...(detail ? { detail } : {}) },
|
|
3717
3740
|
});
|
|
3718
3741
|
}
|
|
3742
|
+
/**
|
|
3743
|
+
* SYG428 (6.5.0): `state="a.b"` / `from="a.b"` is a path, read as `state.a.b`. Before 6.5.0 it was
|
|
3744
|
+
* one key named "a.b"; when the owner's state still has such a key, the designator now misses it.
|
|
3745
|
+
*/
|
|
3746
|
+
function dottedKey(f, what, designator, ownerState) {
|
|
3747
|
+
if (typeof designator != 'string' || !/[.[]/.test(designator) || !isPlainObject(ownerState))
|
|
3748
|
+
return;
|
|
3749
|
+
if (!Object.prototype.hasOwnProperty.call(ownerState, designator) || !once(`SYG428:${f.name}:${designator}`))
|
|
3750
|
+
return;
|
|
3751
|
+
devReport('SYG428', {
|
|
3752
|
+
component: f.name,
|
|
3753
|
+
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}"`,
|
|
3754
|
+
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 }) }}`,
|
|
3755
|
+
data: { designator, what },
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3719
3758
|
/** SYG427: CHILD.select(x) with no component (an import of a name the module doesn't export) */
|
|
3720
3759
|
function notComponent(f, x) {
|
|
3721
3760
|
if (!once(`SYG427:${f.name}`))
|
|
@@ -4082,12 +4121,18 @@ function nextHooks(_api) {
|
|
|
4082
4121
|
// G-527: one chain of SYG149 checks over the latest patches (G-501: it stops once every instance
|
|
4083
4122
|
// that renders a named Collection is disposed, the app's dispose included)
|
|
4084
4123
|
const vtCheck = vtChecker(() => vtp.size && on());
|
|
4124
|
+
// SYG428: the live instances by id, to read a child's parent state
|
|
4125
|
+
const live = new Map();
|
|
4085
4126
|
return {
|
|
4086
4127
|
onCreate(iv) {
|
|
4128
|
+
live.set(iv.id, iv);
|
|
4087
4129
|
if (!on())
|
|
4088
4130
|
return;
|
|
4089
4131
|
log.onCreate(iv);
|
|
4090
4132
|
const f = fac(iv);
|
|
4133
|
+
const parent = live.get(iv.parentId);
|
|
4134
|
+
if (parent)
|
|
4135
|
+
dottedKey(f, `<${f.name} state`, iv.props?.state, parent.state);
|
|
4091
4136
|
removedStatics(f);
|
|
4092
4137
|
if (!iv.def.intent)
|
|
4093
4138
|
wire(iv, []);
|
|
@@ -4146,6 +4191,7 @@ function nextHooks(_api) {
|
|
|
4146
4191
|
},
|
|
4147
4192
|
onDispose(iv) {
|
|
4148
4193
|
vtp.delete(iv);
|
|
4194
|
+
live.delete(iv.id);
|
|
4149
4195
|
H.onDispose(fac(iv));
|
|
4150
4196
|
},
|
|
4151
4197
|
onAction(iv, a) { log.onAction(iv, a); },
|
|
@@ -4193,6 +4239,7 @@ function nextHooks(_api) {
|
|
|
4193
4239
|
checkCollection(f, { data: { props } });
|
|
4194
4240
|
}
|
|
4195
4241
|
catch (_) { /* ignore */ }
|
|
4242
|
+
dottedKey(f, `<${sel == 'collection' ? 'Collection' : 'VirtualCollection'} from`, props.from, owner.state);
|
|
4196
4243
|
// G-419: SYG148 for a prefix that isn't an identifier; a valid one is checked for
|
|
4197
4244
|
// duplicate names after each patch (SYG149). G-460: a falsy one names nothing (as the host)
|
|
4198
4245
|
const p = props.viewTransitionName;
|