@abide/abide 0.101.0 → 0.103.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 (161) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/bin/abide.ts +226 -164
  3. package/docs/REGISTRY.md +730 -0
  4. package/docs/RULEBOOK.md +2768 -0
  5. package/docs/abide.md +13127 -0
  6. package/package.json +6 -3
  7. package/scaffold/AGENTS.md +38 -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 +341 -71
  13. package/src/compiler/appMap.ts +96 -0
  14. package/src/compiler/argsSchemaOf.ts +1 -1
  15. package/src/compiler/checkApp.ts +53 -8
  16. package/src/compiler/checkedShadow.ts +73 -6
  17. package/src/compiler/classifyRead.ts +6 -4
  18. package/src/compiler/classifyTemplate.ts +1 -1
  19. package/src/compiler/compileApp.ts +598 -200
  20. package/src/compiler/compiledEntry.ts +48 -40
  21. package/src/compiler/componentGraph.ts +57 -71
  22. package/src/compiler/declaredExports.ts +26 -9
  23. package/src/compiler/emitClient.ts +142 -72
  24. package/src/compiler/emitComponent.ts +41 -12
  25. package/src/compiler/emitSource.ts +102 -13
  26. package/src/compiler/handlersUnder.ts +164 -76
  27. package/src/compiler/hooksCalled.ts +35 -0
  28. package/src/compiler/index.ts +7 -0
  29. package/src/compiler/mountsOf.ts +150 -0
  30. package/src/compiler/nearest.ts +14 -0
  31. package/src/compiler/parseAbide.ts +157 -20
  32. package/src/compiler/projectShadow.ts +250 -19
  33. package/src/compiler/reactiveTypes.ts +20 -7
  34. package/src/compiler/readSources.ts +15 -2
  35. package/src/compiler/readsIn.ts +62 -8
  36. package/src/compiler/renderMarkup.ts +3 -3
  37. package/src/compiler/scaffold.ts +37 -15
  38. package/src/compiler/serveLanguage.ts +101 -34
  39. package/src/compiler/sharedModuleWrites.ts +2 -2
  40. package/src/compiler/sourceMapOf.ts +193 -0
  41. package/src/compiler/splitImports.ts +205 -83
  42. package/src/compiler/tokenize.ts +14 -0
  43. package/src/compiler/writeDefaultProject.ts +29 -2
  44. package/src/server/CLIENT_BUILD.ts +11 -13
  45. package/src/server/DEFAULT_PORT.ts +1 -1
  46. package/src/server/MODULE_QUERIES.ts +27 -0
  47. package/src/server/TABLES.ts +7 -0
  48. package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
  49. package/src/server/abideModules.ts +457 -0
  50. package/src/server/answerFailure.ts +16 -6
  51. package/src/server/answerRequest.ts +241 -107
  52. package/src/server/answerRpc.ts +107 -33
  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 +238 -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 +38 -37
  65. package/src/server/componentModules.d.ts +2 -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 +77 -79
  70. package/src/server/devWorker.ts +118 -181
  71. package/src/server/executableName.ts +4 -0
  72. package/src/server/fenceServer.ts +57 -32
  73. package/src/server/generatedSurfaces.ts +99 -66
  74. package/src/server/hooks.ts +137 -52
  75. package/src/server/iconAccent.ts +191 -0
  76. package/src/server/index.ts +12 -7
  77. package/src/server/installSourceMaps.ts +93 -0
  78. package/src/server/launcherOptions.ts +33 -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 +6 -6
  83. package/src/server/negotiateEncoding.ts +1 -1
  84. package/src/server/preload.ts +13 -0
  85. package/src/server/principal.ts +10 -5
  86. package/src/server/realOrAsGiven.ts +10 -0
  87. package/src/server/refuseAddressCollision.ts +21 -14
  88. package/src/server/remote.ts +49 -52
  89. package/src/server/render.ts +65 -94
  90. package/src/server/renderComponent.ts +5 -0
  91. package/src/server/reportHealth.ts +2 -2
  92. package/src/server/requestScope.ts +12 -1
  93. package/src/server/runBuild.ts +24 -0
  94. package/src/server/runCompiled.ts +5 -5
  95. package/src/server/runDesktop.ts +48 -28
  96. package/src/server/serveBuild.ts +43 -52
  97. package/src/server/serveDev.ts +79 -37
  98. package/src/server/shell.ts +7 -22
  99. package/src/server/sourceMappings.ts +139 -0
  100. package/src/server/surfaceOf.ts +20 -3
  101. package/src/server/testBuild.ts +1 -19
  102. package/src/server/withAbideSources.ts +117 -0
  103. package/src/shared/ADDRESSES.ts +10 -18
  104. package/src/shared/APP_META.ts +7 -0
  105. package/src/shared/CopyOptions.ts +1 -1
  106. package/src/shared/DEBUG_META.ts +1 -1
  107. package/src/shared/LAUNCHER_FILES.ts +9 -0
  108. package/src/shared/RESERVED_DIRECTORIES.ts +24 -0
  109. package/src/shared/cookies.ts +2 -2
  110. package/src/shared/csp.ts +1 -1
  111. package/src/shared/escapeText.ts +1 -1
  112. package/src/shared/filledPath.ts +85 -0
  113. package/src/shared/framing.ts +79 -6
  114. package/src/shared/headKeyOf.ts +2 -0
  115. package/src/shared/index.ts +2 -1
  116. package/src/shared/log.ts +27 -13
  117. package/src/shared/matchRoute.ts +23 -4
  118. package/src/{ui → shared}/menuBridge.ts +2 -2
  119. package/src/shared/menuItem.ts +4 -4
  120. package/src/shared/onMenu.ts +58 -0
  121. package/src/shared/reactive/Keyed.ts +2 -2
  122. package/src/shared/reactive/NotLanded.ts +1 -1
  123. package/src/shared/reactive/Reactive.ts +43 -43
  124. package/src/shared/reactive/argsContract.ts +4 -4
  125. package/src/shared/reactive/canonicalKey.ts +4 -4
  126. package/src/shared/reactive/chunksOfStream.ts +6 -0
  127. package/src/shared/reactive/index.ts +1 -1
  128. package/src/shared/reactive/recordWrites.ts +103 -5
  129. package/src/shared/reactive/structural.ts +1 -1
  130. package/src/shared/reactive/tracking.ts +1 -1
  131. package/src/shared/reactive/watch.ts +3 -3
  132. package/src/shared/refuse.ts +1 -1
  133. package/src/{ui → shared}/reportMenu.ts +8 -10
  134. package/src/shared/routePattern.ts +122 -48
  135. package/src/shared/rpc.ts +37 -8
  136. package/src/shared/rpcRequest.ts +27 -3
  137. package/src/shared/url.ts +4 -44
  138. package/src/shared/writePath.ts +1 -1
  139. package/src/ui/LAYER_NAMES.ts +1 -1
  140. package/src/ui/List.ts +3 -3
  141. package/src/ui/MOUNTING_LEVEL.ts +18 -0
  142. package/src/ui/Mismatch.ts +1 -1
  143. package/src/ui/RawRun.ts +1 -1
  144. package/src/ui/Router.ts +37 -36
  145. package/src/ui/Scope.ts +1 -1
  146. package/src/ui/TryRegion.ts +2 -2
  147. package/src/ui/applyHead.ts +33 -0
  148. package/src/ui/index.ts +1 -1
  149. package/src/ui/moveNode.ts +1 -1
  150. package/src/ui/reconcile.ts +2 -2
  151. package/src/ui/routing.ts +2 -2
  152. package/src/ui/setAttribute.ts +1 -1
  153. package/src/ui/setText.ts +1 -1
  154. package/src/ui/socketWire.ts +9 -7
  155. package/src/ui/wire.ts +1 -1
  156. package/scaffold/src/server/app.ts +0 -6
  157. package/scaffold/src/shared/.gitkeep +0 -0
  158. package/scaffold/src/ui/app.html +0 -8
  159. package/src/server/componentImports.ts +0 -56
  160. /package/scaffold/src/{ui/pages/page.abide → index.abide} +0 -0
  161. /package/src/{compiler → shared}/SPECIFIERS.ts +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,12 +28,19 @@ 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>()
29
35
  for (const [entry, path] of Object.entries(abidePackage.exports)) {
30
- if (entry === './compiler' || !path.endsWith('.ts')) continue
36
+ // The compiler, and the preload a test run builds the app with (38.109), are no part of an
37
+ // app that is already built.
38
+ if (
39
+ entry === './compiler' ||
40
+ entry === './test' ||
41
+ !path.endsWith('.ts')
42
+ )
43
+ continue
31
44
  specifiers.set(
32
45
  entry === '.'
33
46
  ? abidePackage.name
@@ -41,8 +54,8 @@ function abideEntries(): Map<string, string> {
41
54
  /**
42
55
  * The source of the module a compiled app starts from, written into the build beside `server.ts`.
43
56
  *
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
57
+ * 38.10 — every part `runCompiled` boots over, named by a static import the bundler follows into
58
+ * the executable. 38.44 — NOTHING IN IT IS FOUND AT RUN TIME — a file this module does not name is
46
59
  * a file the executable does not hold, which is what makes the artifact a property of the build
47
60
  * rather than of the machine it lands on.
48
61
  *
@@ -52,24 +65,23 @@ function abideEntries(): Map<string, string> {
52
65
  */
53
66
  export function compiledEntry(app: {
54
67
  /**
55
- * 38.71 — what `abide bundle` read out of `src/bundle/`, and `launch` the path of its
56
- * `launch.abide`; `undefined` for `abide compile`.
68
+ * 38.70 — what `abide bundle` read of the launcher's files: `launch` the path of its
69
+ * `launcher.abide` where that file has markup of its own; `undefined` for `abide compile`.
57
70
  */
58
71
  desktop:
59
72
  | {
60
- options: LauncherOptions
61
73
  launch: string | undefined
62
- shell: string
74
+ document: string
75
+ icon: string | undefined
76
+ accent: string | undefined
63
77
  }
64
78
  | undefined
65
- out: string
66
- /** 37.1 — `src/server/app.ts`, where the app has one. */
67
- hooks: string | undefined
79
+ /** The app's root, below which the server's table is served (`abideModules`). */
80
+ root: string
81
+ /** 38.97 — each server module's map, by the `.abide` it is served at. */
82
+ sourceMaps: SourceMaps
68
83
  package: AppPackage
69
- shell: string
70
84
  manifest: unknown
71
- /** 16.2 — each handler's source path as its row names it, to the file. */
72
- handlerModules: Map<string, string>
73
85
  /** Every file under the build's `client/`, by name to the file. */
74
86
  client: Map<string, string>
75
87
  /** Every file under the build's `public/`, by name to the file. */
@@ -77,8 +89,8 @@ export function compiledEntry(app: {
77
89
  }): string {
78
90
  const runner = RUNNERS[app.desktop === undefined ? 'compiled' : 'desktop']
79
91
  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`)}
92
+ import { installSourceMaps } from ${JSON.stringify(INSTALL_SOURCE_MAPS)}
93
+ import * as table from ${JSON.stringify(tablePath(app.root, SERVER_TABLE))}
82
94
  `
83
95
  const files = (kind: string, paths: Map<string, string>): string => {
84
96
  let entries = ''
@@ -92,49 +104,45 @@ import { handlers } from ${JSON.stringify(`${app.out}/rpc.ts`)}
92
104
  }
93
105
  const client = files('client', app.client)
94
106
  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
107
  let specifiers = ''
100
108
  for (const [specifier, path] of abideEntries())
101
109
  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
110
  const desktop =
113
111
  app.desktop === undefined
114
112
  ? ''
115
113
  : `
116
114
  {
117
- options: ${JSON.stringify(app.desktop.options)},
118
115
  launch: ${app.desktop.launch === undefined ? 'undefined' : `() => import(${JSON.stringify(app.desktop.launch)})`},
119
- shell: ${JSON.stringify(app.desktop.shell)},
116
+ document: ${JSON.stringify(app.desktop.document)},
117
+ icon: ${JSON.stringify(app.desktop.icon)},
118
+ accent: ${JSON.stringify(app.desktop.accent)},
120
119
  },`
121
120
  return `// Written by \`abide compile\`. The whole app, as what one executable holds.
122
121
  ${imports}
123
- const handlerModules = {${modules}
124
- }
122
+ // 38.56 — a file declaring handlers as a script names it through the app's one alias:
123
+ // \`#src/invoices/getInvoice.ts\`, to the module it is.
124
+ const declared = {}
125
+ for (const row of table.handlers) declared[\`#src/\${row.source}\`] = row.load
126
+
127
+ // 38.97 — before anything runs, a script \`run\` included: the executable's frames name each
128
+ // \`.abide\` as it was bundled.
129
+ installSourceMaps(${JSON.stringify(app.sourceMaps)}, undefined)
125
130
 
126
131
  await ${runner.name}(
127
- { hooks: ${hooks}, package: ${JSON.stringify(app.package)} },
132
+ // 37.1 — every server block, imported before the boot.
133
+ {
134
+ hooks: () => Promise.all(table.servers.map((load) => load())),
135
+ package: ${JSON.stringify(app.package)},
136
+ },
128
137
  {
129
138
  table,
130
- handlers,
131
- handlerModule: (source) => handlerModules[source](),
132
- shell: ${JSON.stringify(app.shell)},
139
+ handlers: table.handlers,
133
140
  manifest: ${JSON.stringify(app.manifest)},
134
141
  client: ${client},
135
142
  published: ${published},
136
143
  },
137
144
  {${specifiers}
145
+ ...declared,
138
146
  },${desktop}
139
147
  )
140
148
  `
@@ -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,53 @@ 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.24).
93
+ const relative = relativeTo(root, source.path)
94
+ const position = source.position ?? relative
95
+ // 34.20 — a server block of the app's own under a `server` or `public` directory, refused
96
+ // here before anything reads its handlers: neither directory holds a component that runs.
97
+ const place = placeOf(position)
98
+ if (
99
+ source.file.servers.length > 0 &&
100
+ (place === 'server' || place === 'public')
101
+ )
102
+ throw new Error(
103
+ `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.20.`,
104
+ )
105
+ // 34.21 — a file imported as text is found at build, not when its module first loads.
106
+ for (const script of scriptBodies(source.file))
107
+ for (const { specifier } of attributedImports(script, 'text'))
108
+ if (resolve(specifier, dirname(source.path)) === undefined)
109
+ throw new Error(
110
+ `abide: ${relative} imports ${specifier} as text, and there is no such file. RULEBOOK 34.21.`,
111
+ )
112
+ const kind = fileKind(position)
114
113
  nodes.push({
115
114
  ...source,
116
115
  relative,
116
+ position,
117
117
  kind,
118
118
  route:
119
119
  kind === 'page' || kind === 'error'
120
- ? routeOf(relative)
120
+ ? routeOf(position)
121
121
  : undefined,
122
122
  })
123
123
  }
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 }
124
+ return { nodes, invoked: componentGraph(sources, resolve).invoked }
139
125
  }
@@ -49,7 +49,7 @@ import type { Diagnosis } from './TypeSession.ts'
49
49
  /**
50
50
  * Every value each module exports, with the abide method that declares it.
51
51
  *
52
- * 19.15 — EVERY VALUE EACH MODULE EXPORTS, and the method of the abide call that declares it, or
52
+ * 16.73 — EVERY VALUE EACH MODULE EXPORTS, and the method of the abide call that declares it, or
53
53
  * `undefined` where it is not one. The checker's own export table, so a name exported apart from
54
54
  * its declaration, under another name or from another module is the same export it is to an
55
55
  * importer; and the callee's own symbol, so `GET` imported under another name or through a
@@ -75,7 +75,7 @@ export async function declaredExports(
75
75
  const sourceFile = await project.program.getSourceFile(path)
76
76
  if (sourceFile === undefined)
77
77
  throw new Error(
78
- `abide: ${path} is not inside the project ${projectName}, so what it exports cannot be read (19.15). Give the app a tsconfig whose include reaches it.`,
78
+ `abide: ${path} is not inside the project ${projectName}, so what it exports cannot be read (16.73). Give the app a tsconfig whose include reaches it.`,
79
79
  )
80
80
  const module = await checker.getSymbolAtLocation(sourceFile)
81
81
  if (module === undefined) return []
@@ -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
+ // 16.73 — 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[] },