@abide/abide 0.38.0 → 0.39.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/AGENTS.md +3 -3
- package/CHANGELOG.md +24 -0
- package/package.json +10 -1
- package/src/build.ts +14 -0
- package/src/lib/bundle/exitWithParent.ts +4 -2
- package/src/lib/server/rpc/readBodyWithinLimit.ts +3 -0
- package/src/lib/server/runtime/createAppAssetServer.ts +37 -11
- package/src/lib/server/runtime/createPublicAssetServer.ts +14 -5
- package/src/lib/server/runtime/createUiPageRenderer.ts +53 -42
- package/src/lib/server/runtime/internalErrorResponse.ts +5 -2
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +4 -1
- package/src/lib/server/sockets/createSocketDispatcher.ts +7 -0
- package/src/lib/shared/createRemoteFunction.ts +20 -11
- package/src/lib/shared/escapeHtml.ts +15 -0
- package/src/lib/shared/markFrameworkSourcesIgnored.ts +47 -0
- package/src/lib/shared/streamResponse.ts +8 -1
- package/src/lib/shared/types/RemoteCallable.ts +12 -3
- package/src/lib/shared/types/RpcOptions.ts +22 -0
- package/src/lib/shared/types/SourceMap.ts +14 -0
- package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +4 -3
- package/src/lib/ui/compile/SSR_ESCAPE.ts +13 -3
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +6 -0
- package/src/lib/ui/compile/asOutlet.ts +6 -5
- package/src/lib/ui/compile/compileModule.ts +18 -11
- package/src/lib/ui/compile/compileSSR.ts +32 -9
- package/src/lib/ui/compile/compileShadow.ts +11 -3
- package/src/lib/ui/compile/composeProps.ts +53 -0
- package/src/lib/ui/compile/desugarSignals.ts +45 -17
- package/src/lib/ui/compile/generateBuild.ts +100 -61
- package/src/lib/ui/compile/generateSSR.ts +226 -73
- package/src/lib/ui/compile/isAnchorPositioned.ts +19 -0
- package/src/lib/ui/compile/isControlFlow.ts +3 -1
- package/src/lib/ui/compile/isTextLeaf.ts +1 -1
- package/src/lib/ui/compile/lowerDocAccess.ts +53 -16
- package/src/lib/ui/compile/parseTemplate.ts +44 -1
- package/src/lib/ui/compile/skeletonContext.ts +19 -20
- package/src/lib/ui/compile/skeletonable.ts +3 -2
- package/src/lib/ui/compile/spreadExcludedNames.ts +27 -0
- package/src/lib/ui/compile/staticAttr.ts +1 -1
- package/src/lib/ui/compile/staticTextPart.ts +1 -1
- package/src/lib/ui/compile/types/TemplateAttr.ts +4 -1
- package/src/lib/ui/compile/types/TemplateNode.ts +3 -1
- package/src/lib/ui/dom/commentData.ts +14 -0
- package/src/lib/ui/dom/disposeRange.ts +21 -0
- package/src/lib/ui/dom/fillBoundary.ts +38 -0
- package/src/lib/ui/dom/fillRange.ts +30 -0
- package/src/lib/ui/dom/hydrate.ts +11 -21
- package/src/lib/ui/dom/mergeProps.ts +32 -0
- package/src/lib/ui/dom/mount.ts +16 -25
- package/src/lib/ui/dom/mountChild.ts +27 -14
- package/src/lib/ui/dom/mountRange.ts +45 -0
- package/src/lib/ui/dom/outlet.ts +62 -0
- package/src/lib/ui/dom/readCall.ts +27 -0
- package/src/lib/ui/dom/restProps.ts +32 -0
- package/src/lib/ui/dom/scopeLabel.ts +8 -6
- package/src/lib/ui/dom/skeleton.ts +11 -30
- package/src/lib/ui/dom/spreadAttrs.ts +34 -0
- package/src/lib/ui/dom/spreadProps.ts +32 -0
- package/src/lib/ui/dom/withScope.ts +33 -0
- package/src/lib/ui/installHotBridge.ts +12 -0
- package/src/lib/ui/remoteProxy.ts +68 -36
- package/src/lib/ui/renderChain.ts +49 -39
- package/src/lib/ui/renderToStream.ts +69 -61
- package/src/lib/ui/resumeSeedScript.ts +17 -0
- package/src/lib/ui/router.ts +155 -85
- package/src/lib/ui/runtime/OUTLET_MARKER.ts +10 -0
- package/src/lib/ui/runtime/OUTLET_TAG.ts +5 -6
- package/src/lib/ui/runtime/PENDING_OUTLET.ts +8 -0
- package/src/lib/ui/runtime/captureModelDoc.ts +12 -13
- package/src/lib/ui/runtime/createEffectNode.ts +5 -0
- package/src/lib/ui/runtime/hotReplace.ts +14 -10
- package/src/lib/ui/runtime/localStoragePersistence.ts +8 -1
- package/src/lib/ui/runtime/toTeardown.ts +10 -5
- package/src/lib/ui/runtime/types/HotInstance.ts +11 -7
- package/src/lib/ui/runtime/types/RenderContext.ts +8 -0
- package/src/lib/ui/runtime/types/Route.ts +6 -5
- package/src/lib/ui/runtime/types/SsrRender.ts +16 -11
- package/src/lib/ui/runtime/types/UiComponent.ts +12 -1
- package/src/lib/ui/compile/componentWrapperTag.ts +0 -15
- package/src/lib/ui/compile/escapeHtml.ts +0 -15
- package/src/lib/ui/runtime/firstOutlet.ts +0 -22
- /package/src/lib/{server/runtime → shared}/safeJsonForScript.ts +0 -0
|
@@ -2,12 +2,13 @@ import { HOLE_ATTRIBUTE } from '../runtime/HOLE_ATTRIBUTE.ts'
|
|
|
2
2
|
import { OUTLET_TAG } from '../runtime/OUTLET_TAG.ts'
|
|
3
3
|
import { asOutlet } from './asOutlet.ts'
|
|
4
4
|
import { bindListenEvent } from './bindListenEvent.ts'
|
|
5
|
-
import {
|
|
5
|
+
import { composeProps } from './composeProps.ts'
|
|
6
6
|
import { groupBindParts } from './groupBindParts.ts'
|
|
7
7
|
import { isControlFlow } from './isControlFlow.ts'
|
|
8
8
|
import { lowerContext } from './lowerContext.ts'
|
|
9
9
|
import { scopeAttr } from './scopeAttr.ts'
|
|
10
10
|
import { skeletonContext } from './skeletonContext.ts'
|
|
11
|
+
import { spreadExcludedNames } from './spreadExcludedNames.ts'
|
|
11
12
|
import { staticAttr } from './staticAttr.ts'
|
|
12
13
|
import { staticTextPart } from './staticTextPart.ts'
|
|
13
14
|
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
@@ -22,6 +23,25 @@ surface (`count` → `model.count`) and then lowered to the doc patch/read API
|
|
|
22
23
|
`hostVar` and expects the dom bindings, `doc`, `effect`, and the component's
|
|
23
24
|
`model` in scope — the body the component compiler wraps and hoists cells into.
|
|
24
25
|
*/
|
|
26
|
+
|
|
27
|
+
/* A JS-identifier-safe frame name from an authored construct label (an attribute name
|
|
28
|
+
like `aria-label`, a bound property). Non-identifier chars → `_`; a leading digit gets
|
|
29
|
+
an `_` prefix; empty falls back to `thunk`. Callers prefix the label (`attr_`/`bind_`),
|
|
30
|
+
so the result is never a bare reserved word. */
|
|
31
|
+
function thunkName(label: string): string {
|
|
32
|
+
const safe = label.replace(/[^A-Za-z0-9_$]/g, '_').replace(/^(?=\d)/, '_')
|
|
33
|
+
return safe === '' ? 'thunk' : safe
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Names a reactive thunk so a stack frame reads `name@File.abide:line` instead of
|
|
37
|
+
`(anonymous)` — disambiguating which binding a frame is when several share a line. Emits
|
|
38
|
+
a named function expression (the only form whose name a debugger displays); the named
|
|
39
|
+
bodies never reference `this`/`arguments`, so the arrow→function swap is behaviour-safe,
|
|
40
|
+
and minify strips the name, so it costs nothing in production. */
|
|
41
|
+
function namedThunk(name: string, body: string): string {
|
|
42
|
+
return `function ${thunkName(name)}() { ${body} }`
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
export function generateBuild(
|
|
26
46
|
nodes: TemplateNode[],
|
|
27
47
|
hostVar: string,
|
|
@@ -77,7 +97,7 @@ export function generateBuild(
|
|
|
77
97
|
varName: string,
|
|
78
98
|
): string {
|
|
79
99
|
if (attr.kind === 'expression') {
|
|
80
|
-
return `attr(${varName}, ${JSON.stringify(attr.name)}, (
|
|
100
|
+
return `attr(${varName}, ${JSON.stringify(attr.name)}, ${namedThunk(`attr_${attr.name}`, `return (${lowerExpression(attr.code)})`)});\n`
|
|
81
101
|
}
|
|
82
102
|
if (attr.kind === 'event') {
|
|
83
103
|
return `on(${varName}, ${JSON.stringify(attr.event)}, (${lowerExpression(attr.code)}));\n`
|
|
@@ -96,12 +116,12 @@ export function generateBuild(
|
|
|
96
116
|
const value = lowerExpression(valueCode)
|
|
97
117
|
if (isRadio) {
|
|
98
118
|
return (
|
|
99
|
-
`effect((
|
|
119
|
+
`effect(${namedThunk('bind_group', `${varName}.checked = (${lowerExpression(attr.code)}) === (${value});`)});\n` +
|
|
100
120
|
`on(${varName}, "change", () => { if (${varName}.checked) { ${lowerStatement(`${attr.code} = ${valueCode}`)} } });\n`
|
|
101
121
|
)
|
|
102
122
|
}
|
|
103
123
|
return (
|
|
104
|
-
`effect((
|
|
124
|
+
`effect(${namedThunk('bind_group', `${varName}.checked = (${lowerExpression(attr.code)}).includes(${value});`)});\n` +
|
|
105
125
|
`on(${varName}, "change", () => { const $groupValue = ${value}; if (${varName}.checked) { if (!(${lowerExpression(attr.code)}).includes($groupValue)) { ${lowerStatement(`${attr.code}.push($groupValue)`)} } } else { const $groupIndex = (${lowerExpression(attr.code)}).indexOf($groupValue); if ($groupIndex !== -1) { ${lowerStatement(`delete ${attr.code}[$groupIndex]`)} } } });\n`
|
|
106
126
|
)
|
|
107
127
|
}
|
|
@@ -112,7 +132,7 @@ export function generateBuild(
|
|
|
112
132
|
`.get()` and writes via `.set(v)` — see `bindRead`/`bindWrite`. */
|
|
113
133
|
const event = bindListenEvent(attr.property, node.tag)
|
|
114
134
|
return (
|
|
115
|
-
`effect((
|
|
135
|
+
`effect(${namedThunk(`bind_${attr.property}`, `${varName}.${attr.property} = ${bindRead(attr.code)};`)});\n` +
|
|
116
136
|
`on(${varName}, ${JSON.stringify(event)}, () => { ${bindWrite(attr.code, `${varName}.${attr.property}`)} });\n`
|
|
117
137
|
)
|
|
118
138
|
}
|
|
@@ -125,6 +145,16 @@ export function generateBuild(
|
|
|
125
145
|
block or slot drops an `<!--a-->` anchor at its position and mounts there (see
|
|
126
146
|
`anchorCursor`), so it can sit ANYWHERE among static siblings. Static descendants are
|
|
127
147
|
plain markup. */
|
|
148
|
+
/* The skeleton anchor var for an anchor-positioned node: declares `an<n> = sk.an[i]` as
|
|
149
|
+
a bind and returns the var name. The three anchored kinds (control-flow/component,
|
|
150
|
+
outlet, slot) all mount at this `<!--a-->` anchor, so they number through this ONE
|
|
151
|
+
site (`anIndex`) — no per-branch copy of the lookup to drift from the runtime scan. */
|
|
152
|
+
function anchorVarAt(node: TemplateNode, skVar: string, binds: string[]): string {
|
|
153
|
+
const anchorVar = nextVar('an')
|
|
154
|
+
binds.push(`const ${anchorVar} = ${skVar}.an[${holeIndex(anIndex, node)}];\n`)
|
|
155
|
+
return anchorVar
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
function skeletonMarkup(node: TemplateNode, skVar: string, binds: string[]): string {
|
|
129
159
|
if (node.kind === 'text') {
|
|
130
160
|
/* Reactive text reached here is INTERLEAVED with element siblings (a text-leaf
|
|
@@ -137,31 +167,23 @@ export function generateBuild(
|
|
|
137
167
|
return staticTextPart(part.value)
|
|
138
168
|
}
|
|
139
169
|
binds.push(
|
|
140
|
-
`appendTextAt(${skVar}.an[${holeIndex(anIndex, part)}], (
|
|
170
|
+
`appendTextAt(${skVar}.an[${holeIndex(anIndex, part)}], ${namedThunk('text', `return (${lowerExpression(part.code)})`)});\n`,
|
|
141
171
|
)
|
|
142
172
|
return '<!--a-->'
|
|
143
173
|
})
|
|
144
174
|
.join('')
|
|
145
175
|
}
|
|
146
|
-
if (isControlFlow(node)) {
|
|
147
|
-
/* A control-flow block at its position: an `<!--a-->` anchor
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
element the anchor was cloned into
|
|
151
|
-
|
|
152
|
-
|
|
176
|
+
if (isControlFlow(node) || node.kind === 'component') {
|
|
177
|
+
/* A control-flow block OR a child component at its position: an `<!--a-->` anchor
|
|
178
|
+
in the clone, its content mounted as a marker-bounded range at it. `anchorCursor`
|
|
179
|
+
parks the hydrate cursor past the anchor and returns the create insertion
|
|
180
|
+
reference; the parent is the located element the anchor was cloned into
|
|
181
|
+
(`anchor.parentNode`). A component takes an anchor like a block — no wrapper
|
|
182
|
+
element — so its root lays out as a true direct child of `anchor.parentNode`. */
|
|
183
|
+
const anchorVar = anchorVarAt(node, skVar, binds)
|
|
153
184
|
binds.push(generateChild(node, `${anchorVar}.parentNode`, `anchorCursor(${anchorVar})`))
|
|
154
185
|
return '<!--a-->'
|
|
155
186
|
}
|
|
156
|
-
if (node.kind === 'component') {
|
|
157
|
-
/* The wrapper element is a positioned hole in the skeleton; the child mounts
|
|
158
|
-
into the located node. display:contents (idempotent, static so it lives in
|
|
159
|
-
the markup) keeps the wrapper out of layout — a pure mount host. */
|
|
160
|
-
const tag = componentWrapperTag(node.name)
|
|
161
|
-
const { code } = mountComponent(node, `${skVar}.el[${holeIndex(elIndex, node)}]`)
|
|
162
|
-
binds.push(code)
|
|
163
|
-
return `<${tag} ${HOLE_ATTRIBUTE} style="display:contents"></${tag}>`
|
|
164
|
-
}
|
|
165
187
|
if (node.kind === 'script') {
|
|
166
188
|
/* A nested `<script>` (scoped reactive block) emits no markup — its lowered body
|
|
167
189
|
runs as a bind, in document order, so its signals are declared before the later
|
|
@@ -179,11 +201,18 @@ export function generateBuild(
|
|
|
179
201
|
if (node.kind !== 'element') {
|
|
180
202
|
return '' // <style> emits no markup
|
|
181
203
|
}
|
|
204
|
+
if (node.tag === OUTLET_TAG) {
|
|
205
|
+
/* A layout's router fill point at its position: an `<!--a-->` anchor, an empty
|
|
206
|
+
`outlet` boundary the router fills with the next chain layer (`fillBoundary`).
|
|
207
|
+
No wrapper element — the filled child lays out as a direct child of the parent. */
|
|
208
|
+
const anchorVar = anchorVarAt(node, skVar, binds)
|
|
209
|
+
binds.push(`outlet(${anchorVar}.parentNode, anchorCursor(${anchorVar}));\n`)
|
|
210
|
+
return '<!--a-->'
|
|
211
|
+
}
|
|
182
212
|
if (node.tag === 'slot') {
|
|
183
213
|
/* A `<slot>` outlet at its position: an `<!--a-->` anchor, the slot's content
|
|
184
214
|
mounted as a marker-bounded range (`mountSlot`) so it positions like a block. */
|
|
185
|
-
const anchorVar =
|
|
186
|
-
binds.push(`const ${anchorVar} = ${skVar}.an[${holeIndex(anIndex, node)}];\n`)
|
|
215
|
+
const anchorVar = anchorVarAt(node, skVar, binds)
|
|
187
216
|
const hostVar = nextVar('host')
|
|
188
217
|
binds.push(
|
|
189
218
|
`mountSlot(${anchorVar}.parentNode, (${hostVar}) => {\n${generateSlot(node, hostVar)}}, anchorCursor(${anchorVar}));\n`,
|
|
@@ -211,7 +240,14 @@ export function generateBuild(
|
|
|
211
240
|
binds.push(`const ${elVar} = ${skVar}.el[${holeIndex(elIndex, node)}];\n`)
|
|
212
241
|
openTag += ` ${HOLE_ATTRIBUTE}`
|
|
213
242
|
for (const attr of node.attrs) {
|
|
214
|
-
if (attr.kind
|
|
243
|
+
if (attr.kind === 'spread') {
|
|
244
|
+
/* `{...expr}` onto the element: each key binds as a reactive attribute
|
|
245
|
+
(or an `on<event>` function as a listener) via `spreadAttrs`, skipping
|
|
246
|
+
any key explicitly named on the element (the explicit attr wins). */
|
|
247
|
+
binds.push(
|
|
248
|
+
`spreadAttrs(${elVar}, ${namedThunk('spread', `return (${lowerExpression(attr.code)})`)}, ${JSON.stringify(spreadExcludedNames(node.attrs))});\n`,
|
|
249
|
+
)
|
|
250
|
+
} else if (attr.kind !== 'static') {
|
|
215
251
|
binds.push(dynamicAttr(node, attr, elVar))
|
|
216
252
|
}
|
|
217
253
|
}
|
|
@@ -246,7 +282,7 @@ export function generateBuild(
|
|
|
246
282
|
skeleton string (parsed once, cloned per mount) plus each hole's wiring against
|
|
247
283
|
its located node. */
|
|
248
284
|
function generateSkeleton(
|
|
249
|
-
node: Extract<TemplateNode, { kind: 'element'
|
|
285
|
+
node: Extract<TemplateNode, { kind: 'element' }>,
|
|
250
286
|
parentVar: string,
|
|
251
287
|
): string {
|
|
252
288
|
const skVar = nextVar('sk')
|
|
@@ -279,18 +315,23 @@ export function generateBuild(
|
|
|
279
315
|
const splitAlways = index < consumers.length - 1 ? ', true' : ''
|
|
280
316
|
return part.kind === 'static'
|
|
281
317
|
? `appendStatic(${parentVar}, ${JSON.stringify(part.value)}${splitAlways});\n`
|
|
282
|
-
: `appendText(${parentVar}, (
|
|
318
|
+
: `appendText(${parentVar}, ${namedThunk('text', `return (${lowerExpression(part.code)})`)}${splitAlways});\n`
|
|
283
319
|
})
|
|
284
320
|
.join('')
|
|
285
321
|
}
|
|
322
|
+
if (node.kind === 'element' && node.tag === OUTLET_TAG) {
|
|
323
|
+
/* A standalone layout outlet (a top-level/element-nested `<slot/>` rewritten by
|
|
324
|
+
`asOutlet`, reached outside any skeleton): an empty `outlet` boundary at
|
|
325
|
+
`before`, no anchor — the router fills it with the next chain layer. */
|
|
326
|
+
return `outlet(${parentVar}, ${before});\n`
|
|
327
|
+
}
|
|
286
328
|
if (node.kind === 'element' && node.tag === 'slot') {
|
|
287
|
-
/* In a layout, `<slot/>` is the router's page outlet
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
reached inside a control-flow branch.) */
|
|
329
|
+
/* In a layout, `<slot/>` is the router's page outlet (`outlet` boundary the
|
|
330
|
+
router fills with the next chain layer). Top-level/element-nested layout slots
|
|
331
|
+
are rewritten to `OUTLET_TAG` up front by `asOutlet` and handled above; this
|
|
332
|
+
covers a layout slot reached inside a control-flow branch. */
|
|
292
333
|
if (isLayout) {
|
|
293
|
-
return `
|
|
334
|
+
return `outlet(${parentVar}, ${before});\n`
|
|
294
335
|
}
|
|
295
336
|
return generateSlot(node, parentVar)
|
|
296
337
|
}
|
|
@@ -314,10 +355,10 @@ export function generateBuild(
|
|
|
314
355
|
return '' // branches are consumed by their await block, never standalone
|
|
315
356
|
}
|
|
316
357
|
if (node.kind === 'component') {
|
|
317
|
-
/* A standalone component
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return
|
|
358
|
+
/* A standalone component (top-level, or a bare child of a branch/row/slot) mounts
|
|
359
|
+
directly as a marker range on `parentVar` at `before` — no anchor, no wrapper,
|
|
360
|
+
same as a standalone control-flow block routes through `generateIf`/etc. */
|
|
361
|
+
return generateChildComponent(node, parentVar, before)
|
|
321
362
|
}
|
|
322
363
|
if (node.kind === 'switch') {
|
|
323
364
|
return generateSwitch(node, parentVar, before)
|
|
@@ -402,33 +443,31 @@ export function generateBuild(
|
|
|
402
443
|
return `if ($props && $props.$children) { ${invoke}; } else {\n${fallback}}\n`
|
|
403
444
|
}
|
|
404
445
|
|
|
405
|
-
/*
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
its slot content as a host-taking builder (`$children`). */
|
|
409
|
-
function componentParts(node: Extract<TemplateNode, { kind: 'component' }>): string[] {
|
|
410
|
-
const parts = node.props.map(
|
|
411
|
-
(prop) => `${JSON.stringify(prop.name)}: () => (${lowerExpression(prop.code)})`,
|
|
412
|
-
)
|
|
446
|
+
/* The child's slot content as a host-taking builder (`$children`), or undefined when
|
|
447
|
+
the component has no slotted children. */
|
|
448
|
+
function slotPart(node: Extract<TemplateNode, { kind: 'component' }>): string | undefined {
|
|
413
449
|
const slotCode = generateChildren(node.children, '$slot')
|
|
414
|
-
|
|
415
|
-
parts.push(`"$children": ($slot) => {\n${slotCode}}`)
|
|
416
|
-
}
|
|
417
|
-
return parts
|
|
450
|
+
return slotCode.trim() === '' ? undefined : `"$children": ($slot) => {\n${slotCode}}`
|
|
418
451
|
}
|
|
419
452
|
|
|
420
|
-
/*
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
453
|
+
/* The props bag a child mount receives — composed by the shared `composeProps` so the
|
|
454
|
+
build and SSR back-ends emit the same last-wins layering. */
|
|
455
|
+
function propsArg(node: Extract<TemplateNode, { kind: 'component' }>): string {
|
|
456
|
+
return composeProps(node.props, lowerExpression, slotPart(node))
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Mounts a child component as a marker-bounded range on `parentVar`, positioned at
|
|
460
|
+
`before` (a skeleton anchor's `anchorCursor`, or `null` for a standalone child).
|
|
461
|
+
`mountRange` opens the `[`/`]` markers and builds the child between them — no
|
|
462
|
+
wrapper element — so the child's root is a true direct child of `parentVar`.
|
|
463
|
+
Hydration stays ambient, so the child claims its server range in place. The
|
|
464
|
+
component name passes as the scope label (the inspector's `<Counter>` name). */
|
|
465
|
+
function generateChildComponent(
|
|
424
466
|
node: Extract<TemplateNode, { kind: 'component' }>,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
`const ${wrapper} = ${varExpr};\n` +
|
|
430
|
-
`mountChild(${wrapper}, ${node.name}, { ${componentParts(node).join(', ')} });\n`
|
|
431
|
-
return { code, varName: wrapper }
|
|
467
|
+
parentVar: string,
|
|
468
|
+
before: string,
|
|
469
|
+
): string {
|
|
470
|
+
return `mountChild(${parentVar}, ${node.name}, ${propsArg(node)}, ${before}, ${JSON.stringify(node.name)});\n`
|
|
432
471
|
}
|
|
433
472
|
|
|
434
473
|
/* An await block: pending → resolved(value) / error branches. Each branch is a
|
|
@@ -633,7 +672,7 @@ imperative. Static text and elements nested INSIDE a qualifying element are fine
|
|
|
633
672
|
enclosed by its tags.
|
|
634
673
|
*/
|
|
635
674
|
function isStaticCloneableElement(node: TemplateNode): boolean {
|
|
636
|
-
if (node.kind !== 'element' || node.tag === 'slot') {
|
|
675
|
+
if (node.kind !== 'element' || node.tag === 'slot' || node.tag === OUTLET_TAG) {
|
|
637
676
|
return false
|
|
638
677
|
}
|
|
639
678
|
if (node.attrs.some((attr) => attr.kind !== 'static')) {
|