@abide/abide 0.117.2 → 0.119.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 +22 -0
- package/bin/abide.ts +74 -11
- package/docs/REGISTRY.md +18 -15
- package/docs/RULEBOOK.md +71 -10
- package/docs/abide.md +584 -274
- package/package.json +1 -1
- package/scaffold/AGENTS.md +1 -1
- package/scaffold/src/getGreeting.test.ts +2 -1
- package/scaffold/src/index.abide +5 -2
- package/scaffold/src/index.test.ts +2 -0
- package/scaffold/src/layout.abide +6 -6
- package/src/compiler/Lowering.ts +20 -2
- package/src/compiler/PositionTable.ts +1 -0
- package/src/compiler/Refusals.ts +1 -0
- package/src/compiler/TypeSession.ts +49 -13
- package/src/compiler/aliasedSymbol.ts +1 -1
- package/src/compiler/ambientReadsIn.ts +1 -0
- package/src/compiler/appLayout.ts +55 -3
- package/src/compiler/appMap.ts +15 -0
- package/src/compiler/argsDefaultsIn.ts +1 -0
- package/src/compiler/argsSchemaOf.ts +1 -0
- package/src/compiler/checkApp.ts +3 -1
- package/src/compiler/checkedShadow.ts +2 -1
- package/src/compiler/classifyRead.ts +1 -0
- package/src/compiler/classifyTemplate.ts +1 -0
- package/src/compiler/compileApp.ts +5 -3
- package/src/compiler/compiledEntry.ts +2 -1
- package/src/compiler/componentGraph.ts +1 -0
- package/src/compiler/declaredExports.ts +4 -2
- package/src/compiler/emitClient.ts +19 -14
- package/src/compiler/emitComponent.ts +1 -0
- package/src/compiler/emitSource.ts +2 -1
- package/src/compiler/formatAbide.ts +857 -0
- package/src/compiler/formatDiagnosis.ts +1 -0
- package/src/compiler/handlersUnder.ts +7 -3
- package/src/compiler/hookCalls.ts +1 -0
- package/src/compiler/lineOf.ts +17 -0
- package/src/compiler/matchersOf.ts +157 -0
- package/src/compiler/mountsOf.ts +5 -0
- package/src/compiler/paramFile.ts +125 -0
- package/src/compiler/parseAbide.ts +48 -28
- package/src/compiler/projectShadow.ts +11 -4
- package/src/compiler/reactiveOptions.ts +1 -0
- package/src/compiler/reactiveTypes.ts +2 -1
- package/src/compiler/readsIn.ts +3 -2
- package/src/compiler/renderMarkup.ts +3 -2
- package/src/compiler/resolverIn.ts +2 -0
- package/src/compiler/sameTemplate.ts +150 -0
- package/src/compiler/scaffold.ts +4 -11
- package/src/compiler/serveLanguage.ts +48 -21
- package/src/compiler/sharedModuleWrites.ts +2 -1
- package/src/compiler/sourceMapOf.ts +1 -0
- package/src/compiler/splitImports.ts +2 -0
- package/src/compiler/templateWarnings.ts +2 -2
- package/src/compiler/trackingWarnings.ts +1 -0
- package/src/compiler/writeDefaultProject.ts +5 -1
- package/src/server/BRIDGE_VERSION.ts +1 -1
- package/src/server/abideModules.ts +2 -0
- package/src/server/answerFailure.ts +1 -0
- package/src/server/answerRequest.ts +57 -19
- package/src/server/answerRpc.ts +6 -2
- package/src/server/answerSocket.ts +2 -1
- package/src/server/answerValue.ts +1 -0
- package/src/server/answerWorker.ts +1 -0
- package/src/server/appTree.ts +18 -9
- package/src/server/argsFor.ts +1 -0
- package/src/server/assistant.ts +2 -1
- package/src/server/banner.ts +2 -0
- package/src/server/bridgeRelay.ts +1 -0
- package/src/server/callerOf.ts +1 -0
- package/src/server/claudeApi.ts +1 -0
- package/src/server/claudeCode.ts +1 -0
- package/src/server/clientBundle.ts +1 -0
- package/src/server/coerceArgs.ts +1 -2
- package/src/server/commands.ts +9 -0
- package/src/server/compressAnswer.ts +2 -1
- package/src/server/config.ts +2 -0
- package/src/server/createApp.ts +4 -2
- package/src/server/crossingOrigin.ts +1 -0
- package/src/server/csp.ts +1 -0
- package/src/server/deferredBuilds.ts +1 -0
- package/src/server/derivedArgs.ts +1 -0
- package/src/server/devWorker.ts +2 -1
- package/src/server/faceOf.ts +2 -7
- package/src/server/generatedSurfaces.ts +79 -7
- package/src/server/hooks.ts +1 -0
- package/src/server/iconAccent.ts +1 -1
- package/src/server/installServer.ts +1 -0
- package/src/server/installSourceMaps.ts +1 -0
- package/src/server/launcherPage.ts +2 -2
- package/src/server/manifestOf.ts +1 -0
- package/src/server/muxUpgrade.ts +1 -0
- package/src/server/nativeMenu.ts +2 -0
- package/src/server/placement.ts +12 -2
- package/src/server/principal.ts +2 -1
- package/src/server/remote.ts +7 -2
- package/src/server/render.ts +3 -3
- package/src/server/renderComponent.ts +1 -0
- package/src/server/reportHealth.ts +1 -0
- package/src/server/requestScope.ts +3 -1
- package/src/server/runDesktop.ts +3 -2
- package/src/server/seedBuffer.ts +1 -0
- package/src/server/serveDev.ts +3 -2
- package/src/server/shell.ts +2 -0
- package/src/server/streamEvent.ts +1 -0
- package/src/server/surfaceOf.ts +6 -1
- package/src/server/testBuild.ts +1 -0
- package/src/server/testMount.ts +1 -0
- package/src/server/trace.ts +2 -0
- package/src/server/withAbideSources.ts +1 -0
- package/src/shared/Attributes.ts +12 -8
- package/src/shared/cookies.ts +1 -1
- package/src/shared/escapeText.ts +1 -1
- package/src/shared/filledPath.ts +16 -10
- package/src/shared/index.ts +1 -5
- package/src/shared/jsonSchema.ts +1 -3
- package/src/shared/matchRoute.ts +16 -4
- package/src/shared/menuBridge.ts +1 -2
- package/src/shared/outbox.ts +1 -1
- package/src/shared/reactive/Keyed.ts +2 -2
- package/src/shared/reactive/Reactive.ts +12 -16
- package/src/shared/reactive/index.ts +3 -4
- package/src/shared/reactive/isReactive.ts +1 -1
- package/src/shared/reactive/scoped.ts +2 -2
- package/src/shared/reactive/share.ts +1 -1
- package/src/shared/reactive/watch.ts +4 -4
- package/src/shared/refuse.ts +1 -1
- package/src/shared/routePattern.ts +66 -14
- package/src/shared/rpc.ts +8 -11
- package/src/shared/url.ts +24 -7
- package/src/shared/validateJson.ts +1 -1
- package/src/ui/ArmRegion.ts +1 -0
- package/src/ui/Level.ts +1 -0
- package/src/ui/List.ts +1 -0
- package/src/ui/RawRun.ts +1 -0
- package/src/ui/Router.ts +1 -0
- package/src/ui/TryRegion.ts +1 -0
- package/src/ui/adoptWalk.ts +2 -1
- package/src/ui/bindElement.ts +1 -0
- package/src/ui/bindFiles.ts +1 -0
- package/src/ui/bindGroup.ts +1 -0
- package/src/ui/bindProperty.ts +1 -0
- package/src/ui/bindSpread.ts +1 -0
- package/src/ui/index.ts +1 -6
- package/src/ui/outboxQueue.ts +1 -1
- package/src/ui/parseMarkup.ts +2 -1
- package/src/ui/reconcile.ts +2 -1
- package/src/ui/reconcileHead.ts +1 -0
- package/src/ui/regions.ts +2 -1
- package/src/ui/registerWorker.ts +1 -0
- package/src/ui/rows.ts +1 -0
- package/src/ui/setMarkup.ts +1 -0
- package/src/ui/setText.ts +1 -0
- package/src/ui/socketWire.ts +4 -3
- package/src/ui/wire.ts +2 -1
- /package/scaffold/src/{getGreeting.ts → getGreeting.handler.ts} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { REGION_CLOSE, REGION_OPEN, ROW_OPEN } from '#shared/ANCHORS.ts'
|
|
2
1
|
import { anchorMarkup, regionMarkup } from '#shared/anchorMarkup.ts'
|
|
2
|
+
import { REGION_CLOSE, REGION_OPEN, ROW_OPEN } from '#shared/ANCHORS.ts'
|
|
3
3
|
import { armAnchor } from '#shared/armAnchor.ts'
|
|
4
4
|
import { CHILDREN } from '#shared/CHILDREN.ts'
|
|
5
5
|
import { escapeQuote } from '#shared/escapeText.ts'
|
|
6
6
|
import { headKeyOf } from '#shared/headKeyOf.ts'
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
import {
|
|
9
9
|
attributeOf,
|
|
10
10
|
type ClassifiedAttribute,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
isSlot,
|
|
16
16
|
type MergeGroup,
|
|
17
17
|
} from './classifyTemplate.ts'
|
|
18
|
+
import { Cursor, type PlacedNode, placeTemplate } from './Cursor.ts'
|
|
18
19
|
import { invocationProps, passedName } from './invocationProps.ts'
|
|
19
20
|
import { type Lowering, PROPS } from './Lowering.ts'
|
|
20
21
|
import type { AbideFile } from './parseAbide.ts'
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs' // bun exposes no sync existence check or read of its own
|
|
2
2
|
import { dirname, posix } from 'node:path' // bun exposes no path helpers of its own
|
|
3
|
+
|
|
3
4
|
import { resolvedFile } from '#server/abideModules.ts'
|
|
5
|
+
|
|
4
6
|
import { nearest } from './nearest.ts'
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { SyntaxKind } from 'typescript/unstable/ast'
|
|
2
|
+
|
|
3
|
+
import type { AbideFile } from './parseAbide.ts'
|
|
4
|
+
import type { Body, InlineComponent, TemplateNode } from './TemplateNode.ts'
|
|
5
|
+
import { tokenize } from './tokenize.ts'
|
|
6
|
+
|
|
7
|
+
// 38.127 — WHAT `abide format` MAY CHANGE, and nothing else: the whitespace a browser collapses, a
|
|
8
|
+
// run's indentation where 33.17 and 33.18 read only its kind, and the code `bun fmt` printed, read as
|
|
9
|
+
// its tokens — a script block's in any order, since `sortImports` moves its imports. Everything else — a tag, an attribute's text, a block's shape, a line inside a template
|
|
10
|
+
// literal — is compared as the parser read it.
|
|
11
|
+
|
|
12
|
+
/** Whether `before` and `after` are one template, under what `abide format` may change. 38.127. */
|
|
13
|
+
export function sameTemplate(before: AbideFile, after: AbideFile): boolean {
|
|
14
|
+
return shapeOf(before) === shapeOf(after)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function shapeOf(file: AbideFile): string {
|
|
18
|
+
const parts: string[] = []
|
|
19
|
+
for (const code of file.modules) parts.push(blockShape(code))
|
|
20
|
+
for (const code of file.servers) parts.push(blockShape(code))
|
|
21
|
+
for (const code of file.scripts) parts.push(blockShape(code))
|
|
22
|
+
for (const style of file.styles) parts.push(styleShape(style))
|
|
23
|
+
parts.push(file.generics ?? '')
|
|
24
|
+
nodesShape(file.head, parts)
|
|
25
|
+
if (file.document !== undefined) {
|
|
26
|
+
nodesShape([file.document.html], parts)
|
|
27
|
+
nodesShape([file.document.body], parts)
|
|
28
|
+
}
|
|
29
|
+
nodesShape(file.template, parts)
|
|
30
|
+
for (const component of file.components) componentShape(component, parts)
|
|
31
|
+
return parts.join('\u0000')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function nodesShape(nodes: TemplateNode[], parts: string[]): void {
|
|
35
|
+
for (const node of nodes) {
|
|
36
|
+
if (node.kind === 'text') {
|
|
37
|
+
parts.push(`text ${textShape(node.text)}`)
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
if (node.kind === 'expression') {
|
|
41
|
+
parts.push(`slot ${codeShape(node.source)}`)
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
if (node.kind === 'element') {
|
|
45
|
+
parts.push(`<${node.tag}`)
|
|
46
|
+
for (const attribute of node.attributes)
|
|
47
|
+
parts.push(
|
|
48
|
+
attribute.expression
|
|
49
|
+
? `${attribute.name}={${codeShape(attribute.value)}}`
|
|
50
|
+
: `${attribute.name}=${attribute.value}`,
|
|
51
|
+
)
|
|
52
|
+
nodesShape(node.children, parts)
|
|
53
|
+
for (const component of node.components)
|
|
54
|
+
componentShape(component, parts)
|
|
55
|
+
for (const component of node.passed)
|
|
56
|
+
componentShape(component, parts)
|
|
57
|
+
parts.push(`</${node.tag}>`)
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
if (node.block === 'for') {
|
|
61
|
+
parts.push(
|
|
62
|
+
`{#for ${codeShape(node.item)},${codeShape(node.index)} of ${codeShape(node.subject)} by ${codeShape(node.key)}`,
|
|
63
|
+
)
|
|
64
|
+
bodyShape(node, parts)
|
|
65
|
+
} else {
|
|
66
|
+
parts.push(
|
|
67
|
+
node.block === 'try'
|
|
68
|
+
? `{#try ${node.error}`
|
|
69
|
+
: `{#${node.block}`,
|
|
70
|
+
)
|
|
71
|
+
for (const branch of node.branches) {
|
|
72
|
+
parts.push(`{:${codeShape(branch.subject)}`)
|
|
73
|
+
bodyShape(branch, parts)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
parts.push('{/}')
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function bodyShape(body: Body, parts: string[]): void {
|
|
81
|
+
parts.push(
|
|
82
|
+
body.setup === null ? '' : blockShape(body.setup.text),
|
|
83
|
+
body.style === null ? '' : styleShape(body.style),
|
|
84
|
+
)
|
|
85
|
+
nodesShape(body.children, parts)
|
|
86
|
+
for (const component of body.components) componentShape(component, parts)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function componentShape(component: InlineComponent, parts: string[]): void {
|
|
90
|
+
parts.push(
|
|
91
|
+
`{#component ${component.name}(${component.pattern}: ${component.type})`,
|
|
92
|
+
)
|
|
93
|
+
bodyShape(component, parts)
|
|
94
|
+
parts.push('{/component}')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A text node as the browser renders it: within text, every whitespace run holding a newline is one
|
|
99
|
+
* space however it is indented. A node that is whitespace alone keeps its KIND — a newline alone, or
|
|
100
|
+
* one carrying indentation — since 33.17 and 33.18 drop the two apart.
|
|
101
|
+
*/
|
|
102
|
+
function textShape(text: string): string {
|
|
103
|
+
if (text.trim() === '') return /[ \t]/.test(text) ? '\n ' : '\n'
|
|
104
|
+
return text.replace(/\s*\n\s*/g, '\n')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Code as its tokens, in order. */
|
|
108
|
+
function codeShape(code: string): string {
|
|
109
|
+
return tokensOf(code).join(' ')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** A script block's code as its tokens in any order: `bun fmt`'s `sortImports` moves its imports. */
|
|
113
|
+
function blockShape(code: string): string {
|
|
114
|
+
return tokensOf(code).sort().join(' ')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Code's tokens, with what a printer adds or drops at will taken out: the separators `;` and `,`,
|
|
119
|
+
* the parentheses, and how a string or a number is spelled. A template literal's text is a token,
|
|
120
|
+
* so a line moved inside one is a different token.
|
|
121
|
+
*/
|
|
122
|
+
function tokensOf(code: string): string[] {
|
|
123
|
+
const tokens: string[] = []
|
|
124
|
+
for (const token of tokenize(code))
|
|
125
|
+
switch (token.kind) {
|
|
126
|
+
case SyntaxKind.SemicolonToken:
|
|
127
|
+
case SyntaxKind.CommaToken:
|
|
128
|
+
case SyntaxKind.OpenParenToken:
|
|
129
|
+
case SyntaxKind.CloseParenToken:
|
|
130
|
+
continue
|
|
131
|
+
case SyntaxKind.StringLiteral:
|
|
132
|
+
tokens.push(
|
|
133
|
+
JSON.stringify(
|
|
134
|
+
token.text.slice(1, -1).replace(/\\(['"])/g, '$1'),
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
continue
|
|
138
|
+
case SyntaxKind.NumericLiteral:
|
|
139
|
+
tokens.push(String(Number(token.text.replaceAll('_', ''))))
|
|
140
|
+
continue
|
|
141
|
+
default:
|
|
142
|
+
tokens.push(token.text)
|
|
143
|
+
}
|
|
144
|
+
return tokens
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A style with its whitespace and its `;`s taken out, which CSS reads none of outside a string. */
|
|
148
|
+
function styleShape(style: string): string {
|
|
149
|
+
return style.replace(/[\s;]+/g, '')
|
|
150
|
+
}
|
package/src/compiler/scaffold.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// half-made project finishes it rather than overwriting it.
|
|
6
6
|
|
|
7
7
|
import { withoutTrailingSlash } from '#server/withoutTrailingSlash.ts'
|
|
8
|
+
|
|
8
9
|
import abidePackage from '../../package.json' with { type: 'json' }
|
|
9
10
|
import { COMPILER_OPTIONS } from './writeDefaultProject.ts'
|
|
10
11
|
|
|
@@ -39,7 +40,7 @@ const SOURCE_ALIAS = { '#src/*': ['./src/*', './src/*.ts'] }
|
|
|
39
40
|
* lands first on the PATH inside `bun run`, shadowing the bun that runs everything else.
|
|
40
41
|
*/
|
|
41
42
|
export const BUNFIG =
|
|
42
|
-
'# 38.122 — a peer is not installed: an installed `bun` would shadow this one inside `bun run`.\n[install]\npeer = false\n\n# 38.109, 38.119 — a DOM to mount into and measure, then the app\'s `.abide` files as modules.\n[test]\nroot = "src"\npreload = ["@abide/harness/abide/preload"]\n'
|
|
43
|
+
'# 38.122 — a peer is not installed: an installed `bun` would shadow this one inside `bun run`.\n[install]\npeer = false\n\n# 38.109, 38.119 — a DOM to mount into and measure, then the app\'s `.abide` files as modules.\n[test]\nroot = "src"\npreload = ["@abide/harness/abide/preload"]\n\n# What `bun fmt` and `abide format` print.\n[fmt]\ntabWidth = 4\nsemi = false\nsingleQuote = true\nsortImports = true\n'
|
|
43
44
|
|
|
44
45
|
// WRITTEN, NOT COPIED, beside the manifests: a packer drops a `bunfig.toml` or a `.gitignore` from
|
|
45
46
|
// the tarball (`bun pm pack` both, `npm pack` the second), so neither survives as a starter file.
|
|
@@ -55,6 +56,8 @@ function filesFor(name: string): Record<string, string> {
|
|
|
55
56
|
scripts: {
|
|
56
57
|
// 38.103 — the checker over `.abide`, which `tsc` does not read.
|
|
57
58
|
check: 'abide check',
|
|
59
|
+
// 38.125 — every `.abide`, then every other file `bun fmt` reads.
|
|
60
|
+
format: 'abide format && bun fmt',
|
|
58
61
|
dev: 'abide dev',
|
|
59
62
|
build: 'abide build',
|
|
60
63
|
start: 'abide start',
|
|
@@ -101,16 +104,6 @@ function filesFor(name: string): Record<string, string> {
|
|
|
101
104
|
null,
|
|
102
105
|
4,
|
|
103
106
|
)}\n`,
|
|
104
|
-
'biome.json': `${JSON.stringify(
|
|
105
|
-
{
|
|
106
|
-
formatter: { indentStyle: 'space', indentWidth: 4 },
|
|
107
|
-
javascript: {
|
|
108
|
-
formatter: { quoteStyle: 'single', semicolons: 'asNeeded' },
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
null,
|
|
112
|
-
4,
|
|
113
|
-
)}\n`,
|
|
114
107
|
}
|
|
115
108
|
}
|
|
116
109
|
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { existsSync, type FSWatcher, watch } from 'node:fs' // bun exposes no file watcher or sync existence check of its own
|
|
2
2
|
import { dirname } from 'node:path' // bun exposes no path helpers of its own
|
|
3
|
+
|
|
3
4
|
import { realOrAsGiven } from '#server/realOrAsGiven.ts'
|
|
5
|
+
|
|
4
6
|
import { checkSource, lowerApp } from './checkApp.ts'
|
|
5
7
|
import { citedClauses } from './citedClauses.ts'
|
|
6
8
|
import { type AppChecker, openChecker } from './compileApp.ts'
|
|
7
9
|
import { DOCS_SITE } from './DOCS_SITE.ts'
|
|
10
|
+
import { formatAbide } from './formatAbide.ts'
|
|
11
|
+
import { formatDiagnosis } from './formatDiagnosis.ts'
|
|
8
12
|
import { isNamedAlone } from './isNamedAlone.ts'
|
|
13
|
+
import { lineOf, offsetOf } from './lineOf.ts'
|
|
9
14
|
import { nearest } from './nearest.ts'
|
|
10
15
|
import type { Diagnosis, Inspection, Place } from './TypeSession.ts'
|
|
11
16
|
|
|
@@ -129,8 +134,8 @@ export async function serveLanguage(
|
|
|
129
134
|
documents.get(Bun.pathToFileURL(spelled(path)).href)?.text
|
|
130
135
|
|
|
131
136
|
// An app's checker, opened on first use and kept for the session — and TOLD WHAT MOVED ON DISK,
|
|
132
|
-
// or it answers every check from the text it read when it opened: an export added to a
|
|
133
|
-
//
|
|
137
|
+
// or it answers every check from the text it read when it opened: an export added to a `lib`
|
|
138
|
+
// module the page imports is "no exported member" until the editor restarts this. A project whose
|
|
134
139
|
// tsconfig or package.json moved is opened again.
|
|
135
140
|
const checkerOf = async (app: string): Promise<AppChecker> => {
|
|
136
141
|
let checker = checkers.get(app)
|
|
@@ -290,6 +295,7 @@ export async function serveLanguage(
|
|
|
290
295
|
definitionProvider: true,
|
|
291
296
|
referencesProvider: true,
|
|
292
297
|
renameProvider: { prepareProvider: true },
|
|
298
|
+
documentFormattingProvider: true,
|
|
293
299
|
completionProvider: {
|
|
294
300
|
triggerCharacters: ['.', '<', '#', ':', '/'],
|
|
295
301
|
},
|
|
@@ -392,8 +398,7 @@ export async function serveLanguage(
|
|
|
392
398
|
const found = await references(params)
|
|
393
399
|
const declarations = (
|
|
394
400
|
params?.context as
|
|
395
|
-
|
|
396
|
-
| undefined
|
|
401
|
+
{ includeDeclaration?: boolean } | undefined
|
|
397
402
|
)?.includeDeclaration
|
|
398
403
|
const locations: object[] = []
|
|
399
404
|
for (const place of found?.occurrences ?? [])
|
|
@@ -457,6 +462,45 @@ export async function serveLanguage(
|
|
|
457
462
|
})
|
|
458
463
|
return true
|
|
459
464
|
}
|
|
465
|
+
case 'textDocument/formatting': {
|
|
466
|
+
// 38.130 — the text the editor holds, formatted as `abide format` formats a file:
|
|
467
|
+
// one edit replacing the whole of it, none where nothing moves.
|
|
468
|
+
const uri = (params?.textDocument as { uri: string }).uri
|
|
469
|
+
const document = documents.get(uri)
|
|
470
|
+
if (document === undefined) {
|
|
471
|
+
send({ id: message.id, result: null })
|
|
472
|
+
return true
|
|
473
|
+
}
|
|
474
|
+
const path = Bun.fileURLToPath(uri)
|
|
475
|
+
const text = document.text
|
|
476
|
+
const formatted = await formatAbide(path, text)
|
|
477
|
+
// Shown rather than published: a publish would replace what the check reported.
|
|
478
|
+
for (const finding of formatted.findings)
|
|
479
|
+
send({
|
|
480
|
+
method: 'window/showMessage',
|
|
481
|
+
params: {
|
|
482
|
+
type: 2,
|
|
483
|
+
message: formatDiagnosis(finding, dirname(path)),
|
|
484
|
+
},
|
|
485
|
+
})
|
|
486
|
+
send({
|
|
487
|
+
id: message.id,
|
|
488
|
+
result:
|
|
489
|
+
formatted.text === text
|
|
490
|
+
? []
|
|
491
|
+
: [
|
|
492
|
+
{
|
|
493
|
+
range: rangeOf({
|
|
494
|
+
path,
|
|
495
|
+
start: { line: 1, column: 1 },
|
|
496
|
+
end: lineOf(text, text.length),
|
|
497
|
+
}),
|
|
498
|
+
newText: formatted.text,
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
})
|
|
502
|
+
return true
|
|
503
|
+
}
|
|
460
504
|
case 'textDocument/didClose': {
|
|
461
505
|
const closed = params?.textDocument as { uri: string }
|
|
462
506
|
if (!documents.delete(closed.uri)) return true
|
|
@@ -542,23 +586,6 @@ function wordLength(line: string, character: number): number {
|
|
|
542
586
|
return word === undefined ? 1 : word.length
|
|
543
587
|
}
|
|
544
588
|
|
|
545
|
-
/**
|
|
546
|
-
* The protocol's `{ line, character }`, from 0, as an offset into `text`. A character is a UTF-16
|
|
547
|
-
* code unit in the protocol and in a JavaScript string alike.
|
|
548
|
-
*/
|
|
549
|
-
function offsetOf(
|
|
550
|
-
text: string,
|
|
551
|
-
position: { line: number; character: number },
|
|
552
|
-
): number {
|
|
553
|
-
let at = 0
|
|
554
|
-
for (let line = 0; line < position.line; line += 1) {
|
|
555
|
-
const end = text.indexOf('\n', at)
|
|
556
|
-
if (end === -1) return text.length
|
|
557
|
-
at = end + 1
|
|
558
|
-
}
|
|
559
|
-
return at + position.character
|
|
560
|
-
}
|
|
561
|
-
|
|
562
589
|
/** A `Place` at `path`, lines and columns from 1, as the protocol's `Location`, from 0. */
|
|
563
590
|
function locationOf(path: string, place: Place): object {
|
|
564
591
|
return { uri: Bun.pathToFileURL(path).href, range: rangeOf(place) }
|
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
isVariableDeclarationList,
|
|
5
5
|
} from 'typescript/unstable/ast/is'
|
|
6
6
|
import type {
|
|
7
|
-
Symbol as CheckedSymbol,
|
|
8
7
|
Checker,
|
|
8
|
+
Symbol as CheckedSymbol,
|
|
9
9
|
} from 'typescript/unstable/async'
|
|
10
|
+
|
|
10
11
|
import { classifyRead, FUNCTIONS } from './classifyRead.ts'
|
|
11
12
|
import type { Finding } from './Finding.ts'
|
|
12
13
|
import type { Span } from './projectShadow.ts'
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// What no chunk covers is the framework's own scaffolding, and maps to nothing.
|
|
12
12
|
|
|
13
13
|
import { encodeMappings, type Segment } from '#server/sourceMappings.ts'
|
|
14
|
+
|
|
14
15
|
import type { LoweredChunk } from './Lowering.ts'
|
|
15
16
|
import type { Origin } from './projectShadow.ts'
|
|
16
17
|
import { mapBack } from './ShadowMap.ts'
|
|
@@ -2,12 +2,12 @@ import type { CheckerAnswer } from './CheckerAnswer.ts'
|
|
|
2
2
|
import type { Read } from './classifyRead.ts'
|
|
3
3
|
import { isComponentTag } from './classifyTemplate.ts'
|
|
4
4
|
import { isNamedAlone } from './isNamedAlone.ts'
|
|
5
|
-
import { Lowering } from './Lowering.ts'
|
|
6
5
|
import { diagnosisAt } from './lineOf.ts'
|
|
6
|
+
import { Lowering } from './Lowering.ts'
|
|
7
7
|
import type { AbideFile } from './parseAbide.ts'
|
|
8
8
|
import {
|
|
9
|
-
type Body,
|
|
10
9
|
bodiesOf,
|
|
10
|
+
type Body,
|
|
11
11
|
type ForBlock,
|
|
12
12
|
type TemplateNode,
|
|
13
13
|
visitBodies,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
isPropertyAccessExpression,
|
|
15
15
|
isWhileStatement,
|
|
16
16
|
} from 'typescript/unstable/ast/is'
|
|
17
|
+
|
|
17
18
|
import { FUNCTIONS, TRANSPARENT } from './classifyRead.ts'
|
|
18
19
|
import type { Finding } from './Finding.ts'
|
|
19
20
|
import type { PositionKind, PositionTable } from './PositionTable.ts'
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { mkdtemp, rm } from 'node:fs/promises' // bun exposes no temporary-directory creation or recursive removal of its own
|
|
12
12
|
import { tmpdir } from 'node:os' // bun exposes no temporary-directory path of its own
|
|
13
13
|
import { dirname, resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
14
|
+
|
|
14
15
|
import { nearest } from './nearest.ts'
|
|
15
16
|
|
|
16
17
|
/** What every abide app in the docs and the scaffold is written against. */
|
|
@@ -33,7 +34,10 @@ export async function writeDefaultProject(
|
|
|
33
34
|
await Bun.write(
|
|
34
35
|
tsconfig,
|
|
35
36
|
JSON.stringify({
|
|
36
|
-
compilerOptions: {
|
|
37
|
+
compilerOptions: {
|
|
38
|
+
...COMPILER_OPTIONS,
|
|
39
|
+
paths: await aliasPaths(root),
|
|
40
|
+
},
|
|
37
41
|
include: [`${root}/**/*`],
|
|
38
42
|
}),
|
|
39
43
|
)
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// The `abide-bridge` release a pairing command names, so a visitor's `bunx` runs the relay this
|
|
2
2
|
// server speaks. `packages/abide/tests/claudeCode.test.ts` holds it to the bridge package's own.
|
|
3
|
-
export const BRIDGE_VERSION = '0.3.
|
|
3
|
+
export const BRIDGE_VERSION = '0.3.5'
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
// to this one rather than registering a plugin nobody asks.
|
|
19
19
|
|
|
20
20
|
import { basename, dirname, resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
21
|
+
|
|
21
22
|
import { SERVER_SPECIFIER } from '#shared/SPECIFIERS.ts'
|
|
23
|
+
|
|
22
24
|
import type { ServerTable } from './answerRequest.ts'
|
|
23
25
|
import type { MountRow } from './answerRpc.ts'
|
|
24
26
|
import {
|
|
@@ -7,6 +7,7 @@ import { type Failed, problemOf } from '#shared/Failed.ts'
|
|
|
7
7
|
import { RENDER_LOG } from '#shared/log.ts'
|
|
8
8
|
import { coversPrefix, partsOf } from '#shared/matchRoute.ts'
|
|
9
9
|
import { coversRoute, parseRoutePattern } from '#shared/routePattern.ts'
|
|
10
|
+
|
|
10
11
|
import type { AppSources } from './answerRequest.ts'
|
|
11
12
|
import { DOCUMENT_HEADERS } from './DOCUMENT_HEADERS.ts'
|
|
12
13
|
import { boundPage, renderDocument } from './render.ts'
|
|
@@ -19,23 +19,23 @@ import {
|
|
|
19
19
|
} from '#shared/ADDRESSES.ts'
|
|
20
20
|
import { APP_META } from '#shared/APP_META.ts'
|
|
21
21
|
import { DEBUG_META } from '#shared/DEBUG_META.ts'
|
|
22
|
-
import { ICON_ADDRESSES } from '#shared/LAUNCHER_FILES.ts'
|
|
23
|
-
import { OFFLINE_META } from '#shared/OFFLINE_META.ts'
|
|
24
22
|
import { decodedOrKept } from '#shared/decodedOrKept.ts'
|
|
25
23
|
import { ERROR_META } from '#shared/ERROR_META.ts'
|
|
26
24
|
import { escapeAttribute } from '#shared/escapeText.ts'
|
|
27
25
|
import { type Failed, isFailed } from '#shared/Failed.ts'
|
|
28
26
|
import { HandlerRoute } from '#shared/HandlerRoute.ts'
|
|
27
|
+
import { health } from '#shared/health.ts'
|
|
29
28
|
import { KEEP_HEADER } from '#shared/KEEP_HEADER.ts'
|
|
30
29
|
import { KEEP_OPENED } from '#shared/KEEP_OPENED.ts'
|
|
31
|
-
import {
|
|
30
|
+
import { ICON_ADDRESSES } from '#shared/LAUNCHER_FILES.ts'
|
|
32
31
|
import { appName } from '#shared/log.ts'
|
|
33
|
-
import { compose } from '#shared/Middleware.ts'
|
|
34
|
-
import { MOUNT_META } from '#shared/MOUNT_META.ts'
|
|
35
32
|
import { matchOf, type Params, partsOf } from '#shared/matchRoute.ts'
|
|
33
|
+
import { compose } from '#shared/Middleware.ts'
|
|
36
34
|
import { MOUNT, mounted } from '#shared/mount.ts'
|
|
35
|
+
import { MOUNT_META } from '#shared/MOUNT_META.ts'
|
|
37
36
|
import { NAVIGATION_HEADER } from '#shared/NAVIGATION_HEADER.ts'
|
|
38
|
-
import {
|
|
37
|
+
import { OFFLINE_META } from '#shared/OFFLINE_META.ts'
|
|
38
|
+
import { notFound as refusedNotFound, refuse } from '#shared/refuse.ts'
|
|
39
39
|
import { arrive } from '#shared/route.ts'
|
|
40
40
|
import {
|
|
41
41
|
parseRoutePattern,
|
|
@@ -44,29 +44,30 @@ import {
|
|
|
44
44
|
} from '#shared/routePattern.ts'
|
|
45
45
|
import type { Method, Rpc } from '#shared/rpc.ts'
|
|
46
46
|
import { rpcRequest } from '#shared/rpcRequest.ts'
|
|
47
|
+
|
|
47
48
|
import { answered, answerFailure } from './answerFailure.ts'
|
|
48
49
|
import {
|
|
49
50
|
answerRpc,
|
|
50
51
|
type Mounted,
|
|
51
|
-
type MountRow,
|
|
52
52
|
mountedOwn,
|
|
53
53
|
mountHandlers,
|
|
54
|
+
type MountRow,
|
|
54
55
|
} from './answerRpc.ts'
|
|
55
56
|
import { answerBlobTagged, answerValue, contentEtag } from './answerValue.ts'
|
|
56
|
-
import { answerBridge } from './bridgeRelay.ts'
|
|
57
|
-
import { CLIENT_ENTRY } from './CLIENT_BUILD.ts'
|
|
58
|
-
import { appManifest } from './appManifest.ts'
|
|
59
57
|
import { answerWorker } from './answerWorker.ts'
|
|
58
|
+
import { appManifest } from './appManifest.ts'
|
|
59
|
+
import { answerBridge } from './bridgeRelay.ts'
|
|
60
60
|
import { callerOrMint } from './callerOf.ts'
|
|
61
|
-
import {
|
|
61
|
+
import { CLIENT_ENTRY } from './CLIENT_BUILD.ts'
|
|
62
62
|
import { compressAnswer } from './compressAnswer.ts'
|
|
63
|
-
import {
|
|
64
|
-
import { OFFLINE_STALE } from './OFFLINE_STALE.ts'
|
|
63
|
+
import { settingOn } from './config.ts'
|
|
65
64
|
import { noteCallInRender } from './deferredBuilds.ts'
|
|
65
|
+
import { DOCUMENT_HEADERS } from './DOCUMENT_HEADERS.ts'
|
|
66
66
|
import { answerMcp, openApi } from './generatedSurfaces.ts'
|
|
67
67
|
import { type AppRung, failureOf, rungsOver } from './hooks.ts'
|
|
68
68
|
import { chunksOf, type Manifest } from './manifestOf.ts'
|
|
69
69
|
import { muxAdmission } from './muxUpgrade.ts'
|
|
70
|
+
import { OFFLINE_STALE } from './OFFLINE_STALE.ts'
|
|
70
71
|
import { plainAnswer } from './plainAnswer.ts'
|
|
71
72
|
import { prefers } from './prefers.ts'
|
|
72
73
|
import { PRINCIPAL_MOUNT } from './principal.ts'
|
|
@@ -78,8 +79,8 @@ import {
|
|
|
78
79
|
renderDocument,
|
|
79
80
|
} from './render.ts'
|
|
80
81
|
import {
|
|
81
|
-
type AnsweringApp,
|
|
82
82
|
adoptRequest,
|
|
83
|
+
type AnsweringApp,
|
|
83
84
|
beginRender,
|
|
84
85
|
currentRequestScope,
|
|
85
86
|
type PageRender,
|
|
@@ -100,6 +101,8 @@ type ServerRoute = {
|
|
|
100
101
|
// 38.28 — the modules a browser loads at this address, the page's then its layouts', as keys
|
|
101
102
|
// into the manifest `manifestOf` reads.
|
|
102
103
|
clientModules: string[]
|
|
104
|
+
// 23.26 — each matcher its pattern names, to the anchored source the build resolved it to.
|
|
105
|
+
matchers: Record<string, string>
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
// 35.2 — an error page, and the directory it answers for.
|
|
@@ -122,6 +125,7 @@ type Ranked = RankedError & {
|
|
|
122
125
|
*/
|
|
123
126
|
type RouteRow = {
|
|
124
127
|
pattern: string
|
|
128
|
+
matchers: Record<string, string>
|
|
125
129
|
segments: Segment[]
|
|
126
130
|
page: Ranked | undefined
|
|
127
131
|
handlers: Map<Method, Mounted>
|
|
@@ -178,6 +182,16 @@ export type AppSources = {
|
|
|
178
182
|
// 41.29 — the seed sink of a render no page hydrates: every call is left to run when it is made.
|
|
179
183
|
const SEEDS_NOTHING: PageRender['seed'] = () => false
|
|
180
184
|
|
|
185
|
+
// 35.19 — what fetch calls a navigation request's destinations, and a script's `fetch`.
|
|
186
|
+
const PAGE_DESTINATIONS = new Set([
|
|
187
|
+
'document',
|
|
188
|
+
'embed',
|
|
189
|
+
'frame',
|
|
190
|
+
'iframe',
|
|
191
|
+
'object',
|
|
192
|
+
'empty',
|
|
193
|
+
])
|
|
194
|
+
|
|
181
195
|
/** The `Response` to one request: a handler, a surface of abide's own, a file, or a page. */
|
|
182
196
|
export async function answerRequest(
|
|
183
197
|
app: AppSources,
|
|
@@ -331,6 +345,17 @@ async function answerMatched(
|
|
|
331
345
|
headers: { 'cache-control': 'no-store' },
|
|
332
346
|
})
|
|
333
347
|
|
|
348
|
+
// 35.19 — A REQUEST THAT CANNOT DISPLAY A PAGE, such as the `/favicon.ico` a browser asks every
|
|
349
|
+
// site for where a `[param]` page matches it: refused before setup, so no handler runs for it
|
|
350
|
+
// and no caller is minted. Bare, because an error page would set up too and nothing shows it;
|
|
351
|
+
// `no-store`, because the address answers a navigation with the page.
|
|
352
|
+
const destination = request.headers.get('sec-fetch-dest')
|
|
353
|
+
if (destination !== null && !PAGE_DESTINATIONS.has(destination))
|
|
354
|
+
return new Response(null, {
|
|
355
|
+
status: 404,
|
|
356
|
+
headers: { 'cache-control': 'no-store' },
|
|
357
|
+
})
|
|
358
|
+
|
|
334
359
|
// 33.4 — a prop is an ACCESSOR, which is the contract `renderComponent` renders a child under
|
|
335
360
|
// and the one the client router mounts a page under; the params are the record `firstMatch`
|
|
336
361
|
// hands the client for this same address.
|
|
@@ -534,16 +559,22 @@ export async function appSources(parts: SourceParts): Promise<AppSources> {
|
|
|
534
559
|
pattern: route.pattern,
|
|
535
560
|
load: route.load,
|
|
536
561
|
layouts: route.layouts,
|
|
562
|
+
matchers: route.matchers,
|
|
537
563
|
document: documentOf(route.clientModules),
|
|
538
564
|
})
|
|
539
565
|
// 19.1 — the build refused two files at one route and method, so a route is one row however
|
|
540
|
-
// many methods answer at it.
|
|
566
|
+
// many methods answer at it — and 23.27 one matcher per position, so whichever file opens the
|
|
567
|
+
// row brings what every file at it would.
|
|
541
568
|
const rows = new Map<string, Omit<RouteRow, 'segments'>>()
|
|
542
|
-
const rowAt = (
|
|
569
|
+
const rowAt = (
|
|
570
|
+
pattern: string,
|
|
571
|
+
matchers: Record<string, string>,
|
|
572
|
+
): Omit<RouteRow, 'segments'> => {
|
|
543
573
|
let row = rows.get(pattern)
|
|
544
574
|
if (row === undefined) {
|
|
545
575
|
row = {
|
|
546
576
|
pattern,
|
|
577
|
+
matchers,
|
|
547
578
|
page: undefined,
|
|
548
579
|
handlers: new Map(),
|
|
549
580
|
answers: new Map(),
|
|
@@ -552,12 +583,19 @@ export async function appSources(parts: SourceParts): Promise<AppSources> {
|
|
|
552
583
|
}
|
|
553
584
|
return row
|
|
554
585
|
}
|
|
555
|
-
for (const page of rankRoutes(pages))
|
|
586
|
+
for (const page of rankRoutes(pages))
|
|
587
|
+
rowAt(page.pattern, page.matchers).page = page
|
|
556
588
|
for (const handler of mounted.handlers.values())
|
|
557
|
-
rowAt(handler.address).handlers.set(
|
|
589
|
+
rowAt(handler.address, handler.matchers).handlers.set(
|
|
590
|
+
handler.method,
|
|
591
|
+
handler,
|
|
592
|
+
)
|
|
558
593
|
const errors: RankedError[] = []
|
|
559
594
|
for (const error of parts.table.errors)
|
|
560
|
-
errors.push({
|
|
595
|
+
errors.push({
|
|
596
|
+
...error,
|
|
597
|
+
segments: parseRoutePattern(error.pattern, error.matchers),
|
|
598
|
+
})
|
|
561
599
|
errors.sort((a, b) => b.segments.length - a.segments.length)
|
|
562
600
|
const sources: AppSources = {
|
|
563
601
|
routes: rankRoutes([...rows.values()]),
|
package/src/server/answerRpc.ts
CHANGED
|
@@ -11,15 +11,16 @@ import { GLOBAL_GETS } from '#shared/GLOBAL_GETS.ts'
|
|
|
11
11
|
import { isRecord } from '#shared/isRecord.ts'
|
|
12
12
|
import type { JsonValue } from '#shared/JsonValue.ts'
|
|
13
13
|
import { mounted as underMount } from '#shared/mount.ts'
|
|
14
|
-
import { RPC_SERVER } from '#shared/RPC_SERVER.ts'
|
|
15
14
|
import type { Gated } from '#shared/reactive/argsContract.ts'
|
|
16
15
|
import { deadlineOf, heldGenerationOf } from '#shared/reactive/index.ts'
|
|
17
16
|
import type { Defaults } from '#shared/reactive/parameterDefaults.ts'
|
|
18
17
|
import { notFound, refuse } from '#shared/refuse.ts'
|
|
19
18
|
import type { HeldToAnnotation, Method, Rpc } from '#shared/rpc.ts'
|
|
19
|
+
import { RPC_SERVER } from '#shared/RPC_SERVER.ts'
|
|
20
20
|
import { argsInURL } from '#shared/rpcRequest.ts'
|
|
21
21
|
import { VERSION_HEADER } from '#shared/VERSION_HEADER.ts'
|
|
22
22
|
import { versionInEtag } from '#shared/versionInEtag.ts'
|
|
23
|
+
|
|
23
24
|
import {
|
|
24
25
|
answered,
|
|
25
26
|
answerFailure,
|
|
@@ -29,7 +30,6 @@ import {
|
|
|
29
30
|
import { followAsEvents } from './answerSocket.ts'
|
|
30
31
|
import { answerValue, namesEtag, sseHeaders } from './answerValue.ts'
|
|
31
32
|
import { argsFor } from './argsFor.ts'
|
|
32
|
-
import { OFFLINE_STALE } from './OFFLINE_STALE.ts'
|
|
33
33
|
import { callerOf } from './callerOf.ts'
|
|
34
34
|
import {
|
|
35
35
|
argsDocumentOf,
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
crossingRefusal,
|
|
46
46
|
} from './crossingOrigin.ts'
|
|
47
47
|
import { type AppRung, failureOf, rungsOver } from './hooks.ts'
|
|
48
|
+
import { OFFLINE_STALE } from './OFFLINE_STALE.ts'
|
|
48
49
|
import { prefers } from './prefers.ts'
|
|
49
50
|
import {
|
|
50
51
|
currentRequestScope,
|
|
@@ -72,6 +73,8 @@ export type Mount = {
|
|
|
72
73
|
export: string
|
|
73
74
|
/** The route its hooks are filed at, which its rungs are read over (37.1, 16.72). */
|
|
74
75
|
scope: string
|
|
76
|
+
/** 23.26 — each matcher its address names, to the anchored source its row is ranked with. */
|
|
77
|
+
matchers: Record<string, string>
|
|
75
78
|
/** Whether it is a private handler, listed nowhere (16.71). */
|
|
76
79
|
private: boolean
|
|
77
80
|
/** Whether a browser is handed a wrapper for it. 20.4. */
|
|
@@ -154,6 +157,7 @@ export function mountedOwn(
|
|
|
154
157
|
name,
|
|
155
158
|
export: name,
|
|
156
159
|
scope: '/',
|
|
160
|
+
matchers: {},
|
|
157
161
|
private: false,
|
|
158
162
|
ui: false,
|
|
159
163
|
cli: false,
|