@abide/abide 0.101.0 → 0.102.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 (157) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/bin/abide.ts +223 -164
  3. package/docs/REGISTRY.md +726 -0
  4. package/docs/RULEBOOK.md +2753 -0
  5. package/docs/abide.md +12878 -0
  6. package/package.json +5 -3
  7. package/scaffold/AGENTS.md +31 -0
  8. package/scaffold/CLAUDE.md +1 -0
  9. package/scaffold/src/layout.abide +18 -0
  10. package/src/compiler/Lowering.ts +78 -9
  11. package/src/compiler/ShadowMap.ts +2 -2
  12. package/src/compiler/TypeSession.ts +262 -67
  13. package/src/compiler/argsSchemaOf.ts +1 -1
  14. package/src/compiler/checkApp.ts +43 -15
  15. package/src/compiler/checkedShadow.ts +39 -5
  16. package/src/compiler/classifyRead.ts +6 -4
  17. package/src/compiler/classifyTemplate.ts +1 -1
  18. package/src/compiler/compileApp.ts +433 -193
  19. package/src/compiler/compiledEntry.ts +43 -39
  20. package/src/compiler/componentGraph.ts +58 -71
  21. package/src/compiler/declaredExports.ts +24 -7
  22. package/src/compiler/emitClient.ts +141 -71
  23. package/src/compiler/emitComponent.ts +37 -8
  24. package/src/compiler/emitSource.ts +106 -13
  25. package/src/compiler/handlersUnder.ts +106 -59
  26. package/src/compiler/index.ts +7 -0
  27. package/src/compiler/mountsOf.ts +138 -0
  28. package/src/compiler/nearest.ts +14 -0
  29. package/src/compiler/parseAbide.ts +157 -20
  30. package/src/compiler/projectShadow.ts +213 -7
  31. package/src/compiler/reactiveTypes.ts +20 -7
  32. package/src/compiler/readSources.ts +15 -2
  33. package/src/compiler/readsIn.ts +61 -7
  34. package/src/compiler/renderMarkup.ts +3 -3
  35. package/src/compiler/scaffold.ts +30 -11
  36. package/src/compiler/serveLanguage.ts +125 -34
  37. package/src/compiler/sourceMapOf.ts +193 -0
  38. package/src/compiler/splitImports.ts +205 -83
  39. package/src/compiler/syncTypes.ts +32 -0
  40. package/src/compiler/tokenize.ts +14 -0
  41. package/src/compiler/writeDefaultProject.ts +1 -1
  42. package/src/compiler/writeTypes.ts +82 -0
  43. package/src/server/CLIENT_BUILD.ts +11 -13
  44. package/src/server/DEFAULT_PORT.ts +1 -1
  45. package/src/server/LAUNCHER_FILES.ts +9 -0
  46. package/src/server/MODULE_QUERIES.ts +18 -0
  47. package/src/server/TABLES.ts +7 -0
  48. package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
  49. package/src/server/abideModules.ts +391 -0
  50. package/src/server/answerFailure.ts +16 -6
  51. package/src/server/answerRequest.ts +235 -106
  52. package/src/server/answerRpc.ts +102 -30
  53. package/src/server/answerSocket.ts +65 -20
  54. package/src/server/answerValue.ts +22 -9
  55. package/src/server/appDirectories.ts +1 -1
  56. package/src/server/appPlugins.ts +1 -1
  57. package/src/server/appTree.ts +179 -0
  58. package/src/server/argsFor.ts +2 -2
  59. package/src/server/assistant.ts +2 -2
  60. package/src/server/bridgeRelay.ts +6 -8
  61. package/src/server/claudeCode.ts +0 -6
  62. package/src/server/clientBuildOptions.ts +8 -3
  63. package/src/server/coerceArgs.ts +1 -1
  64. package/src/server/commands.ts +37 -37
  65. package/src/server/componentModules.d.ts +3 -1
  66. package/src/server/compressAnswer.ts +4 -4
  67. package/src/server/config.ts +25 -29
  68. package/src/server/configFields.ts +31 -25
  69. package/src/server/createApp.ts +59 -73
  70. package/src/server/devWorker.ts +114 -181
  71. package/src/server/executableName.ts +4 -0
  72. package/src/server/fenceServer.ts +37 -35
  73. package/src/server/generatedSurfaces.ts +99 -66
  74. package/src/server/hooks.ts +124 -52
  75. package/src/server/iconAccent.ts +191 -0
  76. package/src/server/index.ts +11 -7
  77. package/src/server/installSourceMaps.ts +88 -0
  78. package/src/server/launcherOptions.ts +34 -31
  79. package/src/server/launcherPage.ts +107 -64
  80. package/src/server/manifestOf.ts +16 -13
  81. package/src/server/namesUnder.ts +2 -2
  82. package/src/server/nativeMenu.ts +5 -5
  83. package/src/server/negotiateEncoding.ts +1 -1
  84. package/src/server/principal.ts +10 -5
  85. package/src/server/realOrAsGiven.ts +10 -0
  86. package/src/server/refuseAddressCollision.ts +21 -14
  87. package/src/server/remote.ts +49 -52
  88. package/src/server/render.ts +65 -94
  89. package/src/server/renderComponent.ts +5 -0
  90. package/src/server/reportHealth.ts +2 -2
  91. package/src/server/requestScope.ts +12 -1
  92. package/src/server/runBuild.ts +24 -0
  93. package/src/server/runCompiled.ts +5 -5
  94. package/src/server/runDesktop.ts +52 -27
  95. package/src/server/serveBuild.ts +43 -52
  96. package/src/server/serveDev.ts +73 -37
  97. package/src/server/shell.ts +7 -22
  98. package/src/server/sourceMappings.ts +139 -0
  99. package/src/server/surfaceOf.ts +18 -3
  100. package/src/server/testBuild.ts +1 -19
  101. package/src/server/withAbideSources.ts +117 -0
  102. package/src/shared/ADDRESSES.ts +7 -18
  103. package/src/shared/APP_META.ts +7 -0
  104. package/src/shared/CopyOptions.ts +1 -1
  105. package/src/shared/DEBUG_META.ts +1 -1
  106. package/src/shared/RESERVED_DIRECTORIES.ts +15 -0
  107. package/src/shared/cookies.ts +2 -2
  108. package/src/shared/csp.ts +1 -1
  109. package/src/shared/escapeText.ts +1 -1
  110. package/src/shared/filledPath.ts +85 -0
  111. package/src/shared/framing.ts +79 -6
  112. package/src/shared/headKeyOf.ts +2 -0
  113. package/src/shared/index.ts +1 -1
  114. package/src/shared/log.ts +27 -13
  115. package/src/shared/matchRoute.ts +23 -4
  116. package/src/shared/menuItem.ts +3 -3
  117. package/src/shared/reactive/Keyed.ts +2 -2
  118. package/src/shared/reactive/NotLanded.ts +1 -1
  119. package/src/shared/reactive/Reactive.ts +43 -43
  120. package/src/shared/reactive/argsContract.ts +4 -4
  121. package/src/shared/reactive/canonicalKey.ts +4 -4
  122. package/src/shared/reactive/chunksOfStream.ts +6 -0
  123. package/src/shared/reactive/index.ts +1 -1
  124. package/src/shared/reactive/recordWrites.ts +103 -5
  125. package/src/shared/reactive/structural.ts +1 -1
  126. package/src/shared/reactive/tracking.ts +1 -1
  127. package/src/shared/reactive/watch.ts +3 -3
  128. package/src/shared/refuse.ts +1 -1
  129. package/src/shared/routePattern.ts +82 -46
  130. package/src/shared/rpc.ts +37 -8
  131. package/src/shared/rpcRequest.ts +27 -3
  132. package/src/shared/url.ts +4 -44
  133. package/src/shared/writePath.ts +1 -1
  134. package/src/ui/LAYER_NAMES.ts +1 -1
  135. package/src/ui/List.ts +3 -3
  136. package/src/ui/MOUNTING_LEVEL.ts +18 -0
  137. package/src/ui/Mismatch.ts +1 -1
  138. package/src/ui/RawRun.ts +1 -1
  139. package/src/ui/Router.ts +38 -31
  140. package/src/ui/Scope.ts +1 -1
  141. package/src/ui/TryRegion.ts +2 -2
  142. package/src/ui/applyHead.ts +33 -0
  143. package/src/ui/index.ts +1 -1
  144. package/src/ui/menuBridge.ts +1 -1
  145. package/src/ui/moveNode.ts +1 -1
  146. package/src/ui/reconcile.ts +2 -2
  147. package/src/ui/reportMenu.ts +2 -2
  148. package/src/ui/routing.ts +2 -2
  149. package/src/ui/setAttribute.ts +1 -1
  150. package/src/ui/setText.ts +1 -1
  151. package/src/ui/socketWire.ts +9 -7
  152. package/src/ui/wire.ts +1 -1
  153. package/scaffold/src/server/app.ts +0 -6
  154. package/scaffold/src/shared/.gitkeep +0 -0
  155. package/scaffold/src/ui/app.html +0 -8
  156. package/src/server/componentImports.ts +0 -56
  157. /package/scaffold/src/{ui/pages/page.abide → page.abide} +0 -0
@@ -1,8 +1,14 @@
1
+ import { tablePath } from '#server/abideModules.ts'
1
2
  import type { AppPackage } from '#server/config.ts'
2
- import type { LauncherOptions } from '#server/launcherOptions.ts'
3
+ import type { SourceMaps } from '#server/installSourceMaps.ts'
4
+ import { SERVER_TABLE } from '#server/TABLES.ts'
3
5
  import abidePackage from '../../package.json' with { type: 'json' }
4
6
 
5
- // What the executable starts from: `abide compile`'s, and `abide bundle`'s window over it (38.71).
7
+ // What the executable starts from: `abide compile`'s, and `abide bundle`'s window over it (38.70).
8
+ const INSTALL_SOURCE_MAPS = Bun.fileURLToPath(
9
+ new URL('../server/installSourceMaps.ts', import.meta.url),
10
+ )
11
+
6
12
  const RUNNERS = {
7
13
  compiled: {
8
14
  name: 'runCompiled',
@@ -22,7 +28,7 @@ const PACKAGE = Bun.fileURLToPath(new URL('../../', import.meta.url))
22
28
  /**
23
29
  * Abide's own package entries, by the specifier an app imports each with, to the module behind it.
24
30
  *
25
- * 38.57 — the compiler is not among them: the executable does not hold it.
31
+ * 38.56 — the compiler is not among them: the executable does not hold it.
26
32
  */
27
33
  function abideEntries(): Map<string, string> {
28
34
  const specifiers = new Map<string, string>()
@@ -41,8 +47,8 @@ function abideEntries(): Map<string, string> {
41
47
  /**
42
48
  * The source of the module a compiled app starts from, written into the build beside `server.ts`.
43
49
  *
44
- * 38.11 — every part `runCompiled` boots over, named by a static import the bundler follows into
45
- * the executable. 38.45 — NOTHING IN IT IS FOUND AT RUN TIME — a file this module does not name is
50
+ * 38.10 — every part `runCompiled` boots over, named by a static import the bundler follows into
51
+ * the executable. 38.44 — NOTHING IN IT IS FOUND AT RUN TIME — a file this module does not name is
46
52
  * a file the executable does not hold, which is what makes the artifact a property of the build
47
53
  * rather than of the machine it lands on.
48
54
  *
@@ -52,24 +58,25 @@ function abideEntries(): Map<string, string> {
52
58
  */
53
59
  export function compiledEntry(app: {
54
60
  /**
55
- * 38.71 — what `abide bundle` read out of `src/bundle/`, and `launch` the path of its
56
- * `launch.abide`; `undefined` for `abide compile`.
61
+ * 38.70 — what `abide bundle` read of the launcher's files: `launch` the path of its
62
+ * `launcher.abide`, and `custom` whether that file has markup of its own; `undefined` for
63
+ * `abide compile`.
57
64
  */
58
65
  desktop:
59
66
  | {
60
- options: LauncherOptions
61
67
  launch: string | undefined
62
- shell: string
68
+ custom: boolean
69
+ document: string
70
+ icon: string | undefined
71
+ accent: string | undefined
63
72
  }
64
73
  | undefined
65
- out: string
66
- /** 37.1 — `src/server/app.ts`, where the app has one. */
67
- hooks: string | undefined
74
+ /** The app's root, below which the server's table is served (`abideModules`). */
75
+ root: string
76
+ /** 38.97 — each server module's map, by the `.abide` it is served at. */
77
+ sourceMaps: SourceMaps
68
78
  package: AppPackage
69
- shell: string
70
79
  manifest: unknown
71
- /** 16.2 — each handler's source path as its row names it, to the file. */
72
- handlerModules: Map<string, string>
73
80
  /** Every file under the build's `client/`, by name to the file. */
74
81
  client: Map<string, string>
75
82
  /** Every file under the build's `public/`, by name to the file. */
@@ -77,8 +84,8 @@ export function compiledEntry(app: {
77
84
  }): string {
78
85
  const runner = RUNNERS[app.desktop === undefined ? 'compiled' : 'desktop']
79
86
  let imports = `import { ${runner.name} } from ${JSON.stringify(runner.path)}
80
- import * as table from ${JSON.stringify(`${app.out}/server.ts`)}
81
- import { handlers } from ${JSON.stringify(`${app.out}/rpc.ts`)}
87
+ import { installSourceMaps } from ${JSON.stringify(INSTALL_SOURCE_MAPS)}
88
+ import * as table from ${JSON.stringify(tablePath(app.root, SERVER_TABLE))}
82
89
  `
83
90
  const files = (kind: string, paths: Map<string, string>): string => {
84
91
  let entries = ''
@@ -92,49 +99,46 @@ import { handlers } from ${JSON.stringify(`${app.out}/rpc.ts`)}
92
99
  }
93
100
  const client = files('client', app.client)
94
101
  const published = files('published', app.published)
95
- let modules = ''
96
- for (const [source, path] of app.handlerModules)
97
- modules += `\n ${JSON.stringify(source)}: () => import(${JSON.stringify(path)}),`
98
- // A handler module as a script names it: `#server/rpc/invoices`, with or without its extension.
99
102
  let specifiers = ''
100
103
  for (const [specifier, path] of abideEntries())
101
104
  specifiers += `\n ${JSON.stringify(specifier)}: () => import(${JSON.stringify(path)}),`
102
- for (const source of app.handlerModules.keys())
103
- for (const specifier of [
104
- `#${source}`,
105
- `#${source.replace(/\.ts$/, '')}`,
106
- ])
107
- specifiers += `\n ${JSON.stringify(specifier)}: handlerModules[${JSON.stringify(source)}],`
108
- const hooks =
109
- app.hooks === undefined
110
- ? 'undefined'
111
- : `() => import(${JSON.stringify(app.hooks)})`
112
105
  const desktop =
113
106
  app.desktop === undefined
114
107
  ? ''
115
108
  : `
116
109
  {
117
- options: ${JSON.stringify(app.desktop.options)},
110
+ custom: ${app.desktop.custom},
118
111
  launch: ${app.desktop.launch === undefined ? 'undefined' : `() => import(${JSON.stringify(app.desktop.launch)})`},
119
- shell: ${JSON.stringify(app.desktop.shell)},
112
+ document: ${JSON.stringify(app.desktop.document)},
113
+ icon: ${JSON.stringify(app.desktop.icon)},
114
+ accent: ${JSON.stringify(app.desktop.accent)},
120
115
  },`
121
116
  return `// Written by \`abide compile\`. The whole app, as what one executable holds.
122
117
  ${imports}
123
- const handlerModules = {${modules}
124
- }
118
+ // 38.56 — a file declaring handlers as a script names it through the app's one alias:
119
+ // \`#src/invoices/page.abide\`, to the module its server block compiled into.
120
+ const declared = {}
121
+ for (const row of table.handlers) declared[\`#src/\${row.source}\`] = row.load
122
+
123
+ // 38.97 — before anything runs, a script \`run\` included: the executable's frames name each
124
+ // \`.abide\` as it was bundled.
125
+ installSourceMaps(${JSON.stringify(app.sourceMaps)}, undefined)
125
126
 
126
127
  await ${runner.name}(
127
- { hooks: ${hooks}, package: ${JSON.stringify(app.package)} },
128
+ // 37.1 — every server block, imported before the boot.
129
+ {
130
+ hooks: () => Promise.all(table.servers.map((load) => load())),
131
+ package: ${JSON.stringify(app.package)},
132
+ },
128
133
  {
129
134
  table,
130
- handlers,
131
- handlerModule: (source) => handlerModules[source](),
132
- shell: ${JSON.stringify(app.shell)},
135
+ handlers: table.handlers,
133
136
  manifest: ${JSON.stringify(app.manifest)},
134
137
  client: ${client},
135
138
  published: ${published},
136
139
  },
137
140
  {${specifiers}
141
+ ...declared,
138
142
  },${desktop}
139
143
  )
140
144
  `
@@ -1,57 +1,40 @@
1
- import { dirname, posix, relative as relativePath } from 'node:path' // bun exposes no path helpers of its own
2
- import { type FileKind, fileKind, routeOf } from '#shared/routePattern.ts'
3
- import type { AbideFile } from './parseAbide.ts'
4
- import { abideImports, relativeSpecifiers } from './splitImports.ts'
1
+ import { dirname, relative as relativeTo } from 'node:path' // bun exposes no path helpers of its own
2
+ import {
3
+ type FileKind,
4
+ fileKind,
5
+ placeOf,
6
+ routeOf,
7
+ } from '#shared/routePattern.ts'
8
+ import { type AbideFile, scriptBodies } from './parseAbide.ts'
9
+ import { abideImports, attributedImports } from './splitImports.ts'
5
10
 
6
11
  type Resolve = (specifier: string, from: string) => string | undefined
7
12
 
8
13
  /**
9
- * The build's component graph: which files are invoked as components, and where each file's
10
- * `.abide` imports land in the output tree.
14
+ * Which files are invoked as components: every source path another file imports.
11
15
  *
12
- * 32.17 — off ONE reading of every file's imports: each `.abide` specifier is resolved once,
13
- * against the file that wrote it, and both answers the build needs are read off that resolution.
14
- *
15
- * `invoked` is every source path another file imports as a component (`emitClient`'s `selfFill`).
16
- * By PATH, not by file name: two `Row.abide` in two directories are two components, and only the
17
- * one somebody imports carries its own markup.
18
- *
19
- * `imports` is, per importing file, where each `.abide` specifier it wrote lands in the output
20
- * tree, relative and without an extension (`splitImports`). The output mirrors the source, so
21
- * the path between two sources IS the path between their modules — which is what turns a seam
22
- * specifier (`#ui/components/Row.abide`) into one that resolves inside the build rather than into
23
- * a source tree where the compiler wrote nothing.
16
+ * 32.17 — off ONE reading of every file's imports, each `.abide` specifier resolved once against
17
+ * the file that wrote it. By PATH, not by file name: two `Row.abide` in two directories are two
18
+ * components, and only the one somebody imports carries its own markup (`emitClient`'s `selfFill`).
24
19
  *
25
20
  * `resolve` is the app's: a build asks bun, and a harness that lays an app out by hand answers for
26
- * its own seams. A specifier it cannot resolve, or that lands outside `files`, is left as written.
21
+ * its own seams. A specifier it cannot resolve, or that lands outside `files`, invokes nothing.
27
22
  */
28
23
  export function componentGraph(
29
24
  files: readonly { path: string; file: AbideFile }[],
30
25
  resolve: Resolve,
31
- ): {
32
- invoked: Set<string>
33
- imports: Map<string, Map<string, string>>
34
- } {
26
+ ): { invoked: Set<string> } {
35
27
  const known = new Set<string>()
36
28
  for (const entry of files) known.add(entry.path)
37
29
  const invoked = new Set<string>()
38
- const imports = new Map<string, Map<string, string>>()
39
- for (const entry of files) {
40
- const from = dirname(entry.path)
41
- const landed = new Map<string, string>()
42
- for (const [specifier, target] of abideImportsOf(
30
+ for (const entry of files)
31
+ for (const target of abideImportsOf(
43
32
  entry.path,
44
33
  entry.file,
45
34
  resolve,
46
- )) {
47
- if (!known.has(target)) continue
48
- invoked.add(target)
49
- const path = relativePath(from, target.slice(0, -'.abide'.length))
50
- landed.set(specifier, path.startsWith('.') ? path : `./${path}`)
51
- }
52
- imports.set(entry.path, landed)
53
- }
54
- return { invoked, imports }
35
+ ).values())
36
+ if (known.has(target)) invoked.add(target)
37
+ return { invoked }
55
38
  }
56
39
 
57
40
  /**
@@ -69,7 +52,7 @@ export function abideImportsOf(
69
52
  ): Map<string, string> {
70
53
  const from = dirname(path)
71
54
  const resolved = new Map<string, string>()
72
- for (const script of [...file.modules, ...file.scripts])
55
+ for (const script of scriptBodies(file))
73
56
  for (const { specifier } of abideImports(script)) {
74
57
  if (resolved.has(specifier)) continue
75
58
  const target = resolve(specifier, from)
@@ -81,6 +64,8 @@ export function abideImportsOf(
81
64
  /** One source in the app graph: where it is below the root, what it is, and its address. */
82
65
  export type AppNode<Source> = Source & {
83
66
  relative: string
67
+ /** 23.19 — the path it answers by: its own below the root, or the one its mount gives it. */
68
+ position: string
84
69
  kind: FileKind
85
70
  /** 23.3 — the address of a page or an error page, and `undefined` for anything else. */
86
71
  route: string | undefined
@@ -88,52 +73,54 @@ export type AppNode<Source> = Source & {
88
73
 
89
74
  /**
90
75
  * The app as one graph, built once per build or check: every source with its kind and address,
91
- * which files are invoked as components, and where each file's every import lands.
92
- *
93
- * `imports` is `componentGraph`'s with the relative specifiers landed beside the `.abide` ones —
94
- * 34.14, A RELATIVE IMPORT RESOLVES FROM THE FILE THAT WROTE IT. The module that file compiles to
95
- * is in the output tree, which holds compiled `.abide` files and nothing else, so a stylesheet or
96
- * a `.ts` beside the source is reached by the path from the module back to it.
97
- *
98
- * `output` is where the modules are written, relative to `root`.
76
+ * and which files are invoked as components.
99
77
  */
100
- export function appGraph<Source extends { path: string; file: AbideFile }>(
78
+ export function appGraph<
79
+ Source extends {
80
+ path: string
81
+ file: AbideFile
82
+ position?: string | undefined
83
+ },
84
+ >(
101
85
  root: string,
102
86
  sources: readonly Source[],
103
- output: string,
104
87
  resolve: Resolve,
105
- ): {
106
- nodes: AppNode<Source>[]
107
- invoked: Set<string>
108
- imports: Map<string, Map<string, string>>
109
- } {
88
+ ): { nodes: AppNode<Source>[]; invoked: Set<string> } {
110
89
  const nodes: AppNode<Source>[] = []
111
90
  for (const source of sources) {
112
- const relative = source.path.slice(root.length + 1)
113
- const kind = fileKind(relative)
91
+ // Below the root for the app's own files, and above it — `../node_modules/…` — for a
92
+ // dependency's (34.28).
93
+ const relative = relativeTo(root, source.path)
94
+ const position = source.position ?? relative
95
+ // 34.21 — a server block of the app's own under a `server` or `public` directory has no
96
+ // address: refused here, before anything reads one off it. A dependency's has none of its own
97
+ // (34.31); the app mounts what it declares where the app re-exports it.
98
+ const place = placeOf(position)
99
+ if (
100
+ source.file.servers.length > 0 &&
101
+ (place === 'server' || place === 'public')
102
+ )
103
+ throw new Error(
104
+ `abide: ${relative} has a <script server>, and it is under a ${place} directory, where nothing has an address. Move the file out of it, or the code into a module under a server directory. RULEBOOK 34.21.`,
105
+ )
106
+ // 34.25 — a file imported as text is found at build, not when its module first loads.
107
+ for (const script of scriptBodies(source.file))
108
+ for (const { specifier } of attributedImports(script, 'text'))
109
+ if (resolve(specifier, dirname(source.path)) === undefined)
110
+ throw new Error(
111
+ `abide: ${relative} imports ${specifier} as text, and there is no such file. RULEBOOK 34.25.`,
112
+ )
113
+ const kind = fileKind(position)
114
114
  nodes.push({
115
115
  ...source,
116
116
  relative,
117
+ position,
117
118
  kind,
118
119
  route:
119
120
  kind === 'page' || kind === 'error'
120
- ? routeOf(relative)
121
+ ? routeOf(position)
121
122
  : undefined,
122
123
  })
123
124
  }
124
- const { invoked, imports } = componentGraph(sources, resolve)
125
- for (const node of nodes) {
126
- const landed = imports.get(node.path) as Map<string, string>
127
- const written = dirname(node.path)
128
- const emitted = posix.join(root, output, dirname(node.relative))
129
- for (const script of [...node.file.modules, ...node.file.scripts])
130
- for (const specifier of relativeSpecifiers(script)) {
131
- const path = posix.relative(
132
- emitted,
133
- posix.join(written, specifier),
134
- )
135
- landed.set(specifier, path.startsWith('.') ? path : `./${path}`)
136
- }
137
- }
138
- return { nodes, invoked, imports }
125
+ return { nodes, invoked: componentGraph(sources, resolve).invoked }
139
126
  }
@@ -189,8 +189,8 @@ async function declaringMethod(
189
189
  }
190
190
  | undefined
191
191
  > {
192
- const declaration = await symbol.valueDeclaration?.resolve()
193
- const call =
192
+ let declaration = await symbol.valueDeclaration?.resolve()
193
+ let call =
194
194
  declaration === undefined
195
195
  ? undefined
196
196
  : isVariableDeclaration(declaration)
@@ -198,6 +198,24 @@ async function declaringMethod(
198
198
  : isExportAssignment(declaration)
199
199
  ? declaration.expression
200
200
  : undefined
201
+ // 19.15 — A BINDING NAMING ANOTHER BINDING is the call that one was declared by: `export const
202
+ // named = drop` is `drop`'s handler under another name. Bounded, so a cycle the checker would
203
+ // refuse anyway cannot spin here.
204
+ for (
205
+ let hops = 0;
206
+ hops < 8 && call !== undefined && isIdentifier(call);
207
+ hops += 1
208
+ ) {
209
+ const bound = await checker.getSymbolAtLocation(call)
210
+ if (bound === undefined) return undefined
211
+ declaration = await (
212
+ await aliasedSymbol(checker, bound)
213
+ ).valueDeclaration?.resolve()
214
+ call =
215
+ declaration !== undefined && isVariableDeclaration(declaration)
216
+ ? declaration.initializer
217
+ : undefined
218
+ }
201
219
  if (call === undefined || !isCallExpression(call)) return undefined
202
220
  const callee = isPropertyAccessExpression(call.expression)
203
221
  ? call.expression.name
@@ -520,11 +538,10 @@ function describedAs(
520
538
  /**
521
539
  * What a handler call's inline `clients` declares for `surface`, or `undefined` where it does not.
522
540
  *
523
- * 20.4, 38.63 — written on the declaration, which is what the build can read without running it:
524
- * `ui: false` hands a browser no wrapper, and importing the handler from `#ui` is 16.29's compile
525
- * error; `cli: false` keeps it off the shell's listing, which reads the table the build wrote (38.7);
526
- * and an explicit `true` overrides 16.37's default. An options object built elsewhere, or a value
527
- * that is not a literal, is not read.
541
+ * 20.4, 38.62 — written on the declaration, which is what the build can read without running it:
542
+ * `ui: false` gives the file's browser copy no stand-in for it; `cli: false` keeps it off the
543
+ * shell's listing, which reads the table the build wrote (38.7); and an explicit `true` overrides
544
+ * 16.37's default. An options object built elsewhere, or a value that is not a literal, is not read.
528
545
  */
529
546
  function declaredFor(
530
547
  call: Node & { arguments?: readonly Node[] },