@jxsuite/runtime 4.0.0 → 4.0.1
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/runtime.js +94 -7
- package/dist/runtime.js.map +3 -3
- package/package.json +1 -1
- package/src/runtime.ts +294 -6
package/package.json
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -2467,6 +2467,11 @@ function renderMappedArrayInto(
|
|
|
2467
2467
|
parentEl.append(anchor);
|
|
2468
2468
|
const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef, key: keyRef } = arrayDef;
|
|
2469
2469
|
const keyPointer = isRefObj(keyRef) ? keyRef.$ref : null;
|
|
2470
|
+
/* The instantiation chain the list belongs to (spec.md §16.9), for the same reason
|
|
2471
|
+
{@link renderSwitch} keeps one: a row that arrives with new data is inserted from an effect
|
|
2472
|
+
re-run, and the instance it holds must connect under the chain of the definition that drew
|
|
2473
|
+
the list. */
|
|
2474
|
+
const instancePath = _instancePath;
|
|
2470
2475
|
|
|
2471
2476
|
let warned = false;
|
|
2472
2477
|
const warnOnce = (message: string): void => {
|
|
@@ -2572,7 +2577,7 @@ function renderMappedArrayInto(
|
|
|
2572
2577
|
|
|
2573
2578
|
pauseTracking();
|
|
2574
2579
|
try {
|
|
2575
|
-
reconcile(list, keys);
|
|
2580
|
+
underInstancePath(reexpand(instancePath), () => reconcile(list, keys));
|
|
2576
2581
|
} finally {
|
|
2577
2582
|
resetTracking();
|
|
2578
2583
|
}
|
|
@@ -2687,6 +2692,11 @@ export function resolveTagName(tagName: unknown, scope: JxScope): string {
|
|
|
2687
2692
|
function renderSwitch(def: JxElement, state: JxScope, options?: JxRenderOptions) {
|
|
2688
2693
|
const path = options?._path ?? [];
|
|
2689
2694
|
const container = document.createElement(resolveTagName(def.tagName, state));
|
|
2695
|
+
/* The instantiation chain this switch belongs to (spec.md §16.9), read now because the effect
|
|
2696
|
+
below re-runs with nothing on the stack. A case that flips after the first paint puts it back
|
|
2697
|
+
around the insertion, so an instance the new case creates connects under the chain of the
|
|
2698
|
+
definition that drew the switch rather than under none. See {@link _instancePath}. */
|
|
2699
|
+
const instancePath = _instancePath;
|
|
2690
2700
|
|
|
2691
2701
|
if (options?.onNodeCreated) {
|
|
2692
2702
|
options.onNodeCreated(container, path, def, state);
|
|
@@ -2753,7 +2763,9 @@ function renderSwitch(def: JxElement, state: JxScope, options?: JxRenderOptions)
|
|
|
2753
2763
|
return;
|
|
2754
2764
|
}
|
|
2755
2765
|
container.replaceChildren();
|
|
2756
|
-
|
|
2766
|
+
underInstancePath(reexpand(instancePath), () =>
|
|
2767
|
+
scope.run(() => container.append(renderNode(doc, childScope, childOpts))),
|
|
2768
|
+
);
|
|
2757
2769
|
})
|
|
2758
2770
|
.catch((error: unknown) =>
|
|
2759
2771
|
console.error("Jx $switch: failed to load external case", caseDef.$ref, error),
|
|
@@ -2766,7 +2778,9 @@ function renderSwitch(def: JxElement, state: JxScope, options?: JxRenderOptions)
|
|
|
2766
2778
|
// Change to anything it read. The bindings inside it track for themselves.
|
|
2767
2779
|
pauseTracking();
|
|
2768
2780
|
try {
|
|
2769
|
-
|
|
2781
|
+
underInstancePath(reexpand(instancePath), () =>
|
|
2782
|
+
scope.run(() => container.append(renderNode(caseDef, state, childOpts))),
|
|
2783
|
+
);
|
|
2770
2784
|
} finally {
|
|
2771
2785
|
resetTracking();
|
|
2772
2786
|
}
|
|
@@ -3943,6 +3957,219 @@ function injectHead(entries: JxHeadEntry[], _base: string) {
|
|
|
3943
3957
|
}
|
|
3944
3958
|
}
|
|
3945
3959
|
|
|
3960
|
+
// ─── Instantiation chain (spec.md §16.9) ─────────────────────────────────────
|
|
3961
|
+
|
|
3962
|
+
/**
|
|
3963
|
+
* How deep component nesting may go before an instance refuses to render.
|
|
3964
|
+
*
|
|
3965
|
+
* The same number the compiler holds in `MAX_COMPONENT_NESTING` (`@jxsuite/compiler`, `shared.ts`),
|
|
3966
|
+
* and for the same reason: the cycle check in {@link extendInstancePath} catches a definition that
|
|
3967
|
+
* renders ITSELF — same tag, same props — which is the only shape that can never terminate, while a
|
|
3968
|
+
* self-reference whose props change at every level is data-driven recursion that a `$switch` may or
|
|
3969
|
+
* may not bottom out. The seen-set cannot tell those apart, so this cap is what turns a forgotten
|
|
3970
|
+
* stop into a diagnostic instead of a page that never settles. It is written twice because the
|
|
3971
|
+
* compiler depends on the runtime and not the other way round, and the runtime's browser bundle
|
|
3972
|
+
* cannot reach into the build tool; spec.md §16.9 is the one statement both are held to, and the
|
|
3973
|
+
* compiler's `nesting-cap-agreement.test.ts` asserts its copy against this export, so the two
|
|
3974
|
+
* numbers cannot part without a red test in the workspace that owns the copy.
|
|
3975
|
+
*/
|
|
3976
|
+
export const MAX_COMPONENT_NESTING = 32;
|
|
3977
|
+
|
|
3978
|
+
/** One instance on the chain: its tag, and the props it was given, as an identity. */
|
|
3979
|
+
interface InstanceFrame {
|
|
3980
|
+
tag: string;
|
|
3981
|
+
/** Every value the instance supplied that its definition's state read (§16.9). */
|
|
3982
|
+
props: Record<string, unknown>;
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
/**
|
|
3986
|
+
* The chain an instance is created under, and where on it the current expansion began.
|
|
3987
|
+
*
|
|
3988
|
+
* `frames` is every instance from the page-level one down, outermost first. `expansion` is the
|
|
3989
|
+
* index of the first frame that belongs to the SAME synchronous pass as the instance being created:
|
|
3990
|
+
* the cycle check (§16.9 rule 1) looks back no further than that, while the cap (rule 2) counts
|
|
3991
|
+
* every frame. The distinction is what an effect re-run does to certainty. Along one pass a
|
|
3992
|
+
* repeated frame is a render that repeats itself, deterministically, forever. Across a re-run it is
|
|
3993
|
+
* not: the frames above the boundary rendered to completion once, and whether the page asks for one
|
|
3994
|
+
* of them again is its data — an `onMount` that flips a `$switch` into the same tag every time, or
|
|
3995
|
+
* a click that opens one more level of a node that starts closed — which the runtime cannot tell
|
|
3996
|
+
* apart. So the cycle check is not asked across the boundary, and the loop that lives in re-runs is
|
|
3997
|
+
* ended by the cap instead: thirty-three empty hosts and a diagnostic, not a page that never
|
|
3998
|
+
* settles.
|
|
3999
|
+
*/
|
|
4000
|
+
interface InstancePath {
|
|
4001
|
+
frames: readonly InstanceFrame[];
|
|
4002
|
+
expansion: number;
|
|
4003
|
+
}
|
|
4004
|
+
|
|
4005
|
+
/** The chain a page-level render runs under: nothing above it. */
|
|
4006
|
+
const ROOT_INSTANCE_PATH: InstancePath = { expansion: 0, frames: [] };
|
|
4007
|
+
|
|
4008
|
+
/**
|
|
4009
|
+
* The chain of instances whose definitions are being rendered RIGHT NOW.
|
|
4010
|
+
*
|
|
4011
|
+
* A module-level stack rather than a WeakMap from element to chain, and it is worth saying why that
|
|
4012
|
+
* survives `connectedCallback` being async. The callback awaits `buildScope` before it touches the
|
|
4013
|
+
* DOM, and by the time it resumes, whatever the enclosing instance pushed here has long been popped
|
|
4014
|
+
* — so the stack alone would be empty for every instance and catch nothing. What saves it is WHEN
|
|
4015
|
+
* an instance connects: `this.append(child)` in the enclosing render runs the child's
|
|
4016
|
+
* `connectedCallback` synchronously, and every way a definition brings an instance into being —
|
|
4017
|
+
* `renderCustomElementWithProps`, the bare-tag branch of `renderNode`, a nested element inside
|
|
4018
|
+
* either, and a prerendered host drawing its children again as it upgrades — appends it inside that
|
|
4019
|
+
* same synchronous stretch. So the callback reads the stack ONCE, before its first `await`, and
|
|
4020
|
+
* carries the copy across the gap. A WeakMap would need a write at each of those creation sites to
|
|
4021
|
+
* say the same thing.
|
|
4022
|
+
*
|
|
4023
|
+
* A subtree an effect renders LATER — a `$switch` case that flips after the first paint, a `$map`
|
|
4024
|
+
* row that arrives with new data — connects with nothing on the stack, because no enclosing render
|
|
4025
|
+
* is running when the effect re-runs; a write from `onMount` or a click is what triggered it. So
|
|
4026
|
+
* the two renderers that create nodes from an effect each capture the stack when they are first
|
|
4027
|
+
* rendered (which is inside the enclosing pass) and put it back, re-expanded, around every later
|
|
4028
|
+
* insertion ({@link underInstancePath}). That is the one thing this stack needs beyond the
|
|
4029
|
+
* synchronous stretch, and it is what makes the `onMount`-flip shape a diagnostic rather than a
|
|
4030
|
+
* hang. Reading `@vue/reactivity`'s `getCurrentScope()` there instead would find nothing: an effect
|
|
4031
|
+
* re-run restores no scope, and a case or a row renders in a DETACHED scope with no parent.
|
|
4032
|
+
*/
|
|
4033
|
+
let _instancePath: InstancePath = ROOT_INSTANCE_PATH;
|
|
4034
|
+
|
|
4035
|
+
/**
|
|
4036
|
+
* Run `fn` with `path` as the chain, so every instance that CONNECTS during it reads that chain.
|
|
4037
|
+
*
|
|
4038
|
+
* Restored rather than cleared afterwards: the enclosing render's chain is what was there, and a
|
|
4039
|
+
* restore needs no argument about whether anything was.
|
|
4040
|
+
*
|
|
4041
|
+
* @param {InstancePath} path - The chain the instances `fn` connects are created under
|
|
4042
|
+
* @param {() => T} fn - The render, or the insertion that connects what a render made
|
|
4043
|
+
* @returns {T} What `fn` returned
|
|
4044
|
+
*/
|
|
4045
|
+
function underInstancePath<T>(path: InstancePath, fn: () => T): T {
|
|
4046
|
+
const was = _instancePath;
|
|
4047
|
+
_instancePath = path;
|
|
4048
|
+
try {
|
|
4049
|
+
return fn();
|
|
4050
|
+
} finally {
|
|
4051
|
+
_instancePath = was;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
/**
|
|
4056
|
+
* The chain a later insertion runs under: the same frames, with the expansion beginning now.
|
|
4057
|
+
*
|
|
4058
|
+
* Called on EVERY run of a `$switch` case or a `$map` reconcile, the first one included — harmless
|
|
4059
|
+
* there, because a first run builds into a detached container and nothing connects until the
|
|
4060
|
+
* enclosing pass appends it, under the enclosing pass's own chain.
|
|
4061
|
+
*
|
|
4062
|
+
* @param {InstancePath} path - The chain captured when the renderer was first rendered
|
|
4063
|
+
* @returns {InstancePath}
|
|
4064
|
+
*/
|
|
4065
|
+
function reexpand(path: InstancePath): InstancePath {
|
|
4066
|
+
return { expansion: path.frames.length, frames: path.frames };
|
|
4067
|
+
}
|
|
4068
|
+
|
|
4069
|
+
/**
|
|
4070
|
+
* Whether two prop values are the same input to a definition.
|
|
4071
|
+
*
|
|
4072
|
+
* Reference equality first — a `$ref` into the parent's state forwards the same primitive, the same
|
|
4073
|
+
* reactive proxy or the same function to the next level — and structural equality for plain data
|
|
4074
|
+
* rebuilt at each level (`data-jx-props` parsed twice is two objects with one meaning). Plain data
|
|
4075
|
+
* is what JSON can describe: an array, or an object with no prototype but `Object`'s, reactive or
|
|
4076
|
+
* raw. Anything else — a `Map`, a `Set`, a class instance, a circular object — is different from
|
|
4077
|
+
* everything but itself, because its JSON form would not say what it holds and two different values
|
|
4078
|
+
* that serialise alike would close a cycle that was never there. The cost of that policy is a loop
|
|
4079
|
+
* the depth cap catches instead; the alternative is a false diagnostic on a page that would have
|
|
4080
|
+
* ended. Inside plain data the comparison IS the JSON form, so a member JSON cannot see (a
|
|
4081
|
+
* function, a `Map` two levels down) is not seen here either; §16.9 says so.
|
|
4082
|
+
*
|
|
4083
|
+
* @param {unknown} a
|
|
4084
|
+
* @param {unknown} b
|
|
4085
|
+
* @returns {boolean}
|
|
4086
|
+
*/
|
|
4087
|
+
function samePropValue(a: unknown, b: unknown): boolean {
|
|
4088
|
+
if (Object.is(a, b)) {
|
|
4089
|
+
return true;
|
|
4090
|
+
}
|
|
4091
|
+
if (!isPlainData(a) || !isPlainData(b)) {
|
|
4092
|
+
return false;
|
|
4093
|
+
}
|
|
4094
|
+
try {
|
|
4095
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
4096
|
+
} catch {
|
|
4097
|
+
return false;
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
|
|
4101
|
+
/**
|
|
4102
|
+
* Whether a value is an array or a plain object — the shapes whose JSON form is the whole value.
|
|
4103
|
+
*
|
|
4104
|
+
* @param {unknown} value
|
|
4105
|
+
* @returns {value is object}
|
|
4106
|
+
*/
|
|
4107
|
+
function isPlainData(value: unknown): value is object {
|
|
4108
|
+
if (typeof value !== "object" || value === null) {
|
|
4109
|
+
return false;
|
|
4110
|
+
}
|
|
4111
|
+
const raw: unknown = toRaw(value);
|
|
4112
|
+
if (Array.isArray(raw)) {
|
|
4113
|
+
return true;
|
|
4114
|
+
}
|
|
4115
|
+
const proto: unknown = Object.getPrototypeOf(raw);
|
|
4116
|
+
return proto === Object.prototype || proto === null;
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
/**
|
|
4120
|
+
* Whether two frames are the same instance: one tag, one set of prop names, equal values.
|
|
4121
|
+
*
|
|
4122
|
+
* @param {InstanceFrame} a
|
|
4123
|
+
* @param {InstanceFrame} b
|
|
4124
|
+
* @returns {boolean}
|
|
4125
|
+
*/
|
|
4126
|
+
function sameFrame(a: InstanceFrame, b: InstanceFrame): boolean {
|
|
4127
|
+
if (a.tag !== b.tag) {
|
|
4128
|
+
return false;
|
|
4129
|
+
}
|
|
4130
|
+
const keys = Object.keys(a.props);
|
|
4131
|
+
if (keys.length !== Object.keys(b.props).length) {
|
|
4132
|
+
return false;
|
|
4133
|
+
}
|
|
4134
|
+
return keys.every(
|
|
4135
|
+
(key) => Object.hasOwn(b.props, key) && samePropValue(a.props[key], b.props[key]),
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
|
|
4139
|
+
/**
|
|
4140
|
+
* The chain an instance's own render runs under: the enclosing chain plus itself — or the
|
|
4141
|
+
* diagnostic that says why it must not render at all.
|
|
4142
|
+
*
|
|
4143
|
+
* The two messages are the compiler's, word for word (`renderComponentInstance` in
|
|
4144
|
+
* `@jxsuite/compiler`), so a page that fails to build and a canvas that fails to paint say the same
|
|
4145
|
+
* thing about the same definition. Meeting a frame already on the chain, within the current
|
|
4146
|
+
* expansion, is the instance rendering itself with the same props, which never ends; a chain at the
|
|
4147
|
+
* cap is a self-reference whose props change — or whose repeats live in re-runs, where the cycle
|
|
4148
|
+
* check is not asked ({@link InstancePath}) — and whose stop case never came.
|
|
4149
|
+
*
|
|
4150
|
+
* @param {InstancePath} path - The chain the instance was created under
|
|
4151
|
+
* @param {InstanceFrame} frame - The instance about to render
|
|
4152
|
+
* @returns {InstancePath} The chain its children render under
|
|
4153
|
+
* @throws {Error} The named diagnostic — never a stack overflow, never a hang
|
|
4154
|
+
* @docs framework/concepts/components
|
|
4155
|
+
*/
|
|
4156
|
+
function extendInstancePath(path: InstancePath, frame: InstanceFrame): InstancePath {
|
|
4157
|
+
const chain = [...path.frames.map((f) => f.tag), frame.tag].join(" → ");
|
|
4158
|
+
if (path.frames.slice(path.expansion).some((f) => sameFrame(f, frame))) {
|
|
4159
|
+
throw new Error(
|
|
4160
|
+
`Component <${frame.tag}> renders itself: ${chain}. A component cannot appear inside its own ` +
|
|
4161
|
+
`definition with the same props — the expansion would never end.`,
|
|
4162
|
+
);
|
|
4163
|
+
}
|
|
4164
|
+
if (path.frames.length >= MAX_COMPONENT_NESTING) {
|
|
4165
|
+
throw new Error(
|
|
4166
|
+
`Component nesting exceeds ${MAX_COMPONENT_NESTING} levels: ${chain}. A component that ` +
|
|
4167
|
+
`renders itself with changing props needs a case that stops.`,
|
|
4168
|
+
);
|
|
4169
|
+
}
|
|
4170
|
+
return { expansion: path.expansion, frames: [...path.frames, frame] };
|
|
4171
|
+
}
|
|
4172
|
+
|
|
3946
4173
|
/**
|
|
3947
4174
|
* Register a custom element from a Jx document.
|
|
3948
4175
|
*
|
|
@@ -4009,6 +4236,17 @@ export async function defineElement(source: string | JxDocument, baseUrl?: strin
|
|
|
4009
4236
|
}
|
|
4010
4237
|
this._jxInitialized = true;
|
|
4011
4238
|
|
|
4239
|
+
/* The chain this instance was created under, read BEFORE the first `await`: the enclosing
|
|
4240
|
+
render is still on the stack now and will not be when this callback resumes. See
|
|
4241
|
+
{@link _instancePath} for why that ordering is the whole mechanism. */
|
|
4242
|
+
const enclosingPath = _instancePath;
|
|
4243
|
+
/* What this instance was given, recorded beside each write into state below. It is the
|
|
4244
|
+
instance's identity on the chain (§16.9): two nested instances of one tag that were given
|
|
4245
|
+
the same values render the same thing, forever. Only a value the definition's state READS
|
|
4246
|
+
is recorded — a `$props` key no state entry declares changes nothing about the render, so
|
|
4247
|
+
a definition that "varies" one at each level is still rendering itself. */
|
|
4248
|
+
const supplied: Record<string, unknown> = {};
|
|
4249
|
+
|
|
4012
4250
|
// The definition as it stands NOW — a redefinition since this class was made is honoured.
|
|
4013
4251
|
const live = _elementDefs.get(tagName) ?? { base: initialBase, doc: initialDef };
|
|
4014
4252
|
const def = live.doc;
|
|
@@ -4026,6 +4264,9 @@ export async function defineElement(source: string | JxDocument, baseUrl?: strin
|
|
|
4026
4264
|
for (const attr of observedAttrs) {
|
|
4027
4265
|
if (this.hasAttribute(attr)) {
|
|
4028
4266
|
absorbAttribute(state, attr, this.getAttribute(attr));
|
|
4267
|
+
// The raw string, not the coerced value: coercion is a function of it, so two equal
|
|
4268
|
+
// Strings are one input.
|
|
4269
|
+
supplied[attr] = this.getAttribute(attr);
|
|
4029
4270
|
}
|
|
4030
4271
|
}
|
|
4031
4272
|
|
|
@@ -4039,6 +4280,7 @@ export async function defineElement(source: string | JxDocument, baseUrl?: strin
|
|
|
4039
4280
|
}
|
|
4040
4281
|
if (key in (def.state ?? {})) {
|
|
4041
4282
|
state[key] = val;
|
|
4283
|
+
supplied[key] = val;
|
|
4042
4284
|
}
|
|
4043
4285
|
}
|
|
4044
4286
|
} catch {}
|
|
@@ -4060,6 +4302,7 @@ export async function defineElement(source: string | JxDocument, baseUrl?: strin
|
|
|
4060
4302
|
}
|
|
4061
4303
|
if (key in (def.state ?? {})) {
|
|
4062
4304
|
state[key] = this.getAttribute(name);
|
|
4305
|
+
supplied[key] = this.getAttribute(name);
|
|
4063
4306
|
this.removeAttribute(name);
|
|
4064
4307
|
}
|
|
4065
4308
|
}
|
|
@@ -4075,8 +4318,49 @@ export async function defineElement(source: string | JxDocument, baseUrl?: strin
|
|
|
4075
4318
|
instanceSupplies(this, key)
|
|
4076
4319
|
) {
|
|
4077
4320
|
state[key] = (this as Record<string, unknown>)[key];
|
|
4321
|
+
supplied[key] = (this as Record<string, unknown>)[key];
|
|
4078
4322
|
}
|
|
4079
4323
|
}
|
|
4324
|
+
|
|
4325
|
+
/* Every input is known now, so this is where an instance that would never finish is
|
|
4326
|
+
refused (§16.9): before it has a state, a style or a child, so a refused host is an empty
|
|
4327
|
+
element the page renders around — emptied of what the definition wrote into it for its
|
|
4328
|
+
slots too, since nothing will distribute that. The diagnostic is REPORTED rather than
|
|
4329
|
+
thrown onward: a throw from an async `connectedCallback` is an unhandled rejection that
|
|
4330
|
+
names no element and stops nothing. It goes two ways. The console is the runtime's
|
|
4331
|
+
surface for a render that fails after the page has settled (`$switch` external cases and
|
|
4332
|
+
the dev proxies report there too). The `jx-error` event is the surface a HOST can hold:
|
|
4333
|
+
it bubbles from the refused element with the error on it, so a page, or the studio canvas
|
|
4334
|
+
wrapping one, can collect it where a console line inside an iframe reaches nobody. The
|
|
4335
|
+
rest of the page keeps rendering, which is the point.
|
|
4336
|
+
|
|
4337
|
+
Neither surface is used for a host that is no longer in the document. A refused instance
|
|
4338
|
+
was connected when this callback began, and one that is not by the time the verdict is in
|
|
4339
|
+
belongs to a subtree the page discarded during the `await` above: a prerendered host of the
|
|
4340
|
+
same tag that was parsed INSIDE another and re-rendered from the definition by the outer
|
|
4341
|
+
one's upgrade, which captures it as slot content and empties itself. Its replacement is
|
|
4342
|
+
the instance that reports, on the chain the outer upgrade draws; reporting from the
|
|
4343
|
+
discarded copy too said one defect twice. The refusal itself still stands, so a host
|
|
4344
|
+
re-attached later is an empty element and not a hang. */
|
|
4345
|
+
let ownPath: InstancePath;
|
|
4346
|
+
try {
|
|
4347
|
+
ownPath = extendInstancePath(enclosingPath, { props: supplied, tag: tagName });
|
|
4348
|
+
} catch (error) {
|
|
4349
|
+
this.replaceChildren();
|
|
4350
|
+
if (!this.isConnected) {
|
|
4351
|
+
return;
|
|
4352
|
+
}
|
|
4353
|
+
console.error(`Jx: <${tagName}> was not rendered:`, error);
|
|
4354
|
+
this.dispatchEvent(
|
|
4355
|
+
new ErrorEvent("jx-error", {
|
|
4356
|
+
bubbles: true,
|
|
4357
|
+
composed: true,
|
|
4358
|
+
error,
|
|
4359
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4360
|
+
}),
|
|
4361
|
+
);
|
|
4362
|
+
return;
|
|
4363
|
+
}
|
|
4080
4364
|
/*
|
|
4081
4365
|
* Set up property getters/setters that forward into reactive state. Private entries get NO
|
|
4082
4366
|
* accessor: the property-first interface IS the props mechanism, so defining one would leave
|
|
@@ -4134,10 +4418,14 @@ export async function defineElement(source: string | JxDocument, baseUrl?: strin
|
|
|
4134
4418
|
themselves. Restored rather than cleared, because one definition may render another. */
|
|
4135
4419
|
const wasInside = _canvasInsideStampedHost;
|
|
4136
4420
|
_canvasInsideStampedHost ||= this.dataset.jxPath !== undefined;
|
|
4421
|
+
/* And the chain, for the same span: an instance this definition draws connects inside
|
|
4422
|
+
`this.append` below and reads it there ({@link underInstancePath}). */
|
|
4137
4423
|
try {
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4424
|
+
underInstancePath(ownPath, () => {
|
|
4425
|
+
for (const childDef of children) {
|
|
4426
|
+
this.append(renderNode(childDef, state));
|
|
4427
|
+
}
|
|
4428
|
+
});
|
|
4141
4429
|
} finally {
|
|
4142
4430
|
_canvasInsideStampedHost = wasInside;
|
|
4143
4431
|
}
|