@abide/abide 0.41.1 → 0.43.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 +234 -294
- package/CHANGELOG.md +82 -0
- package/README.md +51 -95
- package/package.json +3 -2
- package/src/abideLsp.ts +46 -8
- package/src/abideResolverPlugin.ts +107 -19
- package/src/buildCli.ts +1 -1
- package/src/discoveryEntry.ts +3 -3
- package/src/lib/bundle/BundleMenuItem.ts +1 -1
- package/src/lib/cli/createClient.ts +12 -12
- package/src/lib/cli/dispatchCommand.ts +1 -1
- package/src/lib/cli/printSessionStatus.ts +1 -1
- package/src/lib/cli/resolveCliTarget.ts +1 -1
- package/src/lib/cli/runCli.ts +1 -1
- package/src/lib/cli/types/CliManifest.ts +1 -1
- package/src/lib/cli/types/CliManifestEntry.ts +2 -2
- package/src/lib/mcp/annotationsForMethod.ts +5 -5
- package/src/lib/mcp/createMcpServer.ts +3 -3
- package/src/lib/mcp/mcpSurface.ts +14 -14
- package/src/lib/mcp/types/McpServerOptions.ts +1 -1
- package/src/lib/server/DELETE.ts +4 -4
- package/src/lib/server/GET.ts +4 -4
- package/src/lib/server/HEAD.ts +4 -4
- package/src/lib/server/PATCH.ts +4 -4
- package/src/lib/server/POST.ts +4 -4
- package/src/lib/server/PUT.ts +4 -4
- package/src/lib/server/agent.ts +4 -4
- package/src/lib/server/env.ts +1 -1
- package/src/lib/server/error.ts +49 -7
- package/src/lib/server/json.ts +1 -1
- package/src/lib/server/rpc/buildErrorConstructors.ts +19 -0
- package/src/lib/server/rpc/{defineVerb.ts → defineRpc.ts} +52 -34
- package/src/lib/server/rpc/{dispatchVerbInProcess.ts → dispatchRpcInProcess.ts} +3 -3
- package/src/lib/server/rpc/fieldErrorsFromIssues.ts +23 -0
- package/src/lib/server/rpc/{findVerbByCommandName.ts → findRpcByCommandName.ts} +5 -5
- package/src/lib/server/rpc/parseArgs.ts +6 -6
- package/src/lib/server/rpc/readBodyWithinLimit.ts +2 -2
- package/src/lib/server/rpc/registerRpc.ts +6 -0
- package/src/lib/server/rpc/{verbRegistry.ts → rpcRegistry.ts} +4 -4
- package/src/lib/server/rpc/{runWithVerbTimeout.ts → runWithRpcTimeout.ts} +4 -4
- package/src/lib/server/rpc/types/RemoteHandler.ts +9 -3
- package/src/lib/server/rpc/types/{VerbHelper.ts → RpcHelper.ts} +57 -23
- package/src/lib/server/rpc/types/{VerbRegistryEntry.ts → RpcRegistryEntry.ts} +3 -3
- package/src/lib/server/rpc/types/TypedResponse.ts +2 -2
- package/src/lib/server/rpc/unprocessed.ts +6 -7
- package/src/lib/server/rpc/validationError.ts +17 -0
- package/src/lib/server/runtime/buildInspectorSurface.ts +7 -7
- package/src/lib/server/runtime/buildOpenApiSpec.ts +6 -6
- package/src/lib/server/runtime/createRouteDispatcher.ts +7 -7
- package/src/lib/server/runtime/createServer.ts +4 -4
- package/src/lib/server/runtime/crossOriginForbidden.ts +1 -1
- package/src/lib/server/runtime/crossOriginGate.ts +4 -4
- package/src/lib/server/runtime/devClientFingerprint.ts +2 -1
- package/src/lib/server/runtime/logExposedSurfaces.ts +4 -4
- package/src/lib/server/runtime/registryManifests.ts +2 -2
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/InspectorRpc.ts +27 -0
- package/src/lib/server/runtime/types/InspectorSurface.ts +4 -4
- package/src/lib/server/runtime/types/RequestStore.ts +1 -1
- package/src/lib/server/runtime/warnUnguardedMcp.ts +2 -2
- package/src/lib/server/sockets/createSocketDispatcher.ts +15 -14
- package/src/lib/server/sockets/types/SocketOperation.ts +2 -2
- package/src/lib/shared/HttpError.ts +15 -1
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +13 -0
- package/src/lib/shared/buildRpcRequest.ts +5 -5
- package/src/lib/shared/cache.ts +50 -168
- package/src/lib/shared/carriesBodyArgs.ts +4 -4
- package/src/lib/shared/createCacheStore.ts +12 -0
- package/src/lib/shared/createRemoteFunction.ts +11 -6
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/detectRpcMethod.ts +17 -0
- package/src/lib/shared/emitLogRecord.ts +3 -3
- package/src/lib/shared/escapeRegex.ts +9 -0
- package/src/lib/shared/extraForwardHeaders.ts +1 -1
- package/src/lib/shared/fileName.ts +9 -0
- package/src/lib/shared/fileStem.ts +3 -1
- package/src/lib/shared/findExportCallSite.ts +30 -273
- package/src/lib/shared/forwardHeaders.ts +2 -2
- package/src/lib/shared/httpErrorFor.ts +26 -0
- package/src/lib/shared/isAsciiWhitespace.ts +5 -0
- package/src/lib/shared/isIdentPart.ts +9 -0
- package/src/lib/shared/isIdentStart.ts +8 -0
- package/src/lib/shared/isReadOnlyMethod.ts +4 -4
- package/src/lib/shared/keyForRemoteCall.ts +4 -4
- package/src/lib/shared/log.ts +1 -1
- package/src/lib/shared/outboxProbeSlot.ts +20 -0
- package/src/lib/shared/pending.ts +4 -1
- package/src/lib/shared/prepareRpcModule.ts +96 -14
- package/src/lib/shared/probeRegistries.ts +17 -4
- package/src/lib/shared/programNameForPackage.ts +3 -1
- package/src/lib/shared/queryStringFromArgs.ts +1 -1
- package/src/lib/shared/remoteMetaStore.ts +2 -2
- package/src/lib/shared/resolveClientFlags.ts +1 -1
- package/src/lib/shared/skipNonCode.ts +230 -0
- package/src/lib/shared/streamResponse.ts +9 -6
- package/src/lib/shared/stripImport.ts +6 -4
- package/src/lib/shared/types/CacheEntry.ts +2 -4
- package/src/lib/shared/types/CacheOnContext.ts +1 -11
- package/src/lib/shared/types/CacheStore.ts +13 -7
- package/src/lib/shared/types/ClientFlags.ts +1 -1
- package/src/lib/shared/types/ErrorConstructors.ts +17 -0
- package/src/lib/shared/types/ErrorDescriptor.ts +10 -0
- package/src/lib/shared/types/ErrorSpec.ts +11 -0
- package/src/lib/shared/types/HttpMethod.ts +1 -0
- package/src/lib/shared/types/Outbox.ts +9 -0
- package/src/lib/shared/types/OutboxEntry.ts +27 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +2 -2
- package/src/lib/shared/types/RemoteCallable.ts +2 -2
- package/src/lib/shared/types/RemoteFunction.ts +21 -6
- package/src/lib/shared/types/RequestScopeInfo.ts +1 -1
- package/src/lib/shared/types/RpcErrorGuard.ts +40 -0
- package/src/lib/shared/types/RpcInvoker.ts +1 -1
- package/src/lib/shared/types/StandardSchemaV1.ts +1 -1
- package/src/lib/shared/types/ValidationErrorData.ts +20 -0
- package/src/lib/shared/url.ts +3 -3
- package/src/lib/shared/writeRpcDts.ts +7 -7
- package/src/lib/shared/writeTestRpcDts.ts +3 -3
- package/src/lib/test/createTestApp.ts +10 -10
- package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +45 -0
- package/src/lib/ui/compile/abideUiPlugin.ts +2 -1
- package/src/lib/ui/compile/awaitPlan.ts +48 -0
- package/src/lib/ui/compile/compileShadow.ts +117 -31
- package/src/lib/ui/compile/createShadowLanguageService.ts +48 -0
- package/src/lib/ui/compile/desugarSignals.ts +42 -6
- package/src/lib/ui/compile/encodeSemanticTokens.ts +37 -0
- package/src/lib/ui/compile/generateBuild.ts +45 -62
- package/src/lib/ui/compile/generateSSR.ts +90 -60
- package/src/lib/ui/compile/ifPlan.ts +30 -0
- package/src/lib/ui/compile/makeVarNamer.ts +10 -0
- package/src/lib/ui/compile/offsetToPosition.ts +9 -0
- package/src/lib/ui/compile/parseTemplate.ts +565 -115
- package/src/lib/ui/compile/stripEffects.ts +17 -9
- package/src/lib/ui/compile/structuralBlockTokens.ts +101 -0
- package/src/lib/ui/compile/switchPlan.ts +22 -0
- package/src/lib/ui/compile/tryPlan.ts +29 -0
- package/src/lib/ui/compile/types/SemanticToken.ts +11 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +8 -2
- package/src/lib/ui/compile/types/TemplateNode.ts +15 -2
- package/src/lib/ui/createScope.ts +20 -2
- package/src/lib/ui/dom/appendText.ts +1 -5
- package/src/lib/ui/dom/applyResolved.ts +1 -5
- package/src/lib/ui/dom/disposeRange.ts +6 -10
- package/src/lib/ui/dom/hydrate.ts +2 -5
- package/src/lib/ui/dom/isComment.ts +6 -0
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/withScope.ts +6 -9
- package/src/lib/ui/effect.ts +4 -2
- package/src/lib/ui/navigate.ts +40 -13
- package/src/lib/ui/outbox.ts +30 -110
- package/src/lib/ui/remoteProxy.ts +160 -10
- package/src/lib/ui/router.ts +3 -3
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +281 -0
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +69 -0
- package/src/lib/ui/runtime/createDoc.ts +3 -3
- package/src/lib/ui/runtime/pathSegments.ts +10 -0
- package/src/lib/ui/socketChannel.ts +7 -1
- package/src/lib/ui/types/Scope.ts +20 -5
- package/template/.zed/settings.json +4 -0
- package/template/CLAUDE.md +1 -1
- package/template/src/server/rpc/getHello.ts +3 -3
- package/template/src/ui/pages/page.abide +4 -4
- package/template/test/app.test.ts +2 -2
- package/src/lib/server/rpc/registerVerb.ts +0 -6
- package/src/lib/server/runtime/types/InspectorVerb.ts +0 -27
- package/src/lib/shared/detectVerbMethod.ts +0 -17
- package/src/lib/shared/types/HttpVerb.ts +0 -1
- package/src/lib/ui/types/Outbox.ts +0 -14
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertExhaustive } from '../../shared/assertExhaustive.ts'
|
|
2
|
+
import { escapeRegex } from '../../shared/escapeRegex.ts'
|
|
2
3
|
import { OUTLET_CLOSE, OUTLET_OPEN } from '../runtime/OUTLET_MARKER.ts'
|
|
3
4
|
import { OUTLET_TAG } from '../runtime/OUTLET_TAG.ts'
|
|
4
5
|
import {
|
|
@@ -7,17 +8,22 @@ import {
|
|
|
7
8
|
RANGE_OPEN as RANGE_OPEN_DATA,
|
|
8
9
|
} from '../runtime/RANGE_MARKER.ts'
|
|
9
10
|
import { asOutlet } from './asOutlet.ts'
|
|
11
|
+
import { awaitPlan } from './awaitPlan.ts'
|
|
10
12
|
import { composeProps } from './composeProps.ts'
|
|
11
13
|
import { groupBindParts } from './groupBindParts.ts'
|
|
14
|
+
import { ifPlan } from './ifPlan.ts'
|
|
12
15
|
import { isAnchorPositioned } from './isAnchorPositioned.ts'
|
|
13
16
|
import { lowerContext } from './lowerContext.ts'
|
|
14
|
-
import {
|
|
17
|
+
import { makeVarNamer } from './makeVarNamer.ts'
|
|
15
18
|
import { scopeAttr } from './scopeAttr.ts'
|
|
16
19
|
import { skeletonContext } from './skeletonContext.ts'
|
|
17
20
|
import { spreadExcludedNames } from './spreadExcludedNames.ts'
|
|
18
21
|
import { staticAttr } from './staticAttr.ts'
|
|
19
22
|
import { staticTextPart } from './staticTextPart.ts'
|
|
20
23
|
import { stripEffects } from './stripEffects.ts'
|
|
24
|
+
import { switchPlan } from './switchPlan.ts'
|
|
25
|
+
import { tryPlan } from './tryPlan.ts'
|
|
26
|
+
import type { TemplateAttr } from './types/TemplateAttr.ts'
|
|
21
27
|
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
22
28
|
import { VOID_TAGS } from './VOID_TAGS.ts'
|
|
23
29
|
|
|
@@ -55,10 +61,9 @@ export function generateSSR(
|
|
|
55
61
|
computedNames: ReadonlySet<string>,
|
|
56
62
|
isLayout = false,
|
|
57
63
|
): string {
|
|
58
|
-
/*
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const nextVar = (prefix: string): string => `${prefix}${varCounter++}`
|
|
64
|
+
/* Unique temp var names (child render results); runtime block ids are
|
|
65
|
+
allocated separately at runtime via `$ctx.next++`. */
|
|
66
|
+
const nextVar = makeVarNamer()
|
|
62
67
|
|
|
63
68
|
/* The shared signal→`model` lowering + branch-scoped nested-script deref scope. */
|
|
64
69
|
const {
|
|
@@ -90,13 +95,12 @@ export function generateSSR(
|
|
|
90
95
|
regex metacharacters escaped, or e.g. a trailing `$` would read as an end-anchor and the
|
|
91
96
|
call site would never match — leaving an un-awaited Promise stringified as `[object
|
|
92
97
|
Promise]`. */
|
|
93
|
-
const escapeRegExp = (text: string): string => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
94
98
|
/* A leading boundary that, unlike `\b`, also fires before a `$`-leading name: `\b$row`
|
|
95
99
|
never matches (`$` is a non-word char, so there is no word boundary before it), which
|
|
96
100
|
would silently miss every `$row(...)` call. A negative lookbehind for word-or-`$`
|
|
97
101
|
matches the same call sites as `\b` for word-leading names while also catching them. */
|
|
98
102
|
const callPattern = (name: string): RegExp =>
|
|
99
|
-
new RegExp(`(?<![$\\w])${
|
|
103
|
+
new RegExp(`(?<![$\\w])${escapeRegex(name)}\\s*\\(`)
|
|
100
104
|
/* A subtree call to any of `names` from a TEXT interpolation (`name()` / `name(args)`). */
|
|
101
105
|
const subtreeCalls = (children: TemplateNode[], names: ReadonlySet<string>): boolean =>
|
|
102
106
|
children.some((child) => {
|
|
@@ -215,12 +219,9 @@ export function generateSSR(
|
|
|
215
219
|
/* `case` children are the `elseif`/`else` branches in source order; the rest are
|
|
216
220
|
the `then` content. Each `elseif` becomes an `else if`, the match-less `else`
|
|
217
221
|
the trailing `else`. */
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
)
|
|
221
|
-
const thenChildren = node.children.filter((child) => child.kind !== 'case')
|
|
222
|
-
let code = `if (${lowerExpression(node.condition)}) {\n${branchContent(thenChildren, target)}}`
|
|
223
|
-
for (const branch of branches) {
|
|
222
|
+
const plan = ifPlan(node)
|
|
223
|
+
let code = `if (${lowerExpression(node.condition)}) {\n${branchContent(plan.thenChildren, target)}}`
|
|
224
|
+
for (const branch of plan.branches) {
|
|
224
225
|
code +=
|
|
225
226
|
branch.condition !== undefined
|
|
226
227
|
? ` else if (${lowerExpression(branch.condition)}) {\n${branchContent(branch.children, target)}}`
|
|
@@ -229,20 +230,17 @@ export function generateSSR(
|
|
|
229
230
|
return `${anchor}${openRange(target)}${code}\n${closeRange(target)}`
|
|
230
231
|
}
|
|
231
232
|
if (node.kind === 'switch') {
|
|
232
|
-
const
|
|
233
|
-
(child): child is Extract<TemplateNode, { kind: 'case' }> => child.kind === 'case',
|
|
234
|
-
)
|
|
233
|
+
const plan = switchPlan(node)
|
|
235
234
|
let code = `{ const $s = (${lowerExpression(node.subject)});\n`
|
|
236
235
|
let started = false
|
|
237
|
-
for (const branch of cases) {
|
|
236
|
+
for (const branch of plan.cases) {
|
|
238
237
|
if (branch.match !== undefined) {
|
|
239
238
|
code += `${started ? 'else ' : ''}if ($s === (${lowerExpression(branch.match)})) {\n${branchContent(branch.children, target)}}\n`
|
|
240
239
|
started = true
|
|
241
240
|
}
|
|
242
241
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
code += `${started ? 'else ' : ''}{\n${branchContent(fallback.children, target)}}\n`
|
|
242
|
+
if (plan.fallback !== undefined) {
|
|
243
|
+
code += `${started ? 'else ' : ''}{\n${branchContent(plan.fallback.children, target)}}\n`
|
|
246
244
|
}
|
|
247
245
|
return `${anchor}${openRange(target)}${code}}\n${closeRange(target)}`
|
|
248
246
|
}
|
|
@@ -371,8 +369,25 @@ export function generateSSR(
|
|
|
371
369
|
for (const scope of node.scopes ?? []) {
|
|
372
370
|
code += push(target, scopeAttr(scope))
|
|
373
371
|
}
|
|
372
|
+
/* `class:`/`style:` directives collapse with any static `class`/`style` into a SINGLE
|
|
373
|
+
merged attribute (a duplicate attribute would be invalid and ignored). The client's
|
|
374
|
+
classList.toggle / style.setProperty effects re-apply the same values on hydrate, so
|
|
375
|
+
the merged SSR value already matches the post-mount DOM — no flash, no desync. */
|
|
376
|
+
const classDirectives = node.attrs.filter(
|
|
377
|
+
(attr): attr is Extract<TemplateAttr, { kind: 'class' }> => attr.kind === 'class',
|
|
378
|
+
)
|
|
379
|
+
const styleDirectives = node.attrs.filter(
|
|
380
|
+
(attr): attr is Extract<TemplateAttr, { kind: 'style' }> => attr.kind === 'style',
|
|
381
|
+
)
|
|
374
382
|
for (const attr of node.attrs) {
|
|
375
383
|
if (attr.kind === 'static') {
|
|
384
|
+
/* A static class/style is folded into the merge below when directives exist. */
|
|
385
|
+
if (
|
|
386
|
+
(attr.name === 'class' && classDirectives.length > 0) ||
|
|
387
|
+
(attr.name === 'style' && styleDirectives.length > 0)
|
|
388
|
+
) {
|
|
389
|
+
continue
|
|
390
|
+
}
|
|
376
391
|
code += push(target, staticAttr(attr.name, attr.value))
|
|
377
392
|
} else if (attr.kind === 'expression') {
|
|
378
393
|
/* present/absent semantics matching the client `attr` binding:
|
|
@@ -400,6 +415,36 @@ export function generateSSR(
|
|
|
400
415
|
code += `${target}.push(${JSON.stringify(` ${attr.property}="`)} + $esc(${bindRead(attr.code)}) + '"');\n`
|
|
401
416
|
}
|
|
402
417
|
}
|
|
418
|
+
/* Merged class: static value + each directive's name when its expression is truthy. */
|
|
419
|
+
if (classDirectives.length > 0) {
|
|
420
|
+
const staticClass = node.attrs.find(
|
|
421
|
+
(attr): attr is Extract<TemplateAttr, { kind: 'static' }> =>
|
|
422
|
+
attr.kind === 'static' && attr.name === 'class',
|
|
423
|
+
)
|
|
424
|
+
const parts = [
|
|
425
|
+
...(staticClass ? [JSON.stringify(staticClass.value)] : []),
|
|
426
|
+
...classDirectives.map(
|
|
427
|
+
(dir) => `((${lowerExpression(dir.code)}) ? ${JSON.stringify(dir.name)} : "")`,
|
|
428
|
+
),
|
|
429
|
+
]
|
|
430
|
+
code += `${target}.push(' class="' + $esc([${parts.join(', ')}].filter(Boolean).join(' ')) + '"');\n`
|
|
431
|
+
}
|
|
432
|
+
/* Merged style: static value + each directive's `prop:value` (String()-coerced, matching
|
|
433
|
+
the client's style.setProperty). */
|
|
434
|
+
if (styleDirectives.length > 0) {
|
|
435
|
+
const staticStyle = node.attrs.find(
|
|
436
|
+
(attr): attr is Extract<TemplateAttr, { kind: 'static' }> =>
|
|
437
|
+
attr.kind === 'static' && attr.name === 'style',
|
|
438
|
+
)
|
|
439
|
+
const parts = [
|
|
440
|
+
...(staticStyle ? [JSON.stringify(staticStyle.value)] : []),
|
|
441
|
+
...styleDirectives.map(
|
|
442
|
+
(dir) =>
|
|
443
|
+
`(${JSON.stringify(`${dir.property}:`)} + String(${lowerExpression(dir.code)}))`,
|
|
444
|
+
),
|
|
445
|
+
]
|
|
446
|
+
code += `${target}.push(' style="' + $esc([${parts.join(', ')}].filter(Boolean).join(';')) + '"');\n`
|
|
447
|
+
}
|
|
403
448
|
code += push(target, '>')
|
|
404
449
|
if (!VOID_TAGS.has(node.tag)) {
|
|
405
450
|
/* Each child's skeleton position (whether its reactive text interleaves into an
|
|
@@ -464,27 +509,23 @@ export function generateSSR(
|
|
|
464
509
|
node: Extract<TemplateNode, { kind: 'await' }>,
|
|
465
510
|
target: string,
|
|
466
511
|
): string {
|
|
467
|
-
const
|
|
468
|
-
const finallyChildren = finallyBranch?.children ?? []
|
|
469
|
-
const resolvedChildren = node.children.filter((child) => child.kind !== 'branch')
|
|
470
|
-
const resolvedAs = node.as ?? '_value'
|
|
471
|
-
const errorAs = catchBranch?.as ?? '_error'
|
|
512
|
+
const plan = awaitPlan(node)
|
|
472
513
|
const id = nextVar('$aid')
|
|
473
514
|
let code = `const ${id} = $ctx.next++;\n`
|
|
474
515
|
code += `${target}.push("<!--abide:await:" + ${id} + "-->");\n`
|
|
475
516
|
code += `try {\n`
|
|
476
|
-
code += `const ${resolvedAs} = await (${lowerExpression(node.promise)});\n`
|
|
477
|
-
code += branchContent(resolvedChildren, target)
|
|
478
|
-
code += branchContent(finallyChildren, target)
|
|
479
|
-
code += `$resume[${id}] = { ok: true, value: ${resolvedAs} };\n`
|
|
480
|
-
if (
|
|
517
|
+
code += `const ${plan.resolvedAs} = await (${lowerExpression(node.promise)});\n`
|
|
518
|
+
code += branchContent(plan.resolvedChildren, target)
|
|
519
|
+
code += branchContent(plan.finallyChildren, target)
|
|
520
|
+
code += `$resume[${id}] = { ok: true, value: ${plan.resolvedAs} };\n`
|
|
521
|
+
if (plan.surfaceRejection) {
|
|
481
522
|
/* No catch/finally → let the rejection surface instead of an empty branch. */
|
|
482
523
|
code += `} catch (_error) { throw _error; }\n`
|
|
483
524
|
} else {
|
|
484
|
-
code += `} catch (${
|
|
485
|
-
code += branchContent(
|
|
486
|
-
code += branchContent(finallyChildren, target)
|
|
487
|
-
code += `$resume[${id}] = { ok: false, error: String(${
|
|
525
|
+
code += `} catch (${plan.catchAs}) {\n`
|
|
526
|
+
code += branchContent(plan.catchChildren, target)
|
|
527
|
+
code += branchContent(plan.finallyChildren, target)
|
|
528
|
+
code += `$resume[${id}] = { ok: false, error: String(${plan.catchAs}) };\n`
|
|
488
529
|
code += `}\n`
|
|
489
530
|
}
|
|
490
531
|
code += `${target}.push("<!--/abide:await:" + ${id} + "-->");\n`
|
|
@@ -502,29 +543,21 @@ export function generateSSR(
|
|
|
502
543
|
node: Extract<TemplateNode, { kind: 'await' }>,
|
|
503
544
|
target: string,
|
|
504
545
|
): string {
|
|
505
|
-
const
|
|
506
|
-
node,
|
|
507
|
-
'then',
|
|
508
|
-
'catch',
|
|
509
|
-
'finally',
|
|
510
|
-
)
|
|
511
|
-
const finallyChildren = finallyBranch?.children ?? []
|
|
512
|
-
const pending = node.children.filter((child) => child.kind !== 'branch')
|
|
546
|
+
const plan = awaitPlan(node)
|
|
513
547
|
const id = nextVar('$aid')
|
|
514
548
|
let code = `const ${id} = $ctx.next++;\n`
|
|
515
549
|
code += `${target}.push("<!--abide:await:" + ${id} + "-->");\n`
|
|
516
|
-
code += branchContent(pending, target)
|
|
550
|
+
code += branchContent(plan.pending, target)
|
|
517
551
|
code += `${target}.push("<!--/abide:await:" + ${id} + "-->");\n`
|
|
518
552
|
const settled = (binding: string, children: TemplateNode[]) =>
|
|
519
|
-
`async (${binding}) => { const $o = []; ${branchContent(children, '$o')}${branchContent(finallyChildren, '$o')}return $o.join(''); }`
|
|
520
|
-
const catchProp =
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
: `catch: ${settled(catchBranch?.as ?? '_error', catchBranch?.children ?? [])} `
|
|
553
|
+
`async (${binding}) => { const $o = []; ${branchContent(children, '$o')}${branchContent(plan.finallyChildren, '$o')}return $o.join(''); }`
|
|
554
|
+
const catchProp = plan.surfaceRejection
|
|
555
|
+
? ''
|
|
556
|
+
: `catch: ${settled(plan.catchAs, plan.catchChildren)} `
|
|
524
557
|
code +=
|
|
525
558
|
`$awaits.push({ id: ${id}, ` +
|
|
526
559
|
`promise: () => (${lowerExpression(node.promise)}), ` +
|
|
527
|
-
`then: ${settled(
|
|
560
|
+
`then: ${settled(plan.resolvedAs, plan.resolvedChildren)}, ` +
|
|
528
561
|
`${catchProp}});\n`
|
|
529
562
|
return code
|
|
530
563
|
}
|
|
@@ -536,24 +569,21 @@ export function generateSSR(
|
|
|
536
569
|
an enclosing boundary / the 500 / the stream). Boundary comments let hydration
|
|
537
570
|
discard the server content if the client adoption fails. */
|
|
538
571
|
function generateTry(node: Extract<TemplateNode, { kind: 'try' }>, target: string): string {
|
|
539
|
-
const
|
|
540
|
-
const finallyChildren = finallyBranch?.children ?? []
|
|
541
|
-
const guarded = node.children.filter((child) => child.kind !== 'branch')
|
|
542
|
-
const errName = catchBranch?.as ?? '_error'
|
|
572
|
+
const plan = tryPlan(node)
|
|
543
573
|
const id = nextVar('$tid')
|
|
544
574
|
const mark = nextVar('$trim')
|
|
545
575
|
let code = `const ${id} = $ctx.next++;\n`
|
|
546
576
|
code += `${target}.push("<!--abide:try:" + ${id} + "-->");\n`
|
|
547
577
|
code += `const ${mark} = ${target}.length;\n`
|
|
548
578
|
code += `try {\n`
|
|
549
|
-
code += branchContent(guarded, target)
|
|
550
|
-
code += branchContent(finallyChildren, target)
|
|
551
|
-
code += `} catch (${
|
|
552
|
-
if (
|
|
553
|
-
code += branchContent(
|
|
554
|
-
code += branchContent(finallyChildren, target)
|
|
579
|
+
code += branchContent(plan.guarded, target)
|
|
580
|
+
code += branchContent(plan.finallyChildren, target)
|
|
581
|
+
code += `} catch (${plan.catchAs}) {\n${target}.length = ${mark};\n`
|
|
582
|
+
if (plan.hasCatch) {
|
|
583
|
+
code += branchContent(plan.catchChildren, target)
|
|
584
|
+
code += branchContent(plan.finallyChildren, target)
|
|
555
585
|
} else {
|
|
556
|
-
code += `throw ${
|
|
586
|
+
code += `throw ${plan.catchAs};\n`
|
|
557
587
|
}
|
|
558
588
|
code += `}\n`
|
|
559
589
|
code += `${target}.push("<!--/abide:try:" + ${id} + "-->");\n`
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
2
|
+
|
|
3
|
+
type Case = Extract<TemplateNode, { kind: 'case' }>
|
|
4
|
+
|
|
5
|
+
/* The structural shape of an `if` chain, resolved once so the build and SSR back-ends share
|
|
6
|
+
one partition of its children and only own emission. Both previously recomputed the
|
|
7
|
+
then-content / branch split and the elseif/else classification. */
|
|
8
|
+
export type IfPlan = {
|
|
9
|
+
/* The `then` content — every child that is not an `elseif`/`else` branch. */
|
|
10
|
+
thenChildren: TemplateNode[]
|
|
11
|
+
/* The `elseif`/`else` branches in source order (each a `case` node: `condition` set for
|
|
12
|
+
`elseif`, both unset for `else`). */
|
|
13
|
+
branches: Case[]
|
|
14
|
+
/* Any branch carries a condition → a cond-chain (the build lowers it through `switchBlock`);
|
|
15
|
+
otherwise a binary `if`/`else`. */
|
|
16
|
+
hasElseif: boolean
|
|
17
|
+
/* The match-less `else` branch, if present. */
|
|
18
|
+
elseBranch: Case | undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Partitions an `if` node's children into the shared structural plan. */
|
|
22
|
+
export function ifPlan(node: Extract<TemplateNode, { kind: 'if' }>): IfPlan {
|
|
23
|
+
const branches = node.children.filter((child): child is Case => child.kind === 'case')
|
|
24
|
+
return {
|
|
25
|
+
thenChildren: node.children.filter((child) => child.kind !== 'case'),
|
|
26
|
+
branches,
|
|
27
|
+
hasElseif: branches.some((branch) => branch.condition !== undefined),
|
|
28
|
+
elseBranch: branches.find((branch) => branch.condition === undefined),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Compile-time generator of unique temp var names: each call appends a fresh
|
|
3
|
+
incrementing suffix to `prefix` (`row` → `row0`, `row1`, …). Shared by the
|
|
4
|
+
generateSSR / generateBuild codegen passes so the two hand-mirrored traversals
|
|
5
|
+
allocate names one way. Runtime block ids stay separate (`$ctx.next++`).
|
|
6
|
+
*/
|
|
7
|
+
export function makeVarNamer(): (prefix: string) => string {
|
|
8
|
+
let counter = 0
|
|
9
|
+
return (prefix: string): string => `${prefix}${counter++}`
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* An absolute offset → LSP `{ line, character }` (0-based, UTF-16 code units). */
|
|
2
|
+
export function offsetToPosition(
|
|
3
|
+
text: string,
|
|
4
|
+
offset: number,
|
|
5
|
+
): { line: number; character: number } {
|
|
6
|
+
const before = text.slice(0, offset)
|
|
7
|
+
const line = before.split('\n').length - 1
|
|
8
|
+
return { line, character: offset - (before.lastIndexOf('\n') + 1) }
|
|
9
|
+
}
|