sygnal 6.4.0 → 6.6.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 +64 -0
- package/dist/astro/index.cjs.js +398 -25
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +398 -25
- package/dist/astro/index.mjs.map +1 -1
- 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/devtools.cjs.js +141 -3
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.esm.js +141 -4
- package/dist/devtools.esm.js.map +1 -1
- package/dist/diagnostics.cjs.js +140 -21
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +140 -21
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/index.cjs.js +54 -24
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +139 -16
- package/dist/index.esm.js +54 -24
- 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/dist/vike/onRenderClient.cjs.js +63 -43
- package/dist/vike/onRenderClient.cjs.js.map +1 -1
- package/dist/vike/onRenderClient.mjs +63 -43
- package/dist/vike/onRenderClient.mjs.map +1 -1
- package/dist/vike/onRenderHtml.cjs.js +21 -25
- package/dist/vike/onRenderHtml.cjs.js.map +1 -1
- package/dist/vike/onRenderHtml.mjs +21 -25
- package/dist/vike/onRenderHtml.mjs.map +1 -1
- package/dist/vite/plugin.cjs.js +398 -25
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +398 -25
- package/dist/vite/plugin.mjs.map +1 -1
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/core/cell.ts +25 -7
- package/src/core/hooks.ts +6 -0
- 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/core/view.ts +2 -0
- package/src/cycle/dom/utils.ts +4 -2
- package/src/devtools.d.ts +13 -0
- package/src/devtools.ts +3 -0
- package/src/extra/devGraph.ts +111 -0
- package/src/extra/devMcp.ts +1 -1
- package/src/extra/devtools.ts +18 -0
- package/src/extra/diagnostics/checks/collections.ts +31 -25
- package/src/extra/diagnostics/checks/index.ts +1 -1
- package/src/extra/diagnostics/checks/inspect.ts +68 -6
- package/src/extra/diagnostics/checks/next.ts +22 -0
- package/src/extra/diagnostics/checks/public.d.ts +121 -12
- package/src/extra/diagnostics/codes.ts +10 -0
- package/src/index.d.ts +19 -3
- package/src/vike/onRenderClient.ts +44 -46
- package/src/vike/onRenderHtml.ts +5 -26
- package/src/vike/shell.ts +33 -0
- package/src/vite/graph.ts +247 -0
- package/src/vite/mcp.ts +3 -3
- package/src/vite/plugin.d.ts +12 -0
- package/src/vite/plugin.ts +136 -22
package/dist/diagnostics.cjs.js
CHANGED
|
@@ -143,6 +143,9 @@ const CODE_TITLES = {
|
|
|
143
143
|
SYG127: 'Behavior collision or unresolvable uses entry',
|
|
144
144
|
SYG128: 'Duplicate control key',
|
|
145
145
|
SYG129: "CHILD.select() of a component this one doesn't render",
|
|
146
|
+
// PLAN-10 X-1 (D512): graph-wide checks, static only (sygnal-check)
|
|
147
|
+
SYG170: 'Context field read with no provider',
|
|
148
|
+
SYG171: 'PARENT sink no parent reads',
|
|
146
149
|
SYG112: 'Reply action has no model entry',
|
|
147
150
|
SYG130: 'href() names no route or leaves out a param',
|
|
148
151
|
SYG131: 'Route params the pattern does not use',
|
|
@@ -272,6 +275,8 @@ const CODE_TITLES = {
|
|
|
272
275
|
// checks/next.ts and sygnal-check: a component reference that is not a function (an import of
|
|
273
276
|
// a name the module doesn't export is undefined under Vitest)
|
|
274
277
|
SYG427: 'CHILD.select() of something that is not a component',
|
|
278
|
+
// 6.5.0 (checks/next.ts): state="a.b" / from="a.b" is a path; the parent state has a literal "a.b" key
|
|
279
|
+
SYG428: 'A state path that names a key containing a dot',
|
|
275
280
|
// PLAN-9 (D470): checks/next.ts; dev entry
|
|
276
281
|
SYG453: 'Two children with the same identity share one instance',
|
|
277
282
|
SYG601: "Retired in 6.0: Missing or invalid sources",
|
|
@@ -414,6 +419,9 @@ const DEV_CODE_SEVERITY = {
|
|
|
414
419
|
SYG128: 'error',
|
|
415
420
|
// PLAN-4 4-G2: static only (sygnal-check); listed so the entry knows its title
|
|
416
421
|
SYG129: 'warn',
|
|
422
|
+
// PLAN-10 X-1: static only (sygnal-check)
|
|
423
|
+
SYG170: 'warn',
|
|
424
|
+
SYG171: 'warn',
|
|
417
425
|
SYG221: 'error',
|
|
418
426
|
// PLAN-4 GS-4 (state.ts)
|
|
419
427
|
SYG222: 'warn',
|
|
@@ -480,6 +488,8 @@ const DEV_CODE_SEVERITY = {
|
|
|
480
488
|
SYG426: 'warn',
|
|
481
489
|
// checks/next.ts: CHILD.select(undefined), usually a missing export
|
|
482
490
|
SYG427: 'error',
|
|
491
|
+
// checks/next.ts: a path designator that 6.4 read as one key with a dot
|
|
492
|
+
SYG428: 'warn',
|
|
483
493
|
// PLAN-9 (checks/next.ts): D470 key identity, two children with one identity
|
|
484
494
|
SYG453: 'warn',
|
|
485
495
|
SYG612: 'error',
|
|
@@ -1153,25 +1163,44 @@ function checkCollection(component, el) {
|
|
|
1153
1163
|
state = component.addCalculated(state);
|
|
1154
1164
|
}
|
|
1155
1165
|
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`.
|
|
1166
|
+
if (typeof from === 'string')
|
|
1167
|
+
return checkFrom(component, name, from, ofName, state);
|
|
1168
|
+
// a lens or an invalid value: the core's (SYG401 / SYG412)
|
|
1161
1169
|
if (from !== undefined)
|
|
1162
1170
|
return;
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1165
|
-
}
|
|
1166
|
-
if (!problem || !once(`SYG401:${name}:undefined`))
|
|
1171
|
+
if (Array.isArray(state) || (state && Array.isArray(state.value)))
|
|
1172
|
+
return;
|
|
1173
|
+
if (!once(`SYG401:${name}:undefined`))
|
|
1167
1174
|
return;
|
|
1168
1175
|
reportSafely('SYG401', {
|
|
1169
1176
|
component,
|
|
1170
|
-
message: `<Collection of={${ofName}}> ${
|
|
1171
|
-
fix,
|
|
1177
|
+
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`,
|
|
1178
|
+
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
1179
|
data: { from: 'undefined', of: ofName },
|
|
1173
1180
|
});
|
|
1174
1181
|
}
|
|
1182
|
+
/** `from` a key or a path ('board.cards', 'boards[0].cards'), as the core reads it (cell.ts pathOf) */
|
|
1183
|
+
function checkFrom(component, name, from, ofName, state) {
|
|
1184
|
+
const ks = from.replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
1185
|
+
const calc = component._calculatedFieldNames;
|
|
1186
|
+
if (calc?.has?.(ks[0]))
|
|
1187
|
+
return;
|
|
1188
|
+
const at = ks.slice(0, -1).reduce((o, k) => (o == null ? o : o[k]), state), last = ks[ks.length - 1];
|
|
1189
|
+
if (!isPlainObject(at) && !Array.isArray(at))
|
|
1190
|
+
return; // a level still missing: renders once it appears (D178)
|
|
1191
|
+
let message;
|
|
1192
|
+
if (!(last in at)) {
|
|
1193
|
+
const arrays = Object.keys(at).filter(k => Array.isArray(at[k]));
|
|
1194
|
+
message = `Collection from="${from}" is not in state${arrays.length ? ` (array fields: '${arrays.join("', '")}')` : ''}; it renders nothing until it exists`;
|
|
1195
|
+
}
|
|
1196
|
+
else if (!Array.isArray(at[last]))
|
|
1197
|
+
message = `Collection 'from' field '${from}' is not an array; it renders nothing`;
|
|
1198
|
+
else
|
|
1199
|
+
return;
|
|
1200
|
+
if (!once(`SYG401:${name}:${from}`))
|
|
1201
|
+
return;
|
|
1202
|
+
reportSafely('SYG401', { component, message, fix: 'Set it to an array in initialState', data: { from, of: ofName } });
|
|
1203
|
+
}
|
|
1175
1204
|
const WRAPPED = '__sygnalDiagWrapped';
|
|
1176
1205
|
const collectionsCheck = {
|
|
1177
1206
|
id: 'collections',
|
|
@@ -1244,7 +1273,7 @@ onReset(() => {
|
|
|
1244
1273
|
});
|
|
1245
1274
|
/** the control that rendered a vnode (only while the dev entry is installed) */
|
|
1246
1275
|
const controlOfVnode = (vnode) => vnode && typeof vnode == 'object' ? vnodeControls.get(vnode) : undefined;
|
|
1247
|
-
const STATICS$
|
|
1276
|
+
const STATICS$2 = ['intent', 'model', 'initialState'];
|
|
1248
1277
|
const describe = (v) => v === undefined || v === null ? String(v)
|
|
1249
1278
|
: Array.isArray(v) ? `an array of ${v.length}`
|
|
1250
1279
|
: typeof v != 'object' ? `a ${typeof v} (${JSON.stringify(v)})`
|
|
@@ -1258,7 +1287,7 @@ function onControl(control, vnode) {
|
|
|
1258
1287
|
vnodeControls.set(vnode, control);
|
|
1259
1288
|
if (!staticsSeen.has(control)) {
|
|
1260
1289
|
staticsSeen.add(control);
|
|
1261
|
-
const given = STATICS$
|
|
1290
|
+
const given = STATICS$2.filter(s => control[s] != null);
|
|
1262
1291
|
if (given.length) {
|
|
1263
1292
|
devReport('SYG125', {
|
|
1264
1293
|
message: `The control ${key} has ${given.map(s => '.' + s).join(', ')}, but controls are elements, not components: ${given.length > 1 ? 'these statics are' : 'it is'} never used`,
|
|
@@ -3455,6 +3484,9 @@ const slim = (d) => {
|
|
|
3455
3484
|
out.fix = d.fix;
|
|
3456
3485
|
if (d.docsUrl)
|
|
3457
3486
|
out.docsUrl = d.docsUrl;
|
|
3487
|
+
// PLAN-10 P-1: when it was reported (runtime diagnostics are reported once per process)
|
|
3488
|
+
if (typeof d.timestamp === 'number')
|
|
3489
|
+
out.timestamp = d.timestamp;
|
|
3458
3490
|
if (d.data !== undefined) {
|
|
3459
3491
|
// keep it JSON-safe (data can hold errors, vnodes, ...)
|
|
3460
3492
|
try {
|
|
@@ -3558,12 +3590,61 @@ function childrenOf(r, all) {
|
|
|
3558
3590
|
const key = `${c.name}\u0000${via[c.kind]}`;
|
|
3559
3591
|
const entry = out.get(key);
|
|
3560
3592
|
if (entry)
|
|
3561
|
-
entry.count =
|
|
3593
|
+
entry.count = entry.ids.push(c.id);
|
|
3562
3594
|
else
|
|
3563
|
-
out.set(key, { name: c.name, via: via[c.kind], count: 1 });
|
|
3595
|
+
out.set(key, { name: c.name, via: via[c.kind], count: 1, ids: [c.id] });
|
|
3564
3596
|
}
|
|
3565
3597
|
return [...out.values()];
|
|
3566
3598
|
}
|
|
3599
|
+
/** the statics sygnal-check reads (its STATIC_PROPS): a component function with none is view-only */
|
|
3600
|
+
const STATICS$1 = ['intent', 'model', 'initialState', 'isolatedState', 'context', 'calculated', 'connections', 'resources', 'route', 'head', 'uses', 'timers', 'persist', 'viewTransitions', 'browser', 'agent'];
|
|
3601
|
+
/** PLAN-10 D522: no statics and nothing the core added (renderComponent's test intent, a behavior's model) */
|
|
3602
|
+
function isViewOnly(c) {
|
|
3603
|
+
const def = c && c.__next && c.__next.def;
|
|
3604
|
+
if (!def || def.intent || (def.handlers && def.handlers.size) || def.initialState !== undefined)
|
|
3605
|
+
return false;
|
|
3606
|
+
const fn = def.view;
|
|
3607
|
+
return !!fn && STATICS$1.every(k => fn[k] === undefined);
|
|
3608
|
+
}
|
|
3609
|
+
/** PLAN-10 P-2 (D544): the static key sygnal/vite's dev transform stamped on the component function */
|
|
3610
|
+
function stampedKey(c) {
|
|
3611
|
+
const fn = c && c.__next && c.__next.def && c.__next.def.view;
|
|
3612
|
+
const k = fn && fn.__sygnalKey;
|
|
3613
|
+
return typeof k === 'string' ? k : undefined;
|
|
3614
|
+
}
|
|
3615
|
+
/** PLAN-10 D530: the context keys the instance's latest render read (InstanceView.reads); every key when it read them all */
|
|
3616
|
+
function contextReadsOf(c) {
|
|
3617
|
+
const v = c && c.__next;
|
|
3618
|
+
const reads = v && v.reads;
|
|
3619
|
+
if (!reads)
|
|
3620
|
+
return [];
|
|
3621
|
+
const out = [...reads].filter(k => k !== '\u0000all');
|
|
3622
|
+
if (reads.has('\u0000all')) {
|
|
3623
|
+
try {
|
|
3624
|
+
for (const k of Object.keys(v.context || {}))
|
|
3625
|
+
if (!out.includes(k))
|
|
3626
|
+
out.push(k);
|
|
3627
|
+
}
|
|
3628
|
+
catch (_) { /* no context */ }
|
|
3629
|
+
}
|
|
3630
|
+
return out;
|
|
3631
|
+
}
|
|
3632
|
+
/** PLAN-10 D522: the app (run()) of each instance: the index of its root among the live roots, oldest first */
|
|
3633
|
+
function appsOf(all) {
|
|
3634
|
+
const byId = new Map(all.map(r => [r.id, r]));
|
|
3635
|
+
const roots = all.filter(r => r.parentId === null).sort((a, b) => Number(a.id) - Number(b.id));
|
|
3636
|
+
const index = new Map(roots.map((r, i) => [r, i]));
|
|
3637
|
+
const out = new Map();
|
|
3638
|
+
for (const r of all) {
|
|
3639
|
+
let top = r;
|
|
3640
|
+
for (let n = 0; top && top.parentId !== null && n < 10000; n++)
|
|
3641
|
+
top = byId.get(top.parentId);
|
|
3642
|
+
const i = top && index.get(top);
|
|
3643
|
+
if (i !== undefined)
|
|
3644
|
+
out.set(r, i);
|
|
3645
|
+
}
|
|
3646
|
+
return out;
|
|
3647
|
+
}
|
|
3567
3648
|
/** The app graph of the live component instances (see ./public.d.ts). */
|
|
3568
3649
|
function inspect(options = {}) {
|
|
3569
3650
|
// report SYG105 (info) for EVENTS types selected but never emitted so far
|
|
@@ -3588,21 +3669,28 @@ function inspect(options = {}) {
|
|
|
3588
3669
|
const ids = options.ids && new Set(options.ids.map(String));
|
|
3589
3670
|
const recs = ids ? all.filter(r => ids.has(r.id)) : all;
|
|
3590
3671
|
const names = new Set(recs.map(r => r.name));
|
|
3672
|
+
const apps = appsOf(all);
|
|
3591
3673
|
const components = recs.map(r => {
|
|
3592
3674
|
const c = r.instance;
|
|
3593
3675
|
const calculated = c && c.calculated && typeof c.calculated === 'object' ? Object.keys(c.calculated) : [];
|
|
3594
3676
|
const selectors = selectorsOf(r, options, diags);
|
|
3595
3677
|
const controls = controlsOf(r, selectors);
|
|
3678
|
+
const key = stampedKey(c);
|
|
3596
3679
|
return {
|
|
3597
3680
|
name: r.name,
|
|
3598
3681
|
id: r.id,
|
|
3682
|
+
...(key ? { key } : {}),
|
|
3599
3683
|
parentId: r.parentId,
|
|
3600
3684
|
kind: r.kind,
|
|
3685
|
+
...(isViewOnly(c) ? { viewOnly: true } : {}),
|
|
3686
|
+
...(r.parentId === null ? { entry: true } : {}),
|
|
3687
|
+
...(apps.has(r) ? { app: apps.get(r) } : {}),
|
|
3688
|
+
...(c && c.__next && c.__next.shown === false ? { shown: false } : {}),
|
|
3601
3689
|
actions: actionsOf(r),
|
|
3602
3690
|
stateKeys: stateKeysOf(c, calculated),
|
|
3603
3691
|
calculated,
|
|
3604
3692
|
contextProvides: c && isPlainObject(c.context) ? Object.keys(c.context) : [],
|
|
3605
|
-
contextConsumes:
|
|
3693
|
+
contextConsumes: contextReadsOf(c),
|
|
3606
3694
|
eventsEmitted: [...r.eventsEmitted],
|
|
3607
3695
|
eventsSelected: [...r.eventsSelected],
|
|
3608
3696
|
children: childrenOf(r, all),
|
|
@@ -3614,14 +3702,21 @@ function inspect(options = {}) {
|
|
|
3614
3702
|
});
|
|
3615
3703
|
const cache = cacheOf(recs) || {};
|
|
3616
3704
|
const events = {};
|
|
3617
|
-
|
|
3705
|
+
// names once each (as before); PLAN-10 D522: every instance's id in emitterIds / selectorIds
|
|
3706
|
+
const ev = (type) => events[type] || (events[type] = { emitters: [], selectors: [], emitterIds: [], selectorIds: [] });
|
|
3618
3707
|
const add = (list, name) => { if (!list.includes(name))
|
|
3619
3708
|
list.push(name); };
|
|
3620
3709
|
for (const c of components) {
|
|
3621
|
-
for (const t of c.eventsEmitted)
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3710
|
+
for (const t of c.eventsEmitted) {
|
|
3711
|
+
const e = ev(t);
|
|
3712
|
+
add(e.emitters, c.name);
|
|
3713
|
+
e.emitterIds.push(c.id);
|
|
3714
|
+
}
|
|
3715
|
+
for (const t of c.eventsSelected) {
|
|
3716
|
+
const e = ev(t);
|
|
3717
|
+
add(e.selectors, c.name);
|
|
3718
|
+
e.selectorIds.push(c.id);
|
|
3719
|
+
}
|
|
3625
3720
|
}
|
|
3626
3721
|
// emitted on the bus by something that isn't a listed component (e.g. a driver, a pruned component)
|
|
3627
3722
|
if (!ids)
|
|
@@ -3736,6 +3831,22 @@ function removed(component, name, anchor, detail) {
|
|
|
3736
3831
|
data: { form: anchor, guide: `${MIGRATE}#${anchor}`, ...(detail ? { detail } : {}) },
|
|
3737
3832
|
});
|
|
3738
3833
|
}
|
|
3834
|
+
/**
|
|
3835
|
+
* SYG428 (6.5.0): `state="a.b"` / `from="a.b"` is a path, read as `state.a.b`. Before 6.5.0 it was
|
|
3836
|
+
* one key named "a.b"; when the owner's state still has such a key, the designator now misses it.
|
|
3837
|
+
*/
|
|
3838
|
+
function dottedKey(f, what, designator, ownerState) {
|
|
3839
|
+
if (typeof designator != 'string' || !/[.[]/.test(designator) || !isPlainObject(ownerState))
|
|
3840
|
+
return;
|
|
3841
|
+
if (!Object.prototype.hasOwnProperty.call(ownerState, designator) || !once(`SYG428:${f.name}:${designator}`))
|
|
3842
|
+
return;
|
|
3843
|
+
devReport('SYG428', {
|
|
3844
|
+
component: f.name,
|
|
3845
|
+
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}"`,
|
|
3846
|
+
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 }) }}`,
|
|
3847
|
+
data: { designator, what },
|
|
3848
|
+
});
|
|
3849
|
+
}
|
|
3739
3850
|
/** SYG427: CHILD.select(x) with no component (an import of a name the module doesn't export) */
|
|
3740
3851
|
function notComponent(f, x) {
|
|
3741
3852
|
if (!once(`SYG427:${f.name}`))
|
|
@@ -4102,12 +4213,18 @@ function nextHooks(_api) {
|
|
|
4102
4213
|
// G-527: one chain of SYG149 checks over the latest patches (G-501: it stops once every instance
|
|
4103
4214
|
// that renders a named Collection is disposed, the app's dispose included)
|
|
4104
4215
|
const vtCheck = vtChecker(() => vtp.size && on());
|
|
4216
|
+
// SYG428: the live instances by id, to read a child's parent state
|
|
4217
|
+
const live = new Map();
|
|
4105
4218
|
return {
|
|
4106
4219
|
onCreate(iv) {
|
|
4220
|
+
live.set(iv.id, iv);
|
|
4107
4221
|
if (!on())
|
|
4108
4222
|
return;
|
|
4109
4223
|
log.onCreate(iv);
|
|
4110
4224
|
const f = fac(iv);
|
|
4225
|
+
const parent = live.get(iv.parentId);
|
|
4226
|
+
if (parent)
|
|
4227
|
+
dottedKey(f, `<${f.name} state`, iv.props?.state, parent.state);
|
|
4111
4228
|
removedStatics(f);
|
|
4112
4229
|
if (!iv.def.intent)
|
|
4113
4230
|
wire(iv, []);
|
|
@@ -4166,6 +4283,7 @@ function nextHooks(_api) {
|
|
|
4166
4283
|
},
|
|
4167
4284
|
onDispose(iv) {
|
|
4168
4285
|
vtp.delete(iv);
|
|
4286
|
+
live.delete(iv.id);
|
|
4169
4287
|
H.onDispose(fac(iv));
|
|
4170
4288
|
},
|
|
4171
4289
|
onAction(iv, a) { log.onAction(iv, a); },
|
|
@@ -4213,6 +4331,7 @@ function nextHooks(_api) {
|
|
|
4213
4331
|
checkCollection(f, { data: { props } });
|
|
4214
4332
|
}
|
|
4215
4333
|
catch (_) { /* ignore */ }
|
|
4334
|
+
dottedKey(f, `<${sel == 'collection' ? 'Collection' : 'VirtualCollection'} from`, props.from, owner.state);
|
|
4216
4335
|
// G-419: SYG148 for a prefix that isn't an identifier; a valid one is checked for
|
|
4217
4336
|
// duplicate names after each patch (SYG149). G-460: a falsy one names nothing (as the host)
|
|
4218
4337
|
const p = props.viewTransitionName;
|