@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.
Files changed (160) hide show
  1. package/AGENTS.md +293 -219
  2. package/CHANGELOG.md +46 -0
  3. package/README.md +191 -67
  4. package/package.json +2 -2
  5. package/src/abideLsp.ts +8 -4
  6. package/src/abideResolverPlugin.ts +11 -1
  7. package/src/build.ts +9 -0
  8. package/src/buildDisconnected.ts +2 -0
  9. package/src/devEntry.ts +1 -0
  10. package/src/lib/bundle/probeAbideServer.ts +1 -1
  11. package/src/lib/cli/parseArgvForRpc.ts +4 -2
  12. package/src/lib/cli/types/CliManifest.ts +6 -4
  13. package/src/lib/mcp/annotationsForMethod.ts +1 -1
  14. package/src/lib/mcp/toolResultFromResponse.ts +1 -1
  15. package/src/lib/mcp/types/JsonRpcRequest.ts +3 -3
  16. package/src/lib/server/AppModule.ts +1 -1
  17. package/src/lib/server/cli/handleCliDownload.ts +7 -5
  18. package/src/lib/server/cli/installScript.ts +3 -3
  19. package/src/lib/server/prompts/promptRegistry.ts +1 -1
  20. package/src/lib/server/prompts/types/Prompt.ts +2 -1
  21. package/src/lib/server/rpc/dispatchRpcInProcess.ts +2 -2
  22. package/src/lib/server/rpc/types/RpcHelper.ts +1 -1
  23. package/src/lib/server/runtime/createPublicAssetServer.ts +2 -2
  24. package/src/lib/server/runtime/createRouteDispatcher.ts +1 -1
  25. package/src/lib/server/runtime/createServer.ts +21 -0
  26. package/src/lib/server/runtime/createUiPageRenderer.ts +7 -11
  27. package/src/lib/server/runtime/crossOriginForbidden.ts +2 -2
  28. package/src/lib/server/runtime/crossOriginGate.ts +1 -1
  29. package/src/lib/server/runtime/devHotModuleResponse.ts +2 -2
  30. package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +1 -1
  31. package/src/lib/server/runtime/logExposedSurfaces.ts +3 -4
  32. package/src/lib/server/runtime/serializeCacheSnapshot.ts +3 -8
  33. package/src/lib/server/runtime/snapshotEntryFromCache.ts +10 -7
  34. package/src/lib/server/runtime/streamFromIterator.ts +2 -2
  35. package/src/lib/server/runtime/types/InspectorRpc.ts +2 -2
  36. package/src/lib/server/socket.ts +1 -1
  37. package/src/lib/server/sockets/createSocketDispatcher.ts +2 -2
  38. package/src/lib/server/sockets/socketRegistry.ts +2 -2
  39. package/src/lib/server/sockets/types/SocketClientFrame.ts +3 -3
  40. package/src/lib/shared/CACHE_WRAPPED.ts +3 -2
  41. package/src/lib/shared/REMOTE_FUNCTION.ts +1 -1
  42. package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +3 -2
  43. package/src/lib/shared/bodyValueForKind.ts +35 -0
  44. package/src/lib/shared/cache.ts +1 -1
  45. package/src/lib/shared/cacheEntryFromSnapshot.ts +23 -18
  46. package/src/lib/shared/createPushIterator.ts +1 -1
  47. package/src/lib/shared/decodeResponse.ts +11 -8
  48. package/src/lib/shared/hasReplayableRequest.ts +17 -0
  49. package/src/lib/shared/html.ts +7 -26
  50. package/src/lib/shared/manifestModule.ts +2 -2
  51. package/src/lib/shared/online.ts +1 -1
  52. package/src/lib/shared/outboxProbeSlot.ts +1 -1
  53. package/src/lib/shared/pageUrlForFile.ts +1 -1
  54. package/src/lib/shared/routeParamsShape.ts +20 -0
  55. package/src/lib/shared/snapshotShippable.ts +22 -0
  56. package/src/lib/shared/types/CacheEntry.ts +1 -1
  57. package/src/lib/shared/types/CacheInvalidation.ts +3 -3
  58. package/src/lib/shared/types/ClientFlags.ts +5 -3
  59. package/src/lib/shared/types/LastConnection.ts +1 -1
  60. package/src/lib/shared/url.ts +4 -1
  61. package/src/lib/shared/withBase.ts +1 -2
  62. package/src/lib/shared/writeDts.ts +2 -2
  63. package/src/lib/shared/writeRoutesDts.ts +3 -31
  64. package/src/lib/test/createTestSocketChannel.ts +3 -3
  65. package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +1 -1
  66. package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +40 -6
  67. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +1 -1
  68. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +49 -37
  69. package/src/lib/ui/compile/abideUiPlugin.ts +1 -1
  70. package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +3 -1
  71. package/src/lib/ui/compile/awaitPlan.ts +14 -2
  72. package/src/lib/ui/compile/catchBinding.ts +8 -0
  73. package/src/lib/ui/compile/classStyleMergePlan.ts +108 -0
  74. package/src/lib/ui/compile/collectAbideDiagnostics.ts +11 -0
  75. package/src/lib/ui/compile/compileComponent.ts +4 -4
  76. package/src/lib/ui/compile/compileModule.ts +20 -6
  77. package/src/lib/ui/compile/compileSSR.ts +12 -10
  78. package/src/lib/ui/compile/compileShadow.ts +107 -16
  79. package/src/lib/ui/compile/composeProps.ts +3 -3
  80. package/src/lib/ui/compile/createShadowLanguageService.ts +30 -2
  81. package/src/lib/ui/compile/createShadowProgram.ts +2 -1
  82. package/src/lib/ui/compile/createShadowScope.ts +42 -0
  83. package/src/lib/ui/compile/decodeHtmlEntities.ts +4 -0
  84. package/src/lib/ui/compile/desugarSignals.ts +12 -7
  85. package/src/lib/ui/compile/eachPlan.ts +61 -0
  86. package/src/lib/ui/compile/elementPlan.ts +55 -0
  87. package/src/lib/ui/compile/encodeSemanticTokens.ts +24 -1
  88. package/src/lib/ui/compile/generateBuild.ts +150 -127
  89. package/src/lib/ui/compile/generateSSR.ts +112 -73
  90. package/src/lib/ui/compile/ifPlan.ts +7 -1
  91. package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +25 -0
  92. package/src/lib/ui/compile/isPlainIdentifier.ts +7 -0
  93. package/src/lib/ui/compile/isTextLeaf.ts +8 -7
  94. package/src/lib/ui/compile/lowerContext.ts +36 -46
  95. package/src/lib/ui/compile/lowerDocAccess.ts +2 -2
  96. package/src/lib/ui/compile/lowerScript.ts +1 -1
  97. package/src/lib/ui/compile/markupTokens.ts +313 -0
  98. package/src/lib/ui/compile/pagePropsType.ts +23 -0
  99. package/src/lib/ui/compile/parseTemplate.ts +131 -61
  100. package/src/lib/ui/compile/reactiveBinding.ts +47 -0
  101. package/src/lib/ui/compile/renameSignalRefs.ts +44 -10
  102. package/src/lib/ui/compile/skeletonable.ts +5 -5
  103. package/src/lib/ui/compile/snippetPlan.ts +28 -0
  104. package/src/lib/ui/compile/spreadExcludedNames.ts +4 -4
  105. package/src/lib/ui/compile/structuralBlockTokens.ts +3 -4
  106. package/src/lib/ui/compile/switchPlan.ts +7 -1
  107. package/src/lib/ui/compile/tryPlan.ts +8 -1
  108. package/src/lib/ui/compile/types/AnalyzedComponent.ts +2 -2
  109. package/src/lib/ui/compile/types/Binding.ts +24 -0
  110. package/src/lib/ui/compile/types/CompiledShadow.ts +13 -1
  111. package/src/lib/ui/compile/types/ElementPlan.ts +43 -0
  112. package/src/lib/ui/compile/types/ShadowKind.ts +9 -0
  113. package/src/lib/ui/compile/types/ShadowScope.ts +19 -0
  114. package/src/lib/ui/compile/types/TemplateAttr.ts +7 -0
  115. package/src/lib/ui/compile/types/TemplateNode.ts +8 -8
  116. package/src/lib/ui/compile/withBindings.ts +45 -0
  117. package/src/lib/ui/createScope.ts +2 -2
  118. package/src/lib/ui/dom/MATHML_NAMESPACE.ts +2 -2
  119. package/src/lib/ui/dom/SVG_NAMESPACE.ts +3 -2
  120. package/src/lib/ui/dom/appendText.ts +1 -1
  121. package/src/lib/ui/dom/applyResolved.ts +1 -1
  122. package/src/lib/ui/dom/buildDetachedRange.ts +4 -3
  123. package/src/lib/ui/dom/cloneStatic.ts +3 -2
  124. package/src/lib/ui/dom/each.ts +26 -7
  125. package/src/lib/ui/dom/foreignWrapperTag.ts +2 -2
  126. package/src/lib/ui/dom/mountChild.ts +8 -1
  127. package/src/lib/ui/dom/readCall.ts +6 -4
  128. package/src/lib/ui/dom/scopeLabel.ts +3 -3
  129. package/src/lib/ui/dom/switchBlock.ts +3 -1
  130. package/src/lib/ui/dom/types/EachRow.ts +4 -0
  131. package/src/lib/ui/dom/types/SkeletonHoles.ts +4 -2
  132. package/src/lib/ui/history.ts +1 -2
  133. package/src/lib/ui/html.ts +28 -0
  134. package/src/lib/ui/installHotBridge.ts +0 -2
  135. package/src/lib/ui/renderChain.ts +16 -3
  136. package/src/lib/ui/router.ts +66 -15
  137. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +1 -1
  138. package/src/lib/ui/runtime/CHILD_PRESENT.ts +8 -0
  139. package/src/lib/ui/runtime/OUTLET_MARKER.ts +2 -2
  140. package/src/lib/ui/runtime/RANGE_MARKER.ts +1 -1
  141. package/src/lib/ui/runtime/REACTIVE_CONTEXT.ts +9 -3
  142. package/src/lib/ui/runtime/captureModelDoc.ts +1 -1
  143. package/src/lib/ui/runtime/createDoc.ts +5 -1
  144. package/src/lib/ui/runtime/createEffectNode.ts +5 -5
  145. package/src/lib/ui/runtime/flushEffects.ts +22 -9
  146. package/src/lib/ui/runtime/historyEntries.ts +1 -1
  147. package/src/lib/ui/runtime/track.ts +4 -2
  148. package/src/lib/ui/runtime/trigger.ts +34 -20
  149. package/src/lib/ui/runtime/types/Route.ts +2 -2
  150. package/src/lib/ui/runtime/types/UiComponent.ts +3 -2
  151. package/src/lib/ui/runtime/types/UiProps.ts +9 -12
  152. package/src/lib/ui/runtime/walkPath.ts +15 -3
  153. package/src/lib/ui/startClient.ts +41 -14
  154. package/src/lib/ui/state.ts +3 -3
  155. package/src/lib/ui/types/Scope.ts +3 -3
  156. package/src/serverBuildPlugins.ts +11 -5
  157. package/src/zodCjsPlugin.ts +36 -0
  158. package/template/.zed/settings.json +7 -1
  159. package/template/package.json +3 -0
  160. 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
+ }