@abide/abide 0.43.0 → 0.44.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 +293 -219
- package/CHANGELOG.md +46 -0
- package/README.md +191 -67
- package/package.json +2 -2
- package/src/abideLsp.ts +8 -4
- package/src/abideResolverPlugin.ts +11 -1
- package/src/build.ts +9 -0
- package/src/buildDisconnected.ts +2 -0
- package/src/devEntry.ts +1 -0
- package/src/lib/bundle/probeAbideServer.ts +1 -1
- package/src/lib/cli/parseArgvForRpc.ts +4 -2
- package/src/lib/cli/types/CliManifest.ts +6 -4
- package/src/lib/mcp/annotationsForMethod.ts +1 -1
- package/src/lib/mcp/toolResultFromResponse.ts +1 -1
- package/src/lib/mcp/types/JsonRpcRequest.ts +3 -3
- package/src/lib/server/AppModule.ts +1 -1
- package/src/lib/server/cli/handleCliDownload.ts +7 -5
- package/src/lib/server/cli/installScript.ts +3 -3
- package/src/lib/server/prompts/promptRegistry.ts +1 -1
- package/src/lib/server/prompts/types/Prompt.ts +2 -1
- package/src/lib/server/rpc/dispatchRpcInProcess.ts +2 -2
- package/src/lib/server/rpc/types/RpcHelper.ts +1 -1
- package/src/lib/server/runtime/createPublicAssetServer.ts +2 -2
- package/src/lib/server/runtime/createRouteDispatcher.ts +1 -1
- package/src/lib/server/runtime/createServer.ts +21 -0
- package/src/lib/server/runtime/createUiPageRenderer.ts +7 -11
- package/src/lib/server/runtime/crossOriginForbidden.ts +2 -2
- package/src/lib/server/runtime/crossOriginGate.ts +1 -1
- package/src/lib/server/runtime/devHotModuleResponse.ts +2 -2
- package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +1 -1
- package/src/lib/server/runtime/logExposedSurfaces.ts +3 -4
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +3 -8
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +10 -7
- package/src/lib/server/runtime/streamFromIterator.ts +2 -2
- package/src/lib/server/runtime/types/InspectorRpc.ts +2 -2
- package/src/lib/server/socket.ts +1 -1
- package/src/lib/server/sockets/createSocketDispatcher.ts +2 -2
- package/src/lib/server/sockets/socketRegistry.ts +2 -2
- package/src/lib/server/sockets/types/SocketClientFrame.ts +3 -3
- package/src/lib/shared/CACHE_WRAPPED.ts +3 -2
- package/src/lib/shared/REMOTE_FUNCTION.ts +1 -1
- package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +3 -2
- package/src/lib/shared/bodyValueForKind.ts +35 -0
- package/src/lib/shared/cache.ts +1 -1
- package/src/lib/shared/cacheEntryFromSnapshot.ts +23 -18
- package/src/lib/shared/createPushIterator.ts +1 -1
- package/src/lib/shared/decodeResponse.ts +11 -8
- package/src/lib/shared/hasReplayableRequest.ts +17 -0
- package/src/lib/shared/html.ts +7 -26
- package/src/lib/shared/manifestModule.ts +2 -2
- package/src/lib/shared/online.ts +1 -1
- package/src/lib/shared/outboxProbeSlot.ts +1 -1
- package/src/lib/shared/pageUrlForFile.ts +1 -1
- package/src/lib/shared/routeParamsShape.ts +20 -0
- package/src/lib/shared/snapshotShippable.ts +22 -0
- package/src/lib/shared/types/CacheEntry.ts +1 -1
- package/src/lib/shared/types/CacheInvalidation.ts +3 -3
- package/src/lib/shared/types/ClientFlags.ts +5 -3
- package/src/lib/shared/types/LastConnection.ts +1 -1
- package/src/lib/shared/url.ts +4 -1
- package/src/lib/shared/withBase.ts +1 -2
- package/src/lib/shared/writeDts.ts +2 -2
- package/src/lib/shared/writeRoutesDts.ts +3 -31
- package/src/lib/test/createTestSocketChannel.ts +3 -3
- package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +1 -1
- package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +40 -6
- package/src/lib/ui/compile/REACTIVE_CALLEES.ts +1 -1
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +49 -37
- package/src/lib/ui/compile/abideUiPlugin.ts +1 -1
- package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +3 -1
- package/src/lib/ui/compile/awaitPlan.ts +14 -2
- package/src/lib/ui/compile/catchBinding.ts +8 -0
- package/src/lib/ui/compile/classStyleMergePlan.ts +108 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +11 -0
- package/src/lib/ui/compile/compileComponent.ts +4 -4
- package/src/lib/ui/compile/compileModule.ts +20 -6
- package/src/lib/ui/compile/compileSSR.ts +12 -10
- package/src/lib/ui/compile/compileShadow.ts +107 -16
- package/src/lib/ui/compile/composeProps.ts +3 -3
- package/src/lib/ui/compile/createShadowLanguageService.ts +30 -2
- package/src/lib/ui/compile/createShadowProgram.ts +2 -1
- package/src/lib/ui/compile/createShadowScope.ts +42 -0
- package/src/lib/ui/compile/decodeHtmlEntities.ts +4 -0
- package/src/lib/ui/compile/desugarSignals.ts +12 -7
- package/src/lib/ui/compile/eachPlan.ts +61 -0
- package/src/lib/ui/compile/elementPlan.ts +55 -0
- package/src/lib/ui/compile/encodeSemanticTokens.ts +24 -1
- package/src/lib/ui/compile/generateBuild.ts +150 -127
- package/src/lib/ui/compile/generateSSR.ts +112 -73
- package/src/lib/ui/compile/ifPlan.ts +7 -1
- package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +25 -0
- package/src/lib/ui/compile/isPlainIdentifier.ts +7 -0
- package/src/lib/ui/compile/isTextLeaf.ts +8 -7
- package/src/lib/ui/compile/lowerContext.ts +36 -46
- package/src/lib/ui/compile/lowerDocAccess.ts +2 -2
- package/src/lib/ui/compile/lowerScript.ts +1 -1
- package/src/lib/ui/compile/markupTokens.ts +313 -0
- package/src/lib/ui/compile/pagePropsType.ts +23 -0
- package/src/lib/ui/compile/parseTemplate.ts +131 -61
- package/src/lib/ui/compile/reactiveBinding.ts +47 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +44 -10
- package/src/lib/ui/compile/skeletonable.ts +5 -5
- package/src/lib/ui/compile/snippetPlan.ts +28 -0
- package/src/lib/ui/compile/spreadExcludedNames.ts +4 -4
- package/src/lib/ui/compile/structuralBlockTokens.ts +3 -4
- package/src/lib/ui/compile/switchPlan.ts +7 -1
- package/src/lib/ui/compile/tryPlan.ts +8 -1
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +2 -2
- package/src/lib/ui/compile/types/Binding.ts +24 -0
- package/src/lib/ui/compile/types/CompiledShadow.ts +13 -1
- package/src/lib/ui/compile/types/ElementPlan.ts +43 -0
- package/src/lib/ui/compile/types/ShadowKind.ts +9 -0
- package/src/lib/ui/compile/types/ShadowScope.ts +19 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +7 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +8 -8
- package/src/lib/ui/compile/withBindings.ts +45 -0
- package/src/lib/ui/createScope.ts +2 -2
- package/src/lib/ui/dom/MATHML_NAMESPACE.ts +2 -2
- package/src/lib/ui/dom/SVG_NAMESPACE.ts +3 -2
- package/src/lib/ui/dom/appendText.ts +1 -1
- package/src/lib/ui/dom/applyResolved.ts +1 -1
- package/src/lib/ui/dom/buildDetachedRange.ts +4 -3
- package/src/lib/ui/dom/cloneStatic.ts +3 -2
- package/src/lib/ui/dom/each.ts +26 -7
- package/src/lib/ui/dom/foreignWrapperTag.ts +2 -2
- package/src/lib/ui/dom/mountChild.ts +8 -1
- package/src/lib/ui/dom/readCall.ts +6 -4
- package/src/lib/ui/dom/scopeLabel.ts +3 -3
- package/src/lib/ui/dom/switchBlock.ts +3 -1
- package/src/lib/ui/dom/types/EachRow.ts +4 -0
- package/src/lib/ui/dom/types/SkeletonHoles.ts +4 -2
- package/src/lib/ui/history.ts +1 -2
- package/src/lib/ui/html.ts +28 -0
- package/src/lib/ui/installHotBridge.ts +0 -2
- package/src/lib/ui/renderChain.ts +16 -3
- package/src/lib/ui/router.ts +66 -15
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +1 -1
- package/src/lib/ui/runtime/CHILD_PRESENT.ts +8 -0
- package/src/lib/ui/runtime/OUTLET_MARKER.ts +2 -2
- package/src/lib/ui/runtime/RANGE_MARKER.ts +1 -1
- package/src/lib/ui/runtime/REACTIVE_CONTEXT.ts +9 -3
- package/src/lib/ui/runtime/captureModelDoc.ts +1 -1
- package/src/lib/ui/runtime/createDoc.ts +5 -1
- package/src/lib/ui/runtime/createEffectNode.ts +5 -5
- package/src/lib/ui/runtime/flushEffects.ts +22 -9
- package/src/lib/ui/runtime/historyEntries.ts +1 -1
- package/src/lib/ui/runtime/track.ts +4 -2
- package/src/lib/ui/runtime/trigger.ts +34 -20
- package/src/lib/ui/runtime/types/Route.ts +2 -2
- package/src/lib/ui/runtime/types/UiComponent.ts +3 -2
- package/src/lib/ui/runtime/types/UiProps.ts +9 -12
- package/src/lib/ui/runtime/walkPath.ts +15 -3
- package/src/lib/ui/startClient.ts +41 -14
- package/src/lib/ui/state.ts +3 -3
- package/src/lib/ui/types/Scope.ts +3 -3
- package/src/serverBuildPlugins.ts +11 -5
- package/src/zodCjsPlugin.ts +36 -0
- package/template/.zed/settings.json +7 -1
- package/template/package.json +3 -0
- package/template/src/ui/pages/layout.abide +3 -5
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import type { SemanticToken } from './types/SemanticToken.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Lexical highlighting for the HTML-shaped markup of a `.abide` component — element
|
|
5
|
+
and component tag names, attribute names and string values, comments, and the
|
|
6
|
+
`<`/`>`/`=` punctuation. A pure scan of raw source (independent of a successful
|
|
7
|
+
parse, so it survives mid-edit), it exists so the LSP owns the *structure* too,
|
|
8
|
+
not just `{…}` interiors: editors reuse tree-sitter-html as the grammar of record,
|
|
9
|
+
which has no production for abide's `attr={expr}` values — a multiline
|
|
10
|
+
template-literal attribute (`code={`…`}`) drops it into error recovery that never
|
|
11
|
+
re-syncs, miscoloring every element below. Emitting these tokens overrides that
|
|
12
|
+
broken parse wherever real structure sits.
|
|
13
|
+
|
|
14
|
+
`{…}` expression regions (attribute values, interpolations, `{#…}` block heads) and
|
|
15
|
+
the raw bodies of `<script>`/`<style>` are skipped here — those are the shadow
|
|
16
|
+
type-checker's and the structural block tokenizer's jobs. Lowercase tags get the
|
|
17
|
+
`tag` type, uppercase (components) get `type` (a constructor), mirroring the
|
|
18
|
+
tree-sitter highlight split.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/* Raw-text elements whose body is JS/CSS, not markup, so the scan jumps past it. */
|
|
22
|
+
const RAW_ELEMENTS = new Set(['script', 'style'])
|
|
23
|
+
|
|
24
|
+
const operator = (start: number, length: number): SemanticToken => ({
|
|
25
|
+
start,
|
|
26
|
+
length,
|
|
27
|
+
type: 'operator',
|
|
28
|
+
modifiers: [],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const isNameStart = (char: string): boolean => /[A-Za-z]/.test(char)
|
|
32
|
+
|
|
33
|
+
/* Characters that end a tag or attribute name. */
|
|
34
|
+
const isNameBreak = (char: string): boolean => /[\s=/>{}"'<]/.test(char)
|
|
35
|
+
|
|
36
|
+
export function markupTokens(source: string): SemanticToken[] {
|
|
37
|
+
const tokens: SemanticToken[] = []
|
|
38
|
+
let cursor = 0
|
|
39
|
+
while (cursor < source.length) {
|
|
40
|
+
const char = source.charAt(cursor)
|
|
41
|
+
/* An expression region — interiors are the shadow classifier's job. */
|
|
42
|
+
if (char === '{') {
|
|
43
|
+
cursor = skipExpression(source, cursor)
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
if (char === '<') {
|
|
47
|
+
if (source.startsWith('<!--', cursor)) {
|
|
48
|
+
const end = source.indexOf('-->', cursor + 4)
|
|
49
|
+
const close = end === -1 ? source.length : end + 3
|
|
50
|
+
tokens.push({
|
|
51
|
+
start: cursor,
|
|
52
|
+
length: close - cursor,
|
|
53
|
+
type: 'comment',
|
|
54
|
+
modifiers: [],
|
|
55
|
+
})
|
|
56
|
+
cursor = close
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
const isClose = source.charAt(cursor + 1) === '/'
|
|
60
|
+
const nameStart = cursor + (isClose ? 2 : 1)
|
|
61
|
+
if (isNameStart(source.charAt(nameStart))) {
|
|
62
|
+
tokens.push(operator(cursor, isClose ? 2 : 1))
|
|
63
|
+
const name = readName(source, nameStart)
|
|
64
|
+
const type = /[A-Z]/.test(name.charAt(0)) ? 'type' : 'tag'
|
|
65
|
+
tokens.push({ start: nameStart, length: name.length, type, modifiers: [] })
|
|
66
|
+
const afterName = nameStart + name.length
|
|
67
|
+
if (isClose) {
|
|
68
|
+
cursor = scanToClose(source, afterName, tokens)
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
const { end, selfClosed } = scanAttributes(source, afterName, tokens)
|
|
72
|
+
cursor =
|
|
73
|
+
RAW_ELEMENTS.has(name) && !selfClosed ? skipRawBody(source, end, name) : end
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
cursor += 1
|
|
78
|
+
}
|
|
79
|
+
return tokens
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Reads a tag/element name from `start` (letters, digits, `-`, `_`). */
|
|
83
|
+
function readName(source: string, start: number): string {
|
|
84
|
+
let cursor = start
|
|
85
|
+
while (cursor < source.length && /[A-Za-z0-9_-]/.test(source.charAt(cursor))) {
|
|
86
|
+
cursor += 1
|
|
87
|
+
}
|
|
88
|
+
return source.slice(start, cursor)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* From after a close tag's name, emits the `>` operator and returns past it. */
|
|
92
|
+
function scanToClose(source: string, from: number, tokens: SemanticToken[]): number {
|
|
93
|
+
let cursor = from
|
|
94
|
+
while (cursor < source.length && source.charAt(cursor) !== '>') {
|
|
95
|
+
cursor += 1
|
|
96
|
+
}
|
|
97
|
+
if (cursor < source.length) {
|
|
98
|
+
tokens.push(operator(cursor, 1))
|
|
99
|
+
return cursor + 1
|
|
100
|
+
}
|
|
101
|
+
return cursor
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Scans an open tag's attributes from after its name to the closing `>`/`/>`,
|
|
105
|
+
emitting an `attribute` token per name, `operator` for `=`, and string tokens
|
|
106
|
+
for quoted values; `{…}` values and spreads are left to the shadow. */
|
|
107
|
+
function scanAttributes(
|
|
108
|
+
source: string,
|
|
109
|
+
from: number,
|
|
110
|
+
tokens: SemanticToken[],
|
|
111
|
+
): { end: number; selfClosed: boolean } {
|
|
112
|
+
let cursor = from
|
|
113
|
+
while (cursor < source.length) {
|
|
114
|
+
const char = source.charAt(cursor)
|
|
115
|
+
if (/\s/.test(char)) {
|
|
116
|
+
cursor += 1
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
if (char === '>') {
|
|
120
|
+
tokens.push(operator(cursor, 1))
|
|
121
|
+
return { end: cursor + 1, selfClosed: false }
|
|
122
|
+
}
|
|
123
|
+
if (char === '/' && source.charAt(cursor + 1) === '>') {
|
|
124
|
+
tokens.push(operator(cursor, 2))
|
|
125
|
+
return { end: cursor + 2, selfClosed: true }
|
|
126
|
+
}
|
|
127
|
+
/* `{…code}` spread / expression attribute — shadow's job. */
|
|
128
|
+
if (char === '{') {
|
|
129
|
+
cursor = skipExpression(source, cursor)
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
const nameStart = cursor
|
|
133
|
+
while (cursor < source.length && !isNameBreak(source.charAt(cursor))) {
|
|
134
|
+
cursor += 1
|
|
135
|
+
}
|
|
136
|
+
if (cursor === nameStart) {
|
|
137
|
+
/* No progress on a stray delimiter — advance to avoid a stall. */
|
|
138
|
+
cursor += 1
|
|
139
|
+
continue
|
|
140
|
+
}
|
|
141
|
+
tokens.push({
|
|
142
|
+
start: nameStart,
|
|
143
|
+
length: cursor - nameStart,
|
|
144
|
+
type: 'attribute',
|
|
145
|
+
modifiers: [],
|
|
146
|
+
})
|
|
147
|
+
let eq = cursor
|
|
148
|
+
while (eq < source.length && /\s/.test(source.charAt(eq))) {
|
|
149
|
+
eq += 1
|
|
150
|
+
}
|
|
151
|
+
if (source.charAt(eq) === '=') {
|
|
152
|
+
tokens.push(operator(eq, 1))
|
|
153
|
+
eq += 1
|
|
154
|
+
while (eq < source.length && /\s/.test(source.charAt(eq))) {
|
|
155
|
+
eq += 1
|
|
156
|
+
}
|
|
157
|
+
cursor = scanAttributeValue(source, eq, tokens)
|
|
158
|
+
} else {
|
|
159
|
+
cursor = eq
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { end: cursor, selfClosed: false }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Scans an attribute value: a `{…}` expression (skipped), a quoted string (split
|
|
166
|
+
around any `{…}` interpolations), or a bare unquoted run. Returns past it. */
|
|
167
|
+
function scanAttributeValue(source: string, from: number, tokens: SemanticToken[]): number {
|
|
168
|
+
const char = source.charAt(from)
|
|
169
|
+
if (char === '{') {
|
|
170
|
+
return skipExpression(source, from)
|
|
171
|
+
}
|
|
172
|
+
if (char === '"' || char === "'") {
|
|
173
|
+
return scanQuotedValue(source, from, tokens)
|
|
174
|
+
}
|
|
175
|
+
let cursor = from
|
|
176
|
+
/* Unquoted values run to whitespace or `>`; a `/` is part of the value (a URL)
|
|
177
|
+
UNLESS it's the self-close `/>`, so stop before that `/` and leave it for the
|
|
178
|
+
attribute scanner to read as the self-close marker. */
|
|
179
|
+
while (cursor < source.length) {
|
|
180
|
+
const valueChar = source.charAt(cursor)
|
|
181
|
+
if (/[\s>]/.test(valueChar) || (valueChar === '/' && source.charAt(cursor + 1) === '>')) {
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
cursor += 1
|
|
185
|
+
}
|
|
186
|
+
if (cursor > from) {
|
|
187
|
+
tokens.push({ start: from, length: cursor - from, type: 'string', modifiers: [] })
|
|
188
|
+
}
|
|
189
|
+
return cursor
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Emits string tokens for the literal runs of a quoted value (delimiters
|
|
193
|
+
included), skipping `{…}` interpolations so the shadow colors their interiors. */
|
|
194
|
+
function scanQuotedValue(source: string, start: number, tokens: SemanticToken[]): number {
|
|
195
|
+
const quote = source.charAt(start)
|
|
196
|
+
let segmentStart = start
|
|
197
|
+
let cursor = start + 1
|
|
198
|
+
const flush = (end: number): void => {
|
|
199
|
+
if (end > segmentStart) {
|
|
200
|
+
tokens.push({
|
|
201
|
+
start: segmentStart,
|
|
202
|
+
length: end - segmentStart,
|
|
203
|
+
type: 'string',
|
|
204
|
+
modifiers: [],
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
while (cursor < source.length) {
|
|
209
|
+
const char = source.charAt(cursor)
|
|
210
|
+
if (char === quote) {
|
|
211
|
+
flush(cursor + 1)
|
|
212
|
+
return cursor + 1
|
|
213
|
+
}
|
|
214
|
+
if (char === '{') {
|
|
215
|
+
flush(cursor)
|
|
216
|
+
cursor = skipExpression(source, cursor)
|
|
217
|
+
segmentStart = cursor
|
|
218
|
+
continue
|
|
219
|
+
}
|
|
220
|
+
cursor += 1
|
|
221
|
+
}
|
|
222
|
+
flush(cursor)
|
|
223
|
+
return cursor
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* From the close `>` of a raw-text element's open tag, returns the index of its
|
|
227
|
+
`</tag` so the close tag is scanned as markup but the body is left untouched. */
|
|
228
|
+
function skipRawBody(source: string, from: number, tag: string): number {
|
|
229
|
+
const close = source.indexOf(`</${tag}`, from)
|
|
230
|
+
return close === -1 ? source.length : close
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* From the opening `{` at `start`, returns the index past the matching `}`,
|
|
234
|
+
tracking nested braces and skipping strings, template literals (with `${…}`),
|
|
235
|
+
and JS comments so their contents never sway the depth. A regex literal holding
|
|
236
|
+
an unbalanced brace is the one unhandled case — rare, accepted for a highlighter. */
|
|
237
|
+
function skipExpression(source: string, start: number): number {
|
|
238
|
+
let depth = 0
|
|
239
|
+
let cursor = start
|
|
240
|
+
while (cursor < source.length) {
|
|
241
|
+
const char = source.charAt(cursor)
|
|
242
|
+
if (char === '"' || char === "'") {
|
|
243
|
+
cursor = skipString(source, cursor)
|
|
244
|
+
continue
|
|
245
|
+
}
|
|
246
|
+
if (char === '`') {
|
|
247
|
+
cursor = skipTemplate(source, cursor)
|
|
248
|
+
continue
|
|
249
|
+
}
|
|
250
|
+
if (char === '/' && source.charAt(cursor + 1) === '/') {
|
|
251
|
+
const newline = source.indexOf('\n', cursor)
|
|
252
|
+
cursor = newline === -1 ? source.length : newline
|
|
253
|
+
continue
|
|
254
|
+
}
|
|
255
|
+
if (char === '/' && source.charAt(cursor + 1) === '*') {
|
|
256
|
+
const end = source.indexOf('*/', cursor + 2)
|
|
257
|
+
cursor = end === -1 ? source.length : end + 2
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
if (char === '{') {
|
|
261
|
+
depth += 1
|
|
262
|
+
} else if (char === '}') {
|
|
263
|
+
depth -= 1
|
|
264
|
+
if (depth === 0) {
|
|
265
|
+
return cursor + 1
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
cursor += 1
|
|
269
|
+
}
|
|
270
|
+
return cursor
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* From the opening quote at `start`, returns the index past the matching quote;
|
|
274
|
+
a backslash escapes the next character. */
|
|
275
|
+
function skipString(source: string, start: number): number {
|
|
276
|
+
const quote = source.charAt(start)
|
|
277
|
+
let cursor = start + 1
|
|
278
|
+
while (cursor < source.length) {
|
|
279
|
+
const char = source.charAt(cursor)
|
|
280
|
+
if (char === '\\') {
|
|
281
|
+
cursor += 2
|
|
282
|
+
continue
|
|
283
|
+
}
|
|
284
|
+
if (char === quote) {
|
|
285
|
+
return cursor + 1
|
|
286
|
+
}
|
|
287
|
+
cursor += 1
|
|
288
|
+
}
|
|
289
|
+
return cursor
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* From the opening backtick at `start`, returns the index past the matching
|
|
293
|
+
backtick. `${…}` re-enters expression scanning so its inner braces balance;
|
|
294
|
+
braces in the literal text are ignored. */
|
|
295
|
+
function skipTemplate(source: string, start: number): number {
|
|
296
|
+
let cursor = start + 1
|
|
297
|
+
while (cursor < source.length) {
|
|
298
|
+
const char = source.charAt(cursor)
|
|
299
|
+
if (char === '\\') {
|
|
300
|
+
cursor += 2
|
|
301
|
+
continue
|
|
302
|
+
}
|
|
303
|
+
if (char === '`') {
|
|
304
|
+
return cursor + 1
|
|
305
|
+
}
|
|
306
|
+
if (char === '$' && source.charAt(cursor + 1) === '{') {
|
|
307
|
+
cursor = skipExpression(source, cursor + 1)
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
cursor += 1
|
|
311
|
+
}
|
|
312
|
+
return cursor
|
|
313
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { pageUrlForFile } from '../../shared/pageUrlForFile.ts'
|
|
2
|
+
import { routeParamsShape } from '../../shared/routeParamsShape.ts'
|
|
3
|
+
|
|
4
|
+
const PAGES_SEGMENT = '/src/ui/pages/'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
The `props()` type for a `.abide` file's check shadow: a page/layout's route param shape
|
|
8
|
+
(so `const { id } = props()` infers `id: string`), or undefined for a non-page component
|
|
9
|
+
(which keeps the `Record<string, any>` default). Derived from the file path — a `page.abide`
|
|
10
|
+
or `layout.abide` under `src/ui/pages/` maps through `pageUrlForFile` to its route.
|
|
11
|
+
*/
|
|
12
|
+
export function pagePropsType(abidePath: string): string | undefined {
|
|
13
|
+
const pagesAt = abidePath.indexOf(PAGES_SEGMENT)
|
|
14
|
+
if (pagesAt === -1) {
|
|
15
|
+
return undefined
|
|
16
|
+
}
|
|
17
|
+
const base = abidePath.slice(abidePath.lastIndexOf('/') + 1)
|
|
18
|
+
if (base !== 'page.abide' && base !== 'layout.abide') {
|
|
19
|
+
return undefined
|
|
20
|
+
}
|
|
21
|
+
const relPath = abidePath.slice(pagesAt + PAGES_SEGMENT.length)
|
|
22
|
+
return routeParamsShape(pageUrlForFile(relPath))
|
|
23
|
+
}
|