@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.
- package/CHANGELOG.md +26 -0
- package/bin/abide.ts +226 -164
- package/docs/REGISTRY.md +730 -0
- package/docs/RULEBOOK.md +2768 -0
- package/docs/abide.md +13127 -0
- package/package.json +6 -3
- package/scaffold/AGENTS.md +38 -0
- package/scaffold/CLAUDE.md +1 -0
- package/scaffold/src/layout.abide +18 -0
- package/src/compiler/Lowering.ts +78 -9
- package/src/compiler/ShadowMap.ts +2 -2
- package/src/compiler/TypeSession.ts +341 -71
- package/src/compiler/appMap.ts +96 -0
- package/src/compiler/argsSchemaOf.ts +1 -1
- package/src/compiler/checkApp.ts +53 -8
- package/src/compiler/checkedShadow.ts +73 -6
- package/src/compiler/classifyRead.ts +6 -4
- package/src/compiler/classifyTemplate.ts +1 -1
- package/src/compiler/compileApp.ts +598 -200
- package/src/compiler/compiledEntry.ts +48 -40
- package/src/compiler/componentGraph.ts +57 -71
- package/src/compiler/declaredExports.ts +26 -9
- package/src/compiler/emitClient.ts +142 -72
- package/src/compiler/emitComponent.ts +41 -12
- package/src/compiler/emitSource.ts +102 -13
- package/src/compiler/handlersUnder.ts +164 -76
- package/src/compiler/hooksCalled.ts +35 -0
- package/src/compiler/index.ts +7 -0
- package/src/compiler/mountsOf.ts +150 -0
- package/src/compiler/nearest.ts +14 -0
- package/src/compiler/parseAbide.ts +157 -20
- package/src/compiler/projectShadow.ts +250 -19
- package/src/compiler/reactiveTypes.ts +20 -7
- package/src/compiler/readSources.ts +15 -2
- package/src/compiler/readsIn.ts +62 -8
- package/src/compiler/renderMarkup.ts +3 -3
- package/src/compiler/scaffold.ts +37 -15
- package/src/compiler/serveLanguage.ts +101 -34
- package/src/compiler/sharedModuleWrites.ts +2 -2
- package/src/compiler/sourceMapOf.ts +193 -0
- package/src/compiler/splitImports.ts +205 -83
- package/src/compiler/tokenize.ts +14 -0
- package/src/compiler/writeDefaultProject.ts +29 -2
- package/src/server/CLIENT_BUILD.ts +11 -13
- package/src/server/DEFAULT_PORT.ts +1 -1
- package/src/server/MODULE_QUERIES.ts +27 -0
- package/src/server/TABLES.ts +7 -0
- package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
- package/src/server/abideModules.ts +457 -0
- package/src/server/answerFailure.ts +16 -6
- package/src/server/answerRequest.ts +241 -107
- package/src/server/answerRpc.ts +107 -33
- package/src/server/answerSocket.ts +65 -20
- package/src/server/answerValue.ts +22 -9
- package/src/server/appDirectories.ts +1 -1
- package/src/server/appPlugins.ts +1 -1
- package/src/server/appTree.ts +238 -0
- package/src/server/argsFor.ts +2 -2
- package/src/server/assistant.ts +2 -2
- package/src/server/bridgeRelay.ts +6 -8
- package/src/server/claudeCode.ts +0 -6
- package/src/server/clientBuildOptions.ts +8 -3
- package/src/server/coerceArgs.ts +1 -1
- package/src/server/commands.ts +38 -37
- package/src/server/componentModules.d.ts +2 -1
- package/src/server/compressAnswer.ts +4 -4
- package/src/server/config.ts +25 -29
- package/src/server/configFields.ts +31 -25
- package/src/server/createApp.ts +77 -79
- package/src/server/devWorker.ts +118 -181
- package/src/server/executableName.ts +4 -0
- package/src/server/fenceServer.ts +57 -32
- package/src/server/generatedSurfaces.ts +99 -66
- package/src/server/hooks.ts +137 -52
- package/src/server/iconAccent.ts +191 -0
- package/src/server/index.ts +12 -7
- package/src/server/installSourceMaps.ts +93 -0
- package/src/server/launcherOptions.ts +33 -31
- package/src/server/launcherPage.ts +107 -64
- package/src/server/manifestOf.ts +16 -13
- package/src/server/namesUnder.ts +2 -2
- package/src/server/nativeMenu.ts +6 -6
- package/src/server/negotiateEncoding.ts +1 -1
- package/src/server/preload.ts +13 -0
- package/src/server/principal.ts +10 -5
- package/src/server/realOrAsGiven.ts +10 -0
- package/src/server/refuseAddressCollision.ts +21 -14
- package/src/server/remote.ts +49 -52
- package/src/server/render.ts +65 -94
- package/src/server/renderComponent.ts +5 -0
- package/src/server/reportHealth.ts +2 -2
- package/src/server/requestScope.ts +12 -1
- package/src/server/runBuild.ts +24 -0
- package/src/server/runCompiled.ts +5 -5
- package/src/server/runDesktop.ts +48 -28
- package/src/server/serveBuild.ts +43 -52
- package/src/server/serveDev.ts +79 -37
- package/src/server/shell.ts +7 -22
- package/src/server/sourceMappings.ts +139 -0
- package/src/server/surfaceOf.ts +20 -3
- package/src/server/testBuild.ts +1 -19
- package/src/server/withAbideSources.ts +117 -0
- package/src/shared/ADDRESSES.ts +10 -18
- package/src/shared/APP_META.ts +7 -0
- package/src/shared/CopyOptions.ts +1 -1
- package/src/shared/DEBUG_META.ts +1 -1
- package/src/shared/LAUNCHER_FILES.ts +9 -0
- package/src/shared/RESERVED_DIRECTORIES.ts +24 -0
- package/src/shared/cookies.ts +2 -2
- package/src/shared/csp.ts +1 -1
- package/src/shared/escapeText.ts +1 -1
- package/src/shared/filledPath.ts +85 -0
- package/src/shared/framing.ts +79 -6
- package/src/shared/headKeyOf.ts +2 -0
- package/src/shared/index.ts +2 -1
- package/src/shared/log.ts +27 -13
- package/src/shared/matchRoute.ts +23 -4
- package/src/{ui → shared}/menuBridge.ts +2 -2
- package/src/shared/menuItem.ts +4 -4
- package/src/shared/onMenu.ts +58 -0
- package/src/shared/reactive/Keyed.ts +2 -2
- package/src/shared/reactive/NotLanded.ts +1 -1
- package/src/shared/reactive/Reactive.ts +43 -43
- package/src/shared/reactive/argsContract.ts +4 -4
- package/src/shared/reactive/canonicalKey.ts +4 -4
- package/src/shared/reactive/chunksOfStream.ts +6 -0
- package/src/shared/reactive/index.ts +1 -1
- package/src/shared/reactive/recordWrites.ts +103 -5
- package/src/shared/reactive/structural.ts +1 -1
- package/src/shared/reactive/tracking.ts +1 -1
- package/src/shared/reactive/watch.ts +3 -3
- package/src/shared/refuse.ts +1 -1
- package/src/{ui → shared}/reportMenu.ts +8 -10
- package/src/shared/routePattern.ts +122 -48
- package/src/shared/rpc.ts +37 -8
- package/src/shared/rpcRequest.ts +27 -3
- package/src/shared/url.ts +4 -44
- package/src/shared/writePath.ts +1 -1
- package/src/ui/LAYER_NAMES.ts +1 -1
- package/src/ui/List.ts +3 -3
- package/src/ui/MOUNTING_LEVEL.ts +18 -0
- package/src/ui/Mismatch.ts +1 -1
- package/src/ui/RawRun.ts +1 -1
- package/src/ui/Router.ts +37 -36
- package/src/ui/Scope.ts +1 -1
- package/src/ui/TryRegion.ts +2 -2
- package/src/ui/applyHead.ts +33 -0
- package/src/ui/index.ts +1 -1
- package/src/ui/moveNode.ts +1 -1
- package/src/ui/reconcile.ts +2 -2
- package/src/ui/routing.ts +2 -2
- package/src/ui/setAttribute.ts +1 -1
- package/src/ui/setText.ts +1 -1
- package/src/ui/socketWire.ts +9 -7
- package/src/ui/wire.ts +1 -1
- package/scaffold/src/server/app.ts +0 -6
- package/scaffold/src/shared/.gitkeep +0 -0
- package/scaffold/src/ui/app.html +0 -8
- package/src/server/componentImports.ts +0 -56
- /package/scaffold/src/{ui/pages/page.abide → index.abide} +0 -0
- /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 {
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
45
|
-
* the executable. 38.
|
|
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.
|
|
56
|
-
* `
|
|
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
|
-
|
|
74
|
+
document: string
|
|
75
|
+
icon: string | undefined
|
|
76
|
+
accent: string | undefined
|
|
63
77
|
}
|
|
64
78
|
| undefined
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
81
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
*
|
|
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
|
|
13
|
-
*
|
|
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`,
|
|
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
|
|
39
|
-
|
|
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 (
|
|
48
|
-
|
|
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
|
|
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
|
|
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<
|
|
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
|
-
|
|
113
|
-
|
|
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(
|
|
120
|
+
? routeOf(position)
|
|
121
121
|
: undefined,
|
|
122
122
|
})
|
|
123
123
|
}
|
|
124
|
-
|
|
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
|
-
*
|
|
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 (
|
|
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
|
-
|
|
193
|
-
|
|
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.
|
|
524
|
-
* `ui: false`
|
|
525
|
-
*
|
|
526
|
-
*
|
|
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[] },
|