@abide/abide 0.123.2 → 0.124.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/ABIDE.md +8 -5
- package/CHANGELOG.md +58 -0
- package/bin/abide.ts +36 -65
- package/docs/REGISTRY.md +45 -12
- package/docs/RULEBOOK.md +24 -14
- package/docs/abide.md +999 -459
- package/package.json +1 -1
- package/src/compiler/ARRAY_MUTATORS.ts +20 -0
- package/src/compiler/Diagnostics.ts +198 -0
- package/src/compiler/KeptChecker.ts +97 -0
- package/src/compiler/LoweredFile.ts +609 -0
- package/src/compiler/Lowering.ts +18 -1
- package/src/compiler/PositionedRefusal.ts +1 -1
- package/src/compiler/SHARED_NAMES.ts +2 -0
- package/src/compiler/ShadowHost.ts +670 -0
- package/src/compiler/ShadowMap.ts +1 -1
- package/src/compiler/TypeSession.ts +76 -1743
- package/src/compiler/appLayout.ts +28 -20
- package/src/compiler/appLints.ts +109 -0
- package/src/compiler/argsDefaultsIn.ts +1 -1
- package/src/compiler/checkApp.ts +64 -68
- package/src/compiler/checkedShadow.ts +2 -2
- package/src/compiler/classifyRead.ts +16 -1
- package/src/compiler/compileApp.ts +102 -71
- package/src/compiler/declaredExports.ts +46 -21
- package/src/compiler/diagnoseSources.ts +232 -0
- package/src/compiler/emitClient.ts +1 -1
- package/src/compiler/formatAbide.ts +7 -8
- package/src/compiler/handlersUnder.ts +32 -63
- package/src/compiler/lineOf.ts +0 -19
- package/src/compiler/matchersOf.ts +3 -3
- package/src/compiler/mountsOf.ts +3 -3
- package/src/compiler/reactiveReads.ts +235 -0
- package/src/compiler/reactiveTypes.ts +13 -0
- package/src/compiler/readSources.ts +16 -18
- package/src/compiler/readsIn.ts +181 -82
- package/src/compiler/routesDeclaration.ts +46 -0
- package/src/compiler/scaffold.ts +7 -2
- package/src/compiler/serveLanguage.ts +46 -74
- package/src/compiler/sharedModuleWrites.ts +1 -1
- package/src/compiler/splitImports.ts +2 -2
- package/src/compiler/templateWarnings.ts +1 -2
- package/src/compiler/trackingWarnings.ts +1 -1
- package/src/server/CLIENT_BUILD.ts +4 -32
- package/src/server/DesktopSession.ts +213 -0
- package/src/server/Followers.ts +399 -0
- package/src/server/MODULE_QUERIES.ts +6 -2
- package/src/server/PageRender.ts +201 -0
- package/src/server/RESUME_MILLISECONDS.ts +2 -0
- package/src/server/abideModules.ts +7 -7
- package/src/server/answerFailure.ts +3 -2
- package/src/server/answerRequest.ts +16 -369
- package/src/server/answerRpc.ts +48 -302
- package/src/server/answerSocket.ts +41 -490
- package/src/server/appBuild.ts +377 -0
- package/src/server/appKit.ts +1 -1
- package/src/server/appPlugins.ts +15 -17
- package/src/server/appTree.ts +6 -5
- package/src/server/argsContract.ts +209 -0
- package/src/server/bridgeRelay.ts +153 -65
- package/src/server/claudeCode.ts +30 -127
- package/src/server/config.ts +6 -7
- package/src/server/createApp.ts +38 -66
- package/src/server/csp.ts +1 -1
- package/src/server/deferredBuilds.ts +3 -16
- package/src/server/devWorker.ts +11 -15
- package/src/server/faceOf.ts +1 -1
- package/src/server/generatedSurfaces.ts +2 -1
- package/src/server/handlerFileMount.ts +40 -0
- package/src/server/index.ts +3 -3
- package/src/server/installServer.ts +5 -9
- package/src/server/invokeHandler.ts +230 -0
- package/src/server/mountHandlers.ts +184 -0
- package/src/server/openDesktopWindow.ts +308 -0
- package/src/server/packageDirectory.ts +14 -0
- package/src/server/preload.ts +21 -2
- package/src/server/principal.ts +5 -4
- package/src/server/remote.ts +2 -2
- package/src/server/render.ts +31 -59
- package/src/server/requestScope.ts +5 -36
- package/src/server/runCompiled.ts +1 -1
- package/src/server/runDesktop.ts +20 -448
- package/src/server/seedBuffer.ts +12 -12
- package/src/server/serveDev.ts +24 -58
- package/src/server/servedApp.ts +390 -0
- package/src/server/shell.ts +1 -1
- package/src/server/signInChanged.ts +20 -0
- package/src/server/surfaceOf.ts +3 -2
- package/src/server/versionOf.ts +7 -16
- package/src/shared/RPC_SERVER.ts +27 -31
- package/src/shared/Schema.ts +1 -2
- package/src/shared/applyPatch.ts +1 -1
- package/src/shared/index.ts +1 -1
- package/src/shared/{note.ts → issueReport.ts} +22 -0
- package/src/shared/loadCall.ts +66 -0
- package/src/shared/muxFrames.ts +1 -1
- package/src/shared/reactive/Keyed.ts +24 -16
- package/src/shared/reactive/Reactive.ts +37 -991
- package/src/shared/reactive/argsContract.ts +131 -4
- package/src/shared/reactive/byOf.ts +2 -5
- package/src/shared/reactive/callLoad.ts +8 -14
- package/src/shared/reactive/cellShape.ts +734 -0
- package/src/shared/reactive/index.ts +12 -23
- package/src/shared/reactive/pairReactive.ts +5 -3
- package/src/shared/reactive/patchLog.ts +79 -0
- package/src/shared/reactive/patchMarks.ts +34 -0
- package/src/shared/reactive/producer.ts +121 -0
- package/src/shared/reactive/recordWrites.ts +11 -14
- package/src/shared/reactive/scoped.ts +3 -2
- package/src/shared/reactive/selections.ts +209 -0
- package/src/shared/reactive/share.ts +4 -3
- package/src/shared/reactive/watch.ts +3 -4
- package/src/shared/rpc.ts +20 -24
- package/src/shared/validateJson.ts +1 -2
- package/src/shared/writePath.ts +29 -0
- package/src/ui/ArmRegion.ts +10 -4
- package/src/ui/Level.ts +11 -20
- package/src/ui/LevelChain.ts +272 -0
- package/src/ui/Router.ts +106 -313
- package/src/ui/Scope.ts +17 -3
- package/src/ui/TryRegion.ts +22 -23
- package/src/ui/bindSpread.ts +2 -4
- package/src/ui/listenForRoutes.ts +3 -10
- package/src/ui/outboxQueue.ts +23 -11
- package/src/ui/outcomeOf.ts +120 -0
- package/src/ui/reconcile.ts +4 -6
- package/src/ui/routing.ts +2 -1
- package/src/ui/rows.ts +2 -2
- package/src/ui/setAttribute.ts +12 -3
- package/src/ui/socketWire.ts +28 -13
- package/src/ui/wire.ts +9 -10
- package/src/compiler/Finding.ts +0 -2
- package/src/compiler/Refusals.ts +0 -65
- package/src/compiler/formatDiagnosis.ts +0 -20
- package/src/server/appDirectories.ts +0 -9
- package/src/server/argsFor.ts +0 -39
- package/src/server/buildDirectory.ts +0 -10
- package/src/server/coerceArgs.ts +0 -109
- package/src/server/derivedArgs.ts +0 -26
- package/src/server/runBuild.ts +0 -24
- package/src/server/serveBuild.ts +0 -144
- package/src/server/testBuild.ts +0 -220
- package/src/shared/LOAD_OFFLINE.ts +0 -15
- package/src/shared/LOAD_SIGNAL.ts +0 -10
- package/src/shared/issuesOf.ts +0 -18
- package/src/shared/reactive/ENTRY_LIVES.ts +0 -7
- package/src/shared/reactive/FOLLOWED.ts +0 -8
- package/src/shared/reactive/PATCHES.ts +0 -11
- package/src/shared/reactive/PATCH_SINKS.ts +0 -26
- package/src/shared/reactive/canonicalKey.ts +0 -95
|
@@ -16,16 +16,15 @@ import {
|
|
|
16
16
|
} from '#shared/routePattern.ts'
|
|
17
17
|
|
|
18
18
|
import { abideImportsOf, componentGraph } from './componentGraph.ts'
|
|
19
|
+
import { type Diagnosis, Diagnostics } from './Diagnostics.ts'
|
|
19
20
|
import type { HandlerFile } from './handlersUnder.ts'
|
|
20
21
|
import { hookCalls, hooksIn } from './hookCalls.ts'
|
|
21
22
|
import { matchersOf, type MountedTree } from './matchersOf.ts'
|
|
22
23
|
import { mountsOf } from './mountsOf.ts'
|
|
23
24
|
import { type AbideFile, scriptBodies } from './parseAbide.ts'
|
|
24
25
|
import { type ParsedSource, parseSources } from './readSources.ts'
|
|
25
|
-
import { Refusals } from './Refusals.ts'
|
|
26
26
|
import { resolverIn } from './resolverIn.ts'
|
|
27
27
|
import { attributedImports } from './splitImports.ts'
|
|
28
|
-
import type { Diagnosis } from './TypeSession.ts'
|
|
29
28
|
|
|
30
29
|
// THE APP, LAID OUT ONCE PER COMPILE OR CHECK: every `.abide` and handler file it holds — its own, a
|
|
31
30
|
// dependency's it imports, a package's it mounts — each with where it sits and what that makes it
|
|
@@ -110,15 +109,15 @@ export type AppLayout = {
|
|
|
110
109
|
*/
|
|
111
110
|
export async function appLayout(root: string): Promise<AppLayout> {
|
|
112
111
|
// COLLECTED, whoever asks: a layout is kept across checks (`AppChecker`), and a build adopts
|
|
113
|
-
// what it found (`
|
|
114
|
-
const refusals = new
|
|
112
|
+
// what it found (`Diagnostics.adopt`).
|
|
113
|
+
const refusals = new Diagnostics(root, true)
|
|
115
114
|
const { abide, handlers, params, modules, published } = appFiles(
|
|
116
115
|
root,
|
|
117
116
|
refusals,
|
|
118
117
|
)
|
|
119
118
|
await refuseModuleHooks(root, modules, refusals)
|
|
120
119
|
const resolve = resolverIn()
|
|
121
|
-
const unparsed = new
|
|
120
|
+
const unparsed = new Diagnostics(root, true)
|
|
122
121
|
const own = parseSources(
|
|
123
122
|
await textsAt(abide.map((path) => `${root}/${path}`)),
|
|
124
123
|
unparsed,
|
|
@@ -140,13 +139,7 @@ export async function appLayout(root: string): Promise<AppLayout> {
|
|
|
140
139
|
found.positions.get(source.path) ?? relative,
|
|
141
140
|
)
|
|
142
141
|
refusePlace(source, placed, resolve, refusals)
|
|
143
|
-
sources.push(
|
|
144
|
-
...source,
|
|
145
|
-
...placed,
|
|
146
|
-
paints: paints(source.file),
|
|
147
|
-
invoked: invoked.has(source.path),
|
|
148
|
-
hooks: hooksIn(scriptBodies(source.file)),
|
|
149
|
-
})
|
|
142
|
+
sources.push(placedSource(source, placed, invoked.has(source.path)))
|
|
150
143
|
}
|
|
151
144
|
const texts = await textsAt(found.handlers.map((file) => file.path))
|
|
152
145
|
const placedHandlers: PlacedHandler[] = []
|
|
@@ -209,8 +202,8 @@ export async function appLayout(root: string): Promise<AppLayout> {
|
|
|
209
202
|
errors,
|
|
210
203
|
published,
|
|
211
204
|
addresses: [...addresses].sort(),
|
|
212
|
-
refused: refusals.
|
|
213
|
-
unparsed: unparsed.
|
|
205
|
+
refused: refusals.refused,
|
|
206
|
+
unparsed: unparsed.refused,
|
|
214
207
|
resolve,
|
|
215
208
|
}
|
|
216
209
|
}
|
|
@@ -250,9 +243,9 @@ export async function withDependencies(
|
|
|
250
243
|
parsed: readonly ParsedSource[],
|
|
251
244
|
own: readonly string[],
|
|
252
245
|
resolve: (specifier: string, from: string) => string | undefined,
|
|
253
|
-
refusals:
|
|
246
|
+
refusals: Diagnostics,
|
|
254
247
|
/** Where what the parser refuses of a file reached here goes, where apart from `refusals`. */
|
|
255
|
-
unparsed:
|
|
248
|
+
unparsed: Diagnostics = refusals,
|
|
256
249
|
): Promise<{
|
|
257
250
|
sources: ParsedSource[]
|
|
258
251
|
handlers: HandlerFile[]
|
|
@@ -347,6 +340,21 @@ export async function withDependencies(
|
|
|
347
340
|
}
|
|
348
341
|
|
|
349
342
|
/** Whether a page or an error page has anything to paint: a template, or a head. 23.3, 35.14. */
|
|
343
|
+
/** `source` at `placed`, with what its own text says it paints and calls. */
|
|
344
|
+
export function placedSource(
|
|
345
|
+
source: ParsedSource,
|
|
346
|
+
placed: Placement,
|
|
347
|
+
invoked: boolean,
|
|
348
|
+
): PlacedSource {
|
|
349
|
+
return {
|
|
350
|
+
...source,
|
|
351
|
+
...placed,
|
|
352
|
+
paints: paints(source.file),
|
|
353
|
+
invoked,
|
|
354
|
+
hooks: hooksIn(scriptBodies(source.file)),
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
350
358
|
export function paints(file: AbideFile): boolean {
|
|
351
359
|
return file.template.length > 0 || file.head.length > 0
|
|
352
360
|
}
|
|
@@ -362,7 +370,7 @@ function refusePlace(
|
|
|
362
370
|
source: ParsedSource,
|
|
363
371
|
placed: Placement,
|
|
364
372
|
resolve: (specifier: string, from: string) => string | undefined,
|
|
365
|
-
refusals:
|
|
373
|
+
refusals: Diagnostics,
|
|
366
374
|
): void {
|
|
367
375
|
const { relative, place, kind } = placed
|
|
368
376
|
if (source.file.servers.length > 0 && place === 'public')
|
|
@@ -404,7 +412,7 @@ function refusePlace(
|
|
|
404
412
|
async function refuseModuleHooks(
|
|
405
413
|
root: string,
|
|
406
414
|
modules: readonly string[],
|
|
407
|
-
refusals:
|
|
415
|
+
refusals: Diagnostics,
|
|
408
416
|
): Promise<void> {
|
|
409
417
|
const texts = await textsAt(modules.map((module) => `${root}/${module}`))
|
|
410
418
|
for (let at = 0; at < modules.length; at += 1) {
|
|
@@ -428,7 +436,7 @@ async function refuseModuleHooks(
|
|
|
428
436
|
function refuseStrayLauncher(
|
|
429
437
|
sources: readonly PlacedSource[],
|
|
430
438
|
handlers: readonly PlacedHandler[],
|
|
431
|
-
refusals:
|
|
439
|
+
refusals: Diagnostics,
|
|
432
440
|
): void {
|
|
433
441
|
const stray = (relative: string): void =>
|
|
434
442
|
refusals.at(
|
|
@@ -457,7 +465,7 @@ function refuseStrayLauncher(
|
|
|
457
465
|
*/
|
|
458
466
|
function routesOf(
|
|
459
467
|
sources: readonly PlacedSource[],
|
|
460
|
-
refusals:
|
|
468
|
+
refusals: Diagnostics,
|
|
461
469
|
): { routes: AppRoute[]; errors: AppRoute[] } {
|
|
462
470
|
const layouts: { source: string; segments: Segment[] }[] = []
|
|
463
471
|
const layoutAt = new Map<string, string>()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { AMBIENT_SOURCES, ambientReadsIn } from './ambientReadsIn.ts'
|
|
2
|
+
import { argsDefaultsIn } from './argsDefaultsIn.ts'
|
|
3
|
+
import {
|
|
4
|
+
byPlace,
|
|
5
|
+
type Diagnosis,
|
|
6
|
+
type Finding,
|
|
7
|
+
placedFindings,
|
|
8
|
+
} from './Diagnostics.ts'
|
|
9
|
+
import { projectShadow } from './projectShadow.ts'
|
|
10
|
+
import type { ParsedSource } from './readSources.ts'
|
|
11
|
+
import type { ShadowHost } from './ShadowHost.ts'
|
|
12
|
+
import { ShadowMap } from './ShadowMap.ts'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* What the build refuses and warns on in the app's `.ts` files and its `.abide` `sources`.
|
|
16
|
+
*
|
|
17
|
+
* 11.37 — `ambient`: every read of a request's ambient inside a `global` value's body, as errors.
|
|
18
|
+
* 11.30 — `unkeyed`: every `args` defaulting a member its body's parameter does not, as warnings.
|
|
19
|
+
* Both over every `.ts` under `within` and each of `sources` as the shadow its reads registered
|
|
20
|
+
* for it, because a keyed value is declared in a `.ts` as often as in a `.abide` — one walk of
|
|
21
|
+
* the files for the two, each finding on its own line of the file an author wrote. `scripts`
|
|
22
|
+
* as a diagnosis reads it: an editor asking about one page does not walk the app's `.ts`.
|
|
23
|
+
*/
|
|
24
|
+
export async function appLints(
|
|
25
|
+
host: ShadowHost,
|
|
26
|
+
within: string,
|
|
27
|
+
sources: readonly Omit<ParsedSource, 'quiet'>[],
|
|
28
|
+
scripts: boolean,
|
|
29
|
+
): Promise<{ ambient: Diagnosis[]; unkeyed: Diagnosis[] }> {
|
|
30
|
+
const ambient: Diagnosis[] = []
|
|
31
|
+
const unkeyed: Diagnosis[] = []
|
|
32
|
+
await host.ask(async (project) => {
|
|
33
|
+
const checker = project.checker
|
|
34
|
+
const ambientPaths = new Map<string, string>()
|
|
35
|
+
const declared = await Promise.all(
|
|
36
|
+
AMBIENT_SOURCES.map((source) =>
|
|
37
|
+
project.program.getSourceFile(source),
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
for (let at = 0; at < AMBIENT_SOURCES.length; at += 1) {
|
|
41
|
+
const found = declared[at]?.path
|
|
42
|
+
if (found !== undefined)
|
|
43
|
+
ambientPaths.set(AMBIENT_SOURCES[at] as string, found)
|
|
44
|
+
}
|
|
45
|
+
const authored = new Map<
|
|
46
|
+
string,
|
|
47
|
+
{ source: (typeof sources)[number]; map: ShadowMap }
|
|
48
|
+
>()
|
|
49
|
+
for (const source of sources) {
|
|
50
|
+
const map = new ShadowMap(
|
|
51
|
+
host.canonical(source.path),
|
|
52
|
+
projectShadow(source.file).origins,
|
|
53
|
+
)
|
|
54
|
+
authored.set(host.keyOf(map.shadowPath), { source, map })
|
|
55
|
+
}
|
|
56
|
+
// A file of the host's own is one of `sources`' shadows or not walked at all: a checked
|
|
57
|
+
// module is the lowering's, and placed nowhere an author wrote.
|
|
58
|
+
const files: string[] = []
|
|
59
|
+
if (scripts)
|
|
60
|
+
for (const path of host.rootsUnder(project, within)) {
|
|
61
|
+
if (authored.has(host.keyOf(path)) || !host.holds(path))
|
|
62
|
+
files.push(path)
|
|
63
|
+
}
|
|
64
|
+
else for (const { map } of authored.values()) files.push(map.shadowPath)
|
|
65
|
+
await Promise.all(
|
|
66
|
+
files.map(async (path) => {
|
|
67
|
+
const sourceFile = await project.program.getSourceFile(path)
|
|
68
|
+
if (sourceFile === undefined) return
|
|
69
|
+
const [reads, defaults] = await Promise.all([
|
|
70
|
+
ambientPaths.size === 0
|
|
71
|
+
? []
|
|
72
|
+
: ambientReadsIn(checker, sourceFile, ambientPaths),
|
|
73
|
+
argsDefaultsIn(checker, sourceFile),
|
|
74
|
+
])
|
|
75
|
+
if (reads.length === 0 && defaults.length === 0) return
|
|
76
|
+
const shadow = authored.get(host.keyOf(path))
|
|
77
|
+
const source = shadow?.source
|
|
78
|
+
const text =
|
|
79
|
+
source === undefined
|
|
80
|
+
? await Bun.file(path).text()
|
|
81
|
+
: source.text
|
|
82
|
+
const place = (at: number): number =>
|
|
83
|
+
shadow === undefined ? at : shadow.map.toSource(path, at)
|
|
84
|
+
const named = source?.path ?? path
|
|
85
|
+
const ambientReads: Finding[] = []
|
|
86
|
+
for (const read of reads)
|
|
87
|
+
ambientReads.push({
|
|
88
|
+
at: read.at,
|
|
89
|
+
message: `abide: a \`global\` value's body reads \`${read.ambient}\`, which belongs to the request it was built in — every later request would be answered with this one's. Take it out of the body, or drop \`global\`. RULEBOOK 11.37.`,
|
|
90
|
+
})
|
|
91
|
+
ambient.push(
|
|
92
|
+
...placedFindings(
|
|
93
|
+
named,
|
|
94
|
+
text,
|
|
95
|
+
ambientReads,
|
|
96
|
+
place,
|
|
97
|
+
'error',
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
unkeyed.push(
|
|
101
|
+
...placedFindings(named, text, defaults, place, 'warning'),
|
|
102
|
+
)
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
ambient.sort(byPlace)
|
|
107
|
+
unkeyed.sort(byPlace)
|
|
108
|
+
return { ambient, unkeyed }
|
|
109
|
+
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from 'typescript/unstable/ast/is'
|
|
18
18
|
import { type Checker, SymbolFlags, type Type } from 'typescript/unstable/async'
|
|
19
19
|
|
|
20
|
-
import type { Finding } from './
|
|
20
|
+
import type { Finding } from './Diagnostics.ts'
|
|
21
21
|
import { optionNamed, reactiveOptions } from './reactiveOptions.ts'
|
|
22
22
|
|
|
23
23
|
// 11.30 — AN `args` SCHEMA DEFAULTING A MEMBER THE BODY'S PARAMETER DOES NOT. The key is taken before
|
package/src/compiler/checkApp.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { relative } from 'node:path' // bun exposes no path helpers of its own
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { placement } from '#server/placement.ts'
|
|
4
4
|
|
|
5
|
-
import type
|
|
6
|
-
import { type AppChecker, compiled } from './compileApp.ts'
|
|
5
|
+
import { type PlacedSource, placedSource } from './appLayout.ts'
|
|
6
|
+
import { type AppChecker, compiled, compiledSources } from './compileApp.ts'
|
|
7
7
|
import { abideImportsOf } from './componentGraph.ts'
|
|
8
|
+
import { type Diagnosis, Diagnostics } from './Diagnostics.ts'
|
|
8
9
|
import { hooksIn } from './hookCalls.ts'
|
|
9
10
|
import { parseAbide, scriptBodies } from './parseAbide.ts'
|
|
10
11
|
import { parseSources, readSources } from './readSources.ts'
|
|
11
|
-
import { Refusals } from './Refusals.ts'
|
|
12
|
-
import type { Diagnosis } from './TypeSession.ts'
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Every finding `abide check` reports for the app at `appRoot`.
|
|
@@ -50,74 +49,71 @@ export async function checkSource(
|
|
|
50
49
|
): Promise<Diagnosis[]> {
|
|
51
50
|
const { session, root } = checker
|
|
52
51
|
const layout = await checker.layout()
|
|
52
|
+
const diagnostics = new Diagnostics(root, true)
|
|
53
53
|
// 38.33 — WHAT THE TREE REFUSES OF IT, off the layout the checker keeps until a move is heard.
|
|
54
54
|
// What the parser refuses is the held text's, below, never the disk's (`layout.unparsed`).
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (refused.path === path) tree.push(refused)
|
|
58
|
-
const refusals = new Refusals(root, true)
|
|
59
|
-
const [page] = parseSources([{ path, text }], refusals)
|
|
60
|
-
if (page === undefined) return [...tree, ...refusals.found]
|
|
55
|
+
diagnostics.adopt(layout.refused)
|
|
56
|
+
const [page] = parseSources([{ path, text }], diagnostics)
|
|
61
57
|
// 23.19 — WHERE THE APP PLACES IT, so a mounted page's `route.params` is typed by the route its
|
|
62
58
|
// mount gives it, as `checkApp` types it; a file the layout has not seen is placed at its path.
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
).values())
|
|
72
|
-
try {
|
|
73
|
-
imports.push({
|
|
74
|
-
path: imported,
|
|
75
|
-
text: held(imported) ?? (await Bun.file(imported).text()),
|
|
76
|
-
quiet: true,
|
|
77
|
-
})
|
|
78
|
-
} catch {
|
|
79
|
-
// A component that cannot be read takes anything here.
|
|
80
|
-
}
|
|
81
|
-
// 27.13 — AND EVERY FILE FILING `onConfig`, quietly, so a page that only reads `config()` is
|
|
82
|
-
// typed by defaults it never imported — as `checkApp`'s whole-app program types it. The calls
|
|
83
|
-
// only name candidates; the checker decides which file.
|
|
84
|
-
const lowered = new Set(imports.map((one) => one.path))
|
|
85
|
-
for (const other of layout.sources) {
|
|
86
|
-
if (other.path === path || lowered.has(other.path)) continue
|
|
87
|
-
const editing = held(other.path)
|
|
88
|
-
if (
|
|
89
|
-
editing === undefined || editing === other.text
|
|
90
|
-
? other.hooks.includes('onConfig')
|
|
91
|
-
: filesConfig(editing)
|
|
59
|
+
const own: PlacedSource[] = []
|
|
60
|
+
const beside: { path: string; text: string; quiet: boolean }[] = []
|
|
61
|
+
if (page !== undefined) {
|
|
62
|
+
const laid = layout.sources.find((source) => source.path === path)
|
|
63
|
+
own.push(
|
|
64
|
+
laid === undefined
|
|
65
|
+
? placedSource(page, placement(relative(root, path)), false)
|
|
66
|
+
: { ...laid, ...page },
|
|
92
67
|
)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
68
|
+
for (const imported of abideImportsOf(
|
|
69
|
+
path,
|
|
70
|
+
page.file,
|
|
71
|
+
layout.resolve,
|
|
72
|
+
).values())
|
|
73
|
+
try {
|
|
74
|
+
beside.push({
|
|
75
|
+
path: imported,
|
|
76
|
+
text: held(imported) ?? (await Bun.file(imported).text()),
|
|
77
|
+
quiet: true,
|
|
78
|
+
})
|
|
79
|
+
} catch {
|
|
80
|
+
// A component that cannot be read takes anything here.
|
|
81
|
+
}
|
|
82
|
+
// 27.13 — AND EVERY FILE FILING `onConfig`, quietly, so a page that only reads `config()`
|
|
83
|
+
// is typed by defaults it never imported — as `checkApp`'s whole-app program types it. The
|
|
84
|
+
// calls only name candidates; the checker decides which file.
|
|
85
|
+
const lowered = new Set(beside.map((one) => one.path))
|
|
86
|
+
for (const other of layout.sources) {
|
|
87
|
+
if (other.path === path || lowered.has(other.path)) continue
|
|
88
|
+
const editing = held(other.path)
|
|
89
|
+
if (
|
|
90
|
+
editing === undefined || editing === other.text
|
|
91
|
+
? other.hooks.includes('onConfig')
|
|
92
|
+
: filesConfig(editing)
|
|
93
|
+
)
|
|
94
|
+
beside.push({
|
|
95
|
+
path: other.path,
|
|
96
|
+
text: editing ?? other.text,
|
|
97
|
+
quiet: true,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
98
100
|
}
|
|
99
101
|
// A component that does not parse, or that the checker refuses, is its own file's finding and
|
|
100
102
|
// takes anything here: a quiet source's refusal is reported by nobody.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
...beside,
|
|
116
|
-
],
|
|
117
|
-
layout,
|
|
118
|
-
false,
|
|
119
|
-
)
|
|
120
|
-
return [...tree, ...own, ...diagnoses]
|
|
103
|
+
if (own.length > 0)
|
|
104
|
+
await compiledSources(
|
|
105
|
+
layout,
|
|
106
|
+
session,
|
|
107
|
+
own,
|
|
108
|
+
parseSources(beside, diagnostics),
|
|
109
|
+
diagnostics,
|
|
110
|
+
true,
|
|
111
|
+
false,
|
|
112
|
+
)
|
|
113
|
+
const found: Diagnosis[] = []
|
|
114
|
+
for (const finding of diagnostics.findings)
|
|
115
|
+
if (finding.path === path) found.push(finding)
|
|
116
|
+
return found
|
|
121
117
|
}
|
|
122
118
|
|
|
123
119
|
/**
|
|
@@ -132,7 +128,7 @@ export async function lowerApp(
|
|
|
132
128
|
held: (path: string) => string | undefined,
|
|
133
129
|
): Promise<void> {
|
|
134
130
|
const layout = await checker.layout()
|
|
135
|
-
const
|
|
131
|
+
const diagnostics = new Diagnostics(checker.root, true)
|
|
136
132
|
const sources: (PlacedSource & { quiet: true })[] = []
|
|
137
133
|
for (const source of layout.sources) {
|
|
138
134
|
const editing = held(source.path)
|
|
@@ -142,12 +138,12 @@ export async function lowerApp(
|
|
|
142
138
|
}
|
|
143
139
|
const [parsed] = parseSources(
|
|
144
140
|
[{ path: source.path, text: editing, quiet: true }],
|
|
145
|
-
|
|
141
|
+
diagnostics,
|
|
146
142
|
)
|
|
147
143
|
if (parsed !== undefined)
|
|
148
144
|
sources.push({ ...source, ...parsed, quiet: true })
|
|
149
145
|
}
|
|
150
|
-
const
|
|
146
|
+
const read = await readSources(checker.session, sources, diagnostics)
|
|
151
147
|
await checker.session.diagnose(read, layout, false)
|
|
152
148
|
}
|
|
153
149
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SERVER_SPECIFIER } from '#shared/SPECIFIERS.ts'
|
|
2
2
|
|
|
3
3
|
import type { CheckerAnswer } from './CheckerAnswer.ts'
|
|
4
|
-
import type { Finding } from './
|
|
4
|
+
import type { Finding } from './Diagnostics.ts'
|
|
5
5
|
import { hookCalls } from './hookCalls.ts'
|
|
6
6
|
import {
|
|
7
7
|
CHECKED_CONFIG,
|
|
@@ -40,7 +40,7 @@ export type CheckedShadow = {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* What a checked module's default export is declared as. Scaffolding, so a jump to it is a jump to
|
|
43
|
-
* the component's file (`
|
|
43
|
+
* the component's file (`LoweredFile.inspect`).
|
|
44
44
|
*/
|
|
45
45
|
export const COMPONENT_STAND_IN = '__component'
|
|
46
46
|
|
|
@@ -46,7 +46,8 @@ export type Read = {
|
|
|
46
46
|
* `value` lowers to `n()`; `asWritten` stays as written (`n.set(v)`, 31.5); `member` is one of
|
|
47
47
|
* the `Reactive`'s own members referenced rather than called, named by `path`; `call` is the
|
|
48
48
|
* explicit `n()` (31.7), which `arguments` says was given any; `shorthand` is `{ n }`;
|
|
49
|
-
* `write` and `update` are 31.4, and 31.8 where `path` is not empty.
|
|
49
|
+
* `write` and `update` are 31.4, and 31.8 where `path` is not empty. `mutate` is 31.9: an
|
|
50
|
+
* array mutator called through `path`, its `value` the call from the receiver's end.
|
|
50
51
|
*
|
|
51
52
|
* 33's five: the `props<Props>()` call, which lowers to the record the caller passed; the pair
|
|
52
53
|
* that brackets a binding element's initializer, which together make a default an accessor;
|
|
@@ -63,6 +64,7 @@ export type Read = {
|
|
|
63
64
|
| 'shorthand'
|
|
64
65
|
| 'write'
|
|
65
66
|
| 'update'
|
|
67
|
+
| 'mutate'
|
|
66
68
|
| 'props'
|
|
67
69
|
| 'defaultOpen'
|
|
68
70
|
| 'defaultClose'
|
|
@@ -244,6 +246,7 @@ export function classifyPropRead(node: Node): Read | undefined {
|
|
|
244
246
|
export function classifyRead(
|
|
245
247
|
node: Node,
|
|
246
248
|
valueMember = false,
|
|
249
|
+
mutator = false,
|
|
247
250
|
): Read | undefined {
|
|
248
251
|
const parent = node.parent
|
|
249
252
|
if (isNotARead(parent)) return undefined
|
|
@@ -325,6 +328,18 @@ export function classifyRead(
|
|
|
325
328
|
top = up
|
|
326
329
|
}
|
|
327
330
|
|
|
331
|
+
// 31.9 — the walk took the mutator's own name as a step: it is the call, not the path.
|
|
332
|
+
if (
|
|
333
|
+
mutator &&
|
|
334
|
+
isPropertyAccessExpression(top) &&
|
|
335
|
+
isCallExpression(top.parent)
|
|
336
|
+
) {
|
|
337
|
+
read.path.pop()
|
|
338
|
+
read.shape = 'mutate'
|
|
339
|
+
read.to = top.parent.end
|
|
340
|
+
read.value = { start: top.expression.end, end: top.parent.end }
|
|
341
|
+
return read
|
|
342
|
+
}
|
|
328
343
|
const outer = top.parent
|
|
329
344
|
if (isAssigned(outer, top)) {
|
|
330
345
|
read.shape = 'write'
|