@abide/abide 0.103.0 → 0.105.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 +29 -0
- package/bin/abide.ts +80 -173
- package/docs/REGISTRY.md +27 -27
- package/docs/RULEBOOK.md +68 -24
- package/docs/abide.md +31 -46
- package/package.json +1 -1
- package/src/compiler/Lowering.ts +15 -5
- package/src/compiler/Refusals.ts +57 -0
- package/src/compiler/TypeSession.ts +46 -89
- package/src/compiler/appLayout.ts +447 -0
- package/src/compiler/appMap.ts +21 -25
- package/src/compiler/checkApp.ts +61 -97
- package/src/compiler/checkedShadow.ts +12 -10
- package/src/compiler/classifyRead.ts +3 -6
- package/src/compiler/compileApp.ts +183 -478
- package/src/compiler/componentGraph.ts +2 -71
- package/src/compiler/declaredExports.ts +79 -60
- package/src/compiler/emitClient.ts +11 -13
- package/src/compiler/emitComponent.ts +21 -6
- package/src/compiler/emitSource.ts +20 -20
- package/src/compiler/handlersUnder.ts +46 -36
- package/src/compiler/hookCalls.ts +152 -0
- package/src/compiler/mountsOf.ts +31 -54
- package/src/compiler/projectShadow.ts +1 -1
- package/src/compiler/readSources.ts +27 -42
- package/src/compiler/readsIn.ts +5 -16
- package/src/compiler/resolverIn.ts +77 -0
- package/src/server/abideModules.ts +152 -146
- package/src/server/answerRequest.ts +18 -4
- package/src/server/answerSocket.ts +136 -76
- package/src/server/answerValue.ts +28 -15
- package/src/server/appTree.ts +59 -73
- package/src/server/claudeApi.ts +2 -5
- package/src/server/clientBundle.ts +198 -0
- package/src/server/coerceArgs.ts +1 -1
- package/src/server/commands.ts +93 -57
- package/src/server/createApp.ts +90 -16
- package/src/server/deferredBuilds.ts +175 -0
- package/src/server/devWorker.ts +107 -319
- package/src/server/faceOf.ts +120 -0
- package/src/server/fenceServer.ts +10 -18
- package/src/server/generatedSurfaces.ts +12 -53
- package/src/server/index.ts +3 -0
- package/src/server/placement.ts +167 -0
- package/src/server/remote.ts +14 -42
- package/src/server/render.ts +25 -17
- package/src/server/renderComponent.ts +9 -5
- package/src/server/requestScope.ts +10 -4
- package/src/server/responseRelay.ts +389 -0
- package/src/server/runCompiled.ts +9 -12
- package/src/server/runDesktop.ts +6 -9
- package/src/server/serveBuild.ts +26 -73
- package/src/server/serveDev.ts +19 -176
- package/src/server/streamEvent.ts +29 -0
- package/src/server/versionOf.ts +1 -2
- package/src/server/withAbideSources.ts +2 -1
- package/src/shared/HandlerRoute.ts +68 -0
- package/src/shared/REFUSAL_KEY.ts +5 -0
- package/src/shared/RESERVED_DIRECTORIES.ts +6 -0
- package/src/shared/answerOf.ts +105 -78
- package/src/shared/filledPath.ts +1 -9
- package/src/shared/framing.ts +127 -128
- package/src/shared/queryText.ts +15 -7
- package/src/shared/reactive/Cursor.ts +69 -124
- package/src/shared/reactive/Reactive.ts +296 -162
- package/src/shared/reactive/callLoad.ts +4 -18
- package/src/shared/reactive/index.ts +11 -8
- package/src/shared/reactive/pairReactive.ts +1 -1
- package/src/shared/reactive/scoped.ts +4 -3
- package/src/shared/reactive/share.ts +11 -1
- package/src/shared/routePattern.ts +0 -110
- package/src/shared/rpc.ts +17 -24
- package/src/shared/rpcRequest.ts +18 -47
- package/src/shared/takeEarlyCall.ts +12 -37
- package/src/shared/url.ts +1 -1
- package/src/ui/Level.ts +276 -0
- package/src/ui/List.ts +17 -17
- package/src/ui/Router.ts +20 -87
- package/src/ui/applyHead.ts +92 -79
- package/src/ui/index.ts +4 -2
- package/src/ui/reconcile.ts +4 -110
- package/src/ui/rows.ts +123 -2
- package/src/ui/wire.ts +12 -27
- package/src/compiler/hooksCalled.ts +0 -35
- package/src/server/clientBuildOptions.ts +0 -54
- package/src/server/refuseAddressCollision.ts +0 -37
- package/src/shared/reactive/chunksOfStream.ts +0 -57
- package/src/ui/MOUNTING_LEVEL.ts +0 -18
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
SignatureKind,
|
|
12
12
|
SymbolFlags,
|
|
13
13
|
} from 'typescript/unstable/async'
|
|
14
|
-
import {
|
|
15
|
-
import { fileKind, placeOf, routeOf } from '#shared/routePattern.ts'
|
|
14
|
+
import { placeOf } from '#server/placement.ts'
|
|
16
15
|
import { aliasedSymbol } from './aliasedSymbol.ts'
|
|
17
16
|
import { AMBIENT_SOURCES, ambientReadsIn } from './ambientReadsIn.ts'
|
|
17
|
+
import type { AppLayout, PlacedSource } from './appLayout.ts'
|
|
18
18
|
import { argsDefaultsIn } from './argsDefaultsIn.ts'
|
|
19
19
|
import { type CheckerAnswer, UNCHECKED } from './CheckerAnswer.ts'
|
|
20
20
|
import {
|
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
import type { Finding } from './Finding.ts'
|
|
27
27
|
import { CHECKED_VALUES } from './Lowering.ts'
|
|
28
28
|
import { diagnosisAt, lineOf } from './lineOf.ts'
|
|
29
|
-
import { mountedUnder } from './mountsOf.ts'
|
|
30
29
|
import type { AbideFile } from './parseAbide.ts'
|
|
31
30
|
import { projectShadow } from './projectShadow.ts'
|
|
32
31
|
import { abideIn } from './reactiveTypes.ts'
|
|
@@ -65,18 +64,6 @@ export class TypeSession {
|
|
|
65
64
|
* text's offsets.
|
|
66
65
|
*/
|
|
67
66
|
private shadows = new Map<string, { path: string; text: string }>()
|
|
68
|
-
/**
|
|
69
|
-
* What `diagnose` last read off one walk of the root: the route table it declared (30.26) and the
|
|
70
|
-
* modules it read for their `onConfig` (27.13). Kept until `edited` hears anything move, so a
|
|
71
|
-
* check asked again and again while an editor types walks and reads nothing on disk.
|
|
72
|
-
*/
|
|
73
|
-
private walked:
|
|
74
|
-
| {
|
|
75
|
-
within: string
|
|
76
|
-
routes: string
|
|
77
|
-
configModules: Promise<[string, string][]>
|
|
78
|
-
}
|
|
79
|
-
| undefined = undefined
|
|
80
67
|
/** The checker's own key for a path: resolved, and case-folded where its host folds case. */
|
|
81
68
|
private keyOf: (path: string) => string
|
|
82
69
|
/**
|
|
@@ -358,7 +345,6 @@ export class TypeSession {
|
|
|
358
345
|
* checker is not told what was under it.
|
|
359
346
|
*/
|
|
360
347
|
edited(paths: string[] | undefined): void {
|
|
361
|
-
this.walked = undefined
|
|
362
348
|
if (paths === undefined) {
|
|
363
349
|
this.everything = true
|
|
364
350
|
return
|
|
@@ -447,10 +433,12 @@ export class TypeSession {
|
|
|
447
433
|
}
|
|
448
434
|
|
|
449
435
|
/**
|
|
450
|
-
* 34.25, 23.19 —
|
|
451
|
-
* `changed` the config moved where it was not one
|
|
436
|
+
* 34.25, 23.19 — where `source` is a dependency's file, makes `path` — it, or what it lowers to
|
|
437
|
+
* — a root of the opened config, telling `changed` the config moved where it was not one
|
|
438
|
+
* already. The app's own is a root already: telling the config it moved reloads the project.
|
|
452
439
|
*/
|
|
453
|
-
private openOutside(path: string, changed: string[]): void {
|
|
440
|
+
private openOutside(source: string, path: string, changed: string[]): void {
|
|
441
|
+
if (placeOf(this.belowRoot(source)) !== 'dependency') return
|
|
454
442
|
if (this.outside.has(path)) return
|
|
455
443
|
this.outside.add(path)
|
|
456
444
|
if (!changed.includes(this.opened)) changed.push(this.opened)
|
|
@@ -472,9 +460,7 @@ export class TypeSession {
|
|
|
472
460
|
const changed: string[] = []
|
|
473
461
|
for (const path of modules) {
|
|
474
462
|
const canonical = this.canonical(path)
|
|
475
|
-
|
|
476
|
-
if (placeOf(this.belowRoot(canonical)) === 'dependency')
|
|
477
|
-
this.openOutside(canonical, changed)
|
|
463
|
+
this.openOutside(canonical, canonical, changed)
|
|
478
464
|
}
|
|
479
465
|
const project = await this.currentProject([], changed)
|
|
480
466
|
if (project === undefined) return project
|
|
@@ -574,7 +560,6 @@ export class TypeSession {
|
|
|
574
560
|
async readsFor(
|
|
575
561
|
sources: readonly (Pick<ParsedSource, 'path' | 'file'> & {
|
|
576
562
|
text?: string
|
|
577
|
-
position?: string | undefined
|
|
578
563
|
})[],
|
|
579
564
|
): Promise<{ answers: CheckerAnswer[]; warnings: Diagnosis[] }> {
|
|
580
565
|
const answered = await this.classify(sources)
|
|
@@ -597,7 +582,6 @@ export class TypeSession {
|
|
|
597
582
|
private async classify(
|
|
598
583
|
sources: readonly (Pick<ParsedSource, 'path' | 'file'> & {
|
|
599
584
|
text?: string
|
|
600
|
-
position?: string | undefined
|
|
601
585
|
})[],
|
|
602
586
|
): Promise<{ answers: CheckerAnswer[]; warnings: Diagnosis[] }> {
|
|
603
587
|
const warnings: Diagnosis[] = []
|
|
@@ -646,8 +630,7 @@ export class TypeSession {
|
|
|
646
630
|
// AN UNCHANGED SHADOW IS NOT REPORTED, which is what a kept session saves on a save:
|
|
647
631
|
// the one page that moved is re-read rather than all of them.
|
|
648
632
|
this.register(map.shadowPath, projection.text, created, changed)
|
|
649
|
-
|
|
650
|
-
this.openOutside(map.shadowPath, changed)
|
|
633
|
+
this.openOutside(source.path, map.shadowPath, changed)
|
|
651
634
|
queries.push({
|
|
652
635
|
map,
|
|
653
636
|
spans: {
|
|
@@ -690,8 +673,6 @@ export class TypeSession {
|
|
|
690
673
|
sourceFile,
|
|
691
674
|
query.map.shadowPath,
|
|
692
675
|
query.spans,
|
|
693
|
-
// 23.19 — a mounted page's route is its position's.
|
|
694
|
-
source.position ?? this.belowRoot(source.path),
|
|
695
676
|
source.file,
|
|
696
677
|
)
|
|
697
678
|
answers[at] = read.answer
|
|
@@ -812,22 +793,27 @@ export class TypeSession {
|
|
|
812
793
|
* 38.3 — WHAT THE CHECKER SAYS ABOUT EACH FILE AS IT RUNS, placed on the `.abide` line. Each
|
|
813
794
|
* source is checked as its lowered program (`checkedShadow`), which is registered for the life
|
|
814
795
|
* of the session beside its shadow for the reason a shadow is; and every `.ts` root of the
|
|
815
|
-
* project under
|
|
796
|
+
* project under the app's root is checked as it stands, where `scripts` asks for them — an editor
|
|
816
797
|
* asking about one page does not want the whole app. `sources` carry their text, which is what
|
|
817
798
|
* a position is a line of. A `quiet` source is lowered and not reported on: a component an
|
|
818
799
|
* editor's page imports, which is there for the props the page is held to (33.4).
|
|
819
800
|
*
|
|
801
|
+
* `app` is the app the sources are checked in (`appLayout`): its route table, which `url`'s
|
|
802
|
+
* literal is held to (30.26), and its handler files, which type `Config` (27.13). A source placed
|
|
803
|
+
* as a page or a layout carries its `kind` and `route`, which its `route.params` is typed by (23.9).
|
|
804
|
+
*
|
|
820
805
|
* `refused` is the path of every source whose checked lowering refused, which `diagnoses` has
|
|
821
806
|
* reported at its line — `abide check` emits the rest of the app without them.
|
|
822
807
|
*/
|
|
823
808
|
async diagnose(
|
|
824
|
-
sources: (Omit<ReadSource, 'quiet'
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
809
|
+
sources: (Omit<ReadSource, 'quiet'> &
|
|
810
|
+
Partial<Pick<PlacedSource, 'kind' | 'route'>> & {
|
|
811
|
+
quiet?: boolean
|
|
812
|
+
})[],
|
|
813
|
+
app: Pick<AppLayout, 'root' | 'addresses' | 'handlers'>,
|
|
829
814
|
scripts = true,
|
|
830
815
|
): Promise<{ diagnoses: Diagnosis[]; refused: Set<string> }> {
|
|
816
|
+
const within = app.root
|
|
831
817
|
const created: string[] = []
|
|
832
818
|
const changed: string[] = []
|
|
833
819
|
const checked: {
|
|
@@ -836,13 +822,24 @@ export class TypeSession {
|
|
|
836
822
|
refusals: readonly Finding[]
|
|
837
823
|
}[] = []
|
|
838
824
|
for (const source of sources) {
|
|
839
|
-
const shadow = checkedShadow(
|
|
825
|
+
const shadow = checkedShadow(
|
|
826
|
+
source.file,
|
|
827
|
+
source.answer,
|
|
828
|
+
(source.kind === 'page' || source.kind === 'layout') &&
|
|
829
|
+
source.route !== undefined
|
|
830
|
+
? { route: source.route, kind: source.kind }
|
|
831
|
+
: undefined,
|
|
832
|
+
)
|
|
840
833
|
const map = new ShadowMap(
|
|
841
834
|
this.canonical(source.path),
|
|
842
835
|
projectShadow(source.file).origins,
|
|
843
836
|
shadow.origins,
|
|
844
837
|
)
|
|
845
838
|
this.register(map.checkedPath, shadow.text, created, changed)
|
|
839
|
+
// 23.19 — a package's page an editor opens is reported on, so its checked module is a
|
|
840
|
+
// root of the project as its shadow is (`classify`).
|
|
841
|
+
if (!source.quiet)
|
|
842
|
+
this.openOutside(source.path, map.checkedPath, changed)
|
|
846
843
|
checked.push({ source, map, refusals: shadow.refusals })
|
|
847
844
|
}
|
|
848
845
|
// 34.6 — WHAT THE BUNDLER RESOLVES AND THE CHECKER CANNOT: a stylesheet imported for its
|
|
@@ -854,30 +851,23 @@ export class TypeSession {
|
|
|
854
851
|
created,
|
|
855
852
|
changed,
|
|
856
853
|
)
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
configModules: configModulesUnder(within, files),
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
// 27.13 — what a handler file or a server module defaults, beside it.
|
|
867
|
-
for (const [path, text] of await this.walked.configModules) {
|
|
868
|
-
const shadow = moduleConfigShadow(text)
|
|
869
|
-
const at = `${this.canonical(path)}${CONFIG_SUFFIX}`
|
|
854
|
+
// 27.13 — what a handler file defaults, beside it.
|
|
855
|
+
for (const handler of app.handlers) {
|
|
856
|
+
const at = `${this.canonical(handler.path)}${CONFIG_SUFFIX}`
|
|
857
|
+
const shadow = handler.hooks.includes('onConfig')
|
|
858
|
+
? moduleConfigShadow(handler.text)
|
|
859
|
+
: undefined
|
|
870
860
|
// One that stops filing keeps its place and declares nothing, so the next snapshot is
|
|
871
861
|
// told it moved.
|
|
872
862
|
if (shadow !== undefined || this.shadows.has(this.keyOf(at)))
|
|
873
863
|
this.register(at, shadow ?? 'export {}\n', created, changed)
|
|
874
864
|
}
|
|
875
|
-
// 30.26 — THE APP'S ROUTE TABLE, which `url`'s literal is held to: every page
|
|
876
|
-
//
|
|
865
|
+
// 30.26 — THE APP'S ROUTE TABLE, which `url`'s literal is held to: every page the build
|
|
866
|
+
// routes, declared into abide's own `Routes`. A module rather than a script, since only a
|
|
877
867
|
// module augments one.
|
|
878
868
|
this.register(
|
|
879
869
|
`${this.canonical(within)}/${ROUTES_DECLARATION}`,
|
|
880
|
-
|
|
870
|
+
routesDeclaration(app.addresses),
|
|
881
871
|
created,
|
|
882
872
|
changed,
|
|
883
873
|
)
|
|
@@ -1056,7 +1046,7 @@ export class TypeSession {
|
|
|
1056
1046
|
const named = (node as { name?: Node }).name ?? node
|
|
1057
1047
|
const file = node.getSourceFile()
|
|
1058
1048
|
definitions.push(
|
|
1059
|
-
this.
|
|
1049
|
+
this.spanInSource(
|
|
1060
1050
|
file.fileName,
|
|
1061
1051
|
file.text,
|
|
1062
1052
|
named.getStart(file),
|
|
@@ -1078,7 +1068,7 @@ export class TypeSession {
|
|
|
1078
1068
|
* back through both of its maps, and one this session has not lowered — or a span of the
|
|
1079
1069
|
* scaffolding a lowering adds, which the author never wrote — placed at its top.
|
|
1080
1070
|
*/
|
|
1081
|
-
private
|
|
1071
|
+
private spanInSource(
|
|
1082
1072
|
fileName: string,
|
|
1083
1073
|
text: string,
|
|
1084
1074
|
start: number,
|
|
@@ -1153,23 +1143,10 @@ function isFile(path: string): boolean {
|
|
|
1153
1143
|
const ROUTES_DECLARATION = 'abide-routes.d.ts'
|
|
1154
1144
|
const URL_SOURCE = Bun.fileURLToPath(new URL('../shared/url', import.meta.url))
|
|
1155
1145
|
|
|
1156
|
-
/**
|
|
1157
|
-
|
|
1158
|
-
* nothing where it has neither.
|
|
1159
|
-
*/
|
|
1160
|
-
function routesDeclaration(
|
|
1161
|
-
root: string,
|
|
1162
|
-
{ abide, published }: ReturnType<typeof appFiles>,
|
|
1163
|
-
): string {
|
|
1164
|
-
const routes = new Set<string>()
|
|
1165
|
-
for (const path of abide)
|
|
1166
|
-
if (fileKind(path) === 'page') routes.add(routeOf(path))
|
|
1167
|
-
// 23.19 — and every page a mount places in the app, at its position.
|
|
1168
|
-
for (const { position } of mountedUnder(root, abide))
|
|
1169
|
-
if (fileKind(position) === 'page') routes.add(routeOf(position))
|
|
1170
|
-
for (const address of published.values()) routes.add(address)
|
|
1146
|
+
/** 30.26 — each of `addresses` as a key of abide's `Routes`. */
|
|
1147
|
+
function routesDeclaration(addresses: readonly string[]): string {
|
|
1171
1148
|
let keys = ''
|
|
1172
|
-
for (const route of
|
|
1149
|
+
for (const route of addresses)
|
|
1173
1150
|
keys += ` ${JSON.stringify(route)}: true\n`
|
|
1174
1151
|
return `export {}\ndeclare module ${JSON.stringify(URL_SOURCE)} {\n interface Routes {\n${keys} }\n}\n`
|
|
1175
1152
|
}
|
|
@@ -1327,23 +1304,3 @@ function flatten(diagnostic: {
|
|
|
1327
1304
|
|
|
1328
1305
|
/** What a module that is no `.abide` is checked beside as, for the `Config` it files (27.13). */
|
|
1329
1306
|
const CONFIG_SUFFIX = '.config-shadow.ts'
|
|
1330
|
-
|
|
1331
|
-
/**
|
|
1332
|
-
* Every handler file and every module under a `server` directory below `within` that names
|
|
1333
|
-
* `onConfig`, with its text — the modules a server runs that 27.13 types `Config` off.
|
|
1334
|
-
*/
|
|
1335
|
-
async function configModulesUnder(
|
|
1336
|
-
within: string,
|
|
1337
|
-
{ handlers, modules }: ReturnType<typeof appFiles>,
|
|
1338
|
-
): Promise<[string, string][]> {
|
|
1339
|
-
const paths: string[] = []
|
|
1340
|
-
for (const name of handlers) paths.push(`${within}/${name}`)
|
|
1341
|
-
for (const name of modules)
|
|
1342
|
-
if (placeOf(name) === 'server') paths.push(`${within}/${name}`)
|
|
1343
|
-
const texts = await Promise.all(paths.map((path) => Bun.file(path).text()))
|
|
1344
|
-
const found: [string, string][] = []
|
|
1345
|
-
for (let at = 0; at < paths.length; at += 1)
|
|
1346
|
-
if ((texts[at] as string).includes('onConfig'))
|
|
1347
|
-
found.push([paths[at] as string, texts[at] as string])
|
|
1348
|
-
return found
|
|
1349
|
-
}
|