@abide/abide 0.118.0 → 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 +11 -0
- package/bin/abide.ts +74 -11
- package/docs/REGISTRY.md +2 -1
- package/docs/RULEBOOK.md +22 -0
- package/docs/abide.md +338 -118
- package/package.json +1 -1
- package/scaffold/src/getGreeting.test.ts +1 -0
- package/scaffold/src/index.abide +4 -1
- 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 +11 -8
- package/src/compiler/aliasedSymbol.ts +1 -1
- package/src/compiler/ambientReadsIn.ts +1 -0
- package/src/compiler/appLayout.ts +4 -2
- 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 +4 -2
- package/src/compiler/compiledEntry.ts +1 -0
- 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 +2 -1
- package/src/compiler/hookCalls.ts +1 -0
- package/src/compiler/lineOf.ts +17 -0
- package/src/compiler/matchersOf.ts +13 -3
- package/src/compiler/mountsOf.ts +2 -0
- package/src/compiler/paramFile.ts +1 -0
- package/src/compiler/parseAbide.ts +48 -28
- package/src/compiler/projectShadow.ts +10 -3
- 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 +46 -19
- 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 +15 -14
- package/src/server/answerRpc.ts +3 -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 +3 -1
- 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 +5 -1
- 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/principal.ts +2 -1
- package/src/server/remote.ts +1 -0
- 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 +1 -0
- 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/index.ts +1 -5
- package/src/shared/jsonSchema.ts +1 -3
- 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/rpc.ts +8 -11
- package/src/shared/url.ts +1 -1
- 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
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Mount } from '#server/answerRpc.ts'
|
|
13
13
|
import { frameworkRouteRefusal } from '#server/appTree.ts'
|
|
14
|
+
import { PRIVATE_EXPORT } from '#server/PRIVATE_EXPORT.ts'
|
|
14
15
|
import { surfaceName } from '#server/surfaceOf.ts'
|
|
15
16
|
import { PRIVATE_HANDLERS_ADDRESS } from '#shared/ADDRESSES.ts'
|
|
16
17
|
import { parseRoutePattern } from '#shared/routePattern.ts'
|
|
18
|
+
|
|
17
19
|
import type { PlacedHandler, PlacedSource } from './appLayout.ts'
|
|
18
20
|
import { declaredExports } from './declaredExports.ts'
|
|
19
|
-
import { PRIVATE_EXPORT } from '#server/PRIVATE_EXPORT.ts'
|
|
20
21
|
import type { Refusals } from './Refusals.ts'
|
|
21
22
|
import type { Diagnosis, TypeSession } from './TypeSession.ts'
|
|
22
23
|
|
package/src/compiler/lineOf.ts
CHANGED
|
@@ -20,6 +20,23 @@ export function lineOf(
|
|
|
20
20
|
return { line, column: end - lineStart + 1 }
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The protocol's `{ line, character }`, from 0, as an offset into `text`. A character is a UTF-16
|
|
25
|
+
* code unit in the protocol and in a JavaScript string alike.
|
|
26
|
+
*/
|
|
27
|
+
export function offsetOf(
|
|
28
|
+
text: string,
|
|
29
|
+
position: { line: number; character: number },
|
|
30
|
+
): number {
|
|
31
|
+
let at = 0
|
|
32
|
+
for (let line = 0; line < position.line; line += 1) {
|
|
33
|
+
const end = text.indexOf('\n', at)
|
|
34
|
+
if (end === -1) return text.length
|
|
35
|
+
at = end + 1
|
|
36
|
+
}
|
|
37
|
+
return at + position.character
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
/** abide's own finding at `offset` into `text`, or at line 0 where the text was not handed in. */
|
|
24
41
|
export function diagnosisAt(
|
|
25
42
|
path: string,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// layout whose segment refused it would be wrapped by a layout no address of its own reaches.
|
|
10
10
|
|
|
11
11
|
import { parseRoutePattern } from '#shared/routePattern.ts'
|
|
12
|
+
|
|
12
13
|
import { matcherOf } from './paramFile.ts'
|
|
13
14
|
import type { Refusals } from './Refusals.ts'
|
|
14
15
|
|
|
@@ -16,7 +17,12 @@ import type { Refusals } from './Refusals.ts'
|
|
|
16
17
|
export type MountedTree = { directory: string; at: string; params: string[] }
|
|
17
18
|
|
|
18
19
|
/** A file with a route: its path, the name refusals give it, and the position it answers by. */
|
|
19
|
-
type Routed = {
|
|
20
|
+
type Routed = {
|
|
21
|
+
path: string
|
|
22
|
+
relative: string
|
|
23
|
+
position: string
|
|
24
|
+
route: string
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
28
|
* Each routed file's matchers, by its path: every name its route's segments give, to the anchored
|
|
@@ -39,7 +45,10 @@ export async function matchersOf(
|
|
|
39
45
|
for (const tree of trees) all.push(...tree.params)
|
|
40
46
|
for (const path of all) {
|
|
41
47
|
if (sources.has(path)) continue
|
|
42
|
-
const matcher = matcherOf(
|
|
48
|
+
const matcher = matcherOf(
|
|
49
|
+
labelOf(root, path),
|
|
50
|
+
await Bun.file(path).text(),
|
|
51
|
+
)
|
|
43
52
|
if (matcher instanceof Error) {
|
|
44
53
|
refusals.at(path, matcher)
|
|
45
54
|
sources.set(path, undefined)
|
|
@@ -79,7 +88,8 @@ export async function matchersOf(
|
|
|
79
88
|
continue
|
|
80
89
|
}
|
|
81
90
|
const other = atPosition.get(shape)
|
|
82
|
-
if (other === undefined)
|
|
91
|
+
if (other === undefined)
|
|
92
|
+
atPosition.set(shape, { found, file: file.relative })
|
|
83
93
|
else if (other.found !== found)
|
|
84
94
|
refusals.at(
|
|
85
95
|
file.path,
|
package/src/compiler/mountsOf.ts
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
import { realpathSync, statSync } from 'node:fs' // bun exposes no sync stat or realpath of its own
|
|
12
12
|
import { dirname, relative, resolve } from 'node:path' // bun exposes no path helpers of its own
|
|
13
|
+
|
|
13
14
|
import { appFiles } from '#server/appTree.ts'
|
|
14
15
|
import { placeOf } from '#server/placement.ts'
|
|
16
|
+
|
|
15
17
|
import { nearest } from './nearest.ts'
|
|
16
18
|
import { type AbideFile, scriptBodies } from './parseAbide.ts'
|
|
17
19
|
import { Refusals } from './Refusals.ts'
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// and still never admit `.` or `..`, which the URL parser collapses into another route.
|
|
10
10
|
|
|
11
11
|
import { SyntaxKind } from 'typescript/unstable/ast'
|
|
12
|
+
|
|
12
13
|
import { tokenize } from './tokenize.ts'
|
|
13
14
|
|
|
14
15
|
/** The name a param file declares: its file name before `.param.ts`. */
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { SyntaxKind } from 'typescript/unstable/ast'
|
|
2
|
+
|
|
2
3
|
import { URL_ATTRIBUTES } from '#shared/URL_ATTRIBUTES.ts'
|
|
4
|
+
|
|
3
5
|
import { isComponentTag, isSlot, propKeyOf } from './classifyTemplate.ts'
|
|
4
6
|
import { decodeCharacterReferences } from './decodeCharacterReferences.ts'
|
|
5
7
|
import { isNamedAlone } from './isNamedAlone.ts'
|
|
6
8
|
import { lineOf } from './lineOf.ts'
|
|
7
9
|
import { PositionedRefusal } from './PositionedRefusal.ts'
|
|
8
|
-
import { TEMPLATE_BLOCKS } from './TEMPLATE_BLOCKS.ts'
|
|
9
10
|
import { importedNames } from './splitImports.ts'
|
|
11
|
+
import { TEMPLATE_BLOCKS } from './TEMPLATE_BLOCKS.ts'
|
|
10
12
|
import {
|
|
11
13
|
type Attribute,
|
|
12
|
-
type Branch,
|
|
13
14
|
bodiesOf,
|
|
15
|
+
type Branch,
|
|
14
16
|
type ElementNode,
|
|
15
17
|
type ForBlock,
|
|
16
18
|
type InlineComponent,
|
|
@@ -106,6 +108,8 @@ type Origins = {
|
|
|
106
108
|
names: Map<object, number>
|
|
107
109
|
/** 38.116 — per element closed by a tag of its own, where that close tag's name starts. */
|
|
108
110
|
closes: Map<object, number>
|
|
111
|
+
/** Where each block body's `<script>` setup starts — 32.48, 14.8. */
|
|
112
|
+
setups: number[]
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
/**
|
|
@@ -216,6 +220,7 @@ export function parseAbide(source: string): AbideFile {
|
|
|
216
220
|
bindings: new Map(),
|
|
217
221
|
names: new Map(),
|
|
218
222
|
closes: new Map(),
|
|
223
|
+
setups: [],
|
|
219
224
|
}
|
|
220
225
|
const root: TemplateNode[] = []
|
|
221
226
|
// The open elements, innermost last. `root` is the floor and is never popped.
|
|
@@ -365,8 +370,7 @@ export function parseAbide(source: string): AbideFile {
|
|
|
365
370
|
if (block === styles && blocks.length > 0) {
|
|
366
371
|
const owner = bodyOf(
|
|
367
372
|
blocks[blocks.length - 1] as
|
|
368
|
-
|
|
|
369
|
-
| InlineComponent,
|
|
373
|
+
TemplateNode | InlineComponent,
|
|
370
374
|
)
|
|
371
375
|
owner.style =
|
|
372
376
|
owner.style === null
|
|
@@ -377,8 +381,7 @@ export function parseAbide(source: string): AbideFile {
|
|
|
377
381
|
}
|
|
378
382
|
if (block === scripts && blocks.length > 0) {
|
|
379
383
|
const holder = blocks[blocks.length - 1] as
|
|
380
|
-
|
|
|
381
|
-
| InlineComponent
|
|
384
|
+
TemplateNode | InlineComponent
|
|
382
385
|
const owner = bodyOf(holder)
|
|
383
386
|
if (owner.setup !== null)
|
|
384
387
|
throw new Error(
|
|
@@ -388,6 +391,7 @@ export function parseAbide(source: string): AbideFile {
|
|
|
388
391
|
)
|
|
389
392
|
owner.setup = { text: raw.body }
|
|
390
393
|
origins.holders.set(owner.setup, tag.end)
|
|
394
|
+
origins.setups.push(tag.end)
|
|
391
395
|
at = raw.end
|
|
392
396
|
continue
|
|
393
397
|
}
|
|
@@ -1072,7 +1076,7 @@ function rawBody(
|
|
|
1072
1076
|
* already `trim()`s for this, which is one question with two answers until now — so the end is
|
|
1073
1077
|
* handed back rather than recomputed from the tag's length.
|
|
1074
1078
|
*/
|
|
1075
|
-
function closeTagAt(
|
|
1079
|
+
export function closeTagAt(
|
|
1076
1080
|
source: string,
|
|
1077
1081
|
tag: string,
|
|
1078
1082
|
from: number,
|
|
@@ -1113,6 +1117,9 @@ function pushText(
|
|
|
1113
1117
|
return next
|
|
1114
1118
|
}
|
|
1115
1119
|
|
|
1120
|
+
/** The ` if` after `{:else` — a whole word, so `{:elseif}` and `{:else ifValid}` are not one. */
|
|
1121
|
+
export const ELSE_IF = /^\s+if(?=[\s(])/
|
|
1122
|
+
|
|
1116
1123
|
/**
|
|
1117
1124
|
* Where the condition on a `{:else …}` arm starts in `inner`, or its end for the bare `{:else}`
|
|
1118
1125
|
* that always takes.
|
|
@@ -1124,7 +1131,7 @@ function pushText(
|
|
|
1124
1131
|
function elseSubjectAt(inner: string): number {
|
|
1125
1132
|
const rest = inner.slice(':else'.length)
|
|
1126
1133
|
if (rest.trim() === '') return inner.length
|
|
1127
|
-
const keyword =
|
|
1134
|
+
const keyword = ELSE_IF.exec(rest)
|
|
1128
1135
|
if (keyword === null || rest.slice(keyword[0].length).trim() === '')
|
|
1129
1136
|
throw new Error(
|
|
1130
1137
|
`abide: {${inner}} is not a control block. Write {:else} or {:else if <condition>}.`,
|
|
@@ -1147,28 +1154,18 @@ function trimmed(
|
|
|
1147
1154
|
}
|
|
1148
1155
|
|
|
1149
1156
|
/**
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
* `item, index of list by key` — `index` and `by` are optional, and everything after `of` up to
|
|
1153
|
-
* a top-level `by` is the subject. Split on the KEYWORDS rather than on commas, because the
|
|
1154
|
-
* subject may hold both.
|
|
1157
|
+
* The `of`, the `,` and the `by` that divide a `{#for}` header — each the first at depth zero.
|
|
1155
1158
|
*
|
|
1156
|
-
* THE
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
* names.filter(n => n !== "run by me")}` split on the ` by ` inside the string literal and the
|
|
1160
|
-
* subject ended mid-quote. One scanner for all three rather than one fix for the comma.
|
|
1159
|
+
* THE HEADER IS TOKENS, read by the scanner a slot is: an `of`, a `,` or a `by` inside a string,
|
|
1160
|
+
* a call or an object pattern is the expression's own, and only one at depth zero divides the
|
|
1161
|
+
* header. `by` is a word rather than a keyword, so a member named `by` is not one.
|
|
1161
1162
|
*/
|
|
1162
|
-
function
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
const
|
|
1168
|
-
// THE HEADER IS TOKENS, read by the scanner a slot is: an `of`, a `,` or a `by` inside a string,
|
|
1169
|
-
// a call or an object pattern is the expression's own, and only one at depth zero divides the
|
|
1170
|
-
// header. `by` is a word rather than a keyword, so a member named `by` is not one.
|
|
1171
|
-
const tokens = tokenize(source)
|
|
1163
|
+
export function forSeparators(header: string): {
|
|
1164
|
+
of: Token | undefined
|
|
1165
|
+
comma: Token | undefined
|
|
1166
|
+
by: Token | undefined
|
|
1167
|
+
} {
|
|
1168
|
+
const tokens = tokenize(header)
|
|
1172
1169
|
let depth = 0
|
|
1173
1170
|
let of: Token | undefined
|
|
1174
1171
|
let comma: Token | undefined
|
|
@@ -1191,6 +1188,29 @@ function forBlock(
|
|
|
1191
1188
|
)
|
|
1192
1189
|
by = token
|
|
1193
1190
|
}
|
|
1191
|
+
return { of, comma, by }
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* Parses a `{#for}` header into its item, index, subject and key.
|
|
1196
|
+
*
|
|
1197
|
+
* `item, index of list by key` — `index` and `by` are optional, and everything after `of` up to
|
|
1198
|
+
* a top-level `by` is the subject. Split on the KEYWORDS rather than on commas, because the
|
|
1199
|
+
* subject may hold both.
|
|
1200
|
+
*
|
|
1201
|
+
* THE SEPARATOR IS THE ONE AT DEPTH ZERO AND OUTSIDE A STRING — all three of them, and each was
|
|
1202
|
+
* wrong its own way. `{#for { id, name } of rows}` split on the pattern's own comma and the item
|
|
1203
|
+
* became `{ id`, which the emitters write straight into a parameter list. `{#for x of
|
|
1204
|
+
* names.filter(n => n !== "run by me")}` split on the ` by ` inside the string literal and the
|
|
1205
|
+
* subject ended mid-quote. One scanner for all three rather than one fix for the comma.
|
|
1206
|
+
*/
|
|
1207
|
+
function forBlock(
|
|
1208
|
+
file: string,
|
|
1209
|
+
header: { text: string; at: number },
|
|
1210
|
+
origins: Origins,
|
|
1211
|
+
): ForBlock {
|
|
1212
|
+
const source = header.text
|
|
1213
|
+
const { of, comma, by } = forSeparators(source)
|
|
1194
1214
|
// A `{#for}` with nothing to iterate rendered as a block over the subject '' — zero rows,
|
|
1195
1215
|
// forever, with nothing saying why (32.22 is an ABSENT subject, not a missing one).
|
|
1196
1216
|
if (of === undefined)
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { SyntaxKind } from 'typescript/unstable/ast'
|
|
2
|
+
|
|
2
3
|
import { PRIVATE_EXPORT } from '#server/PRIVATE_EXPORT.ts'
|
|
3
4
|
import { isEventName } from '#shared/isEventName.ts'
|
|
5
|
+
|
|
4
6
|
import { isComponentTag, propKeyOf } from './classifyTemplate.ts'
|
|
5
7
|
import { isNamedAlone } from './isNamedAlone.ts'
|
|
8
|
+
import type { AbideFile } from './parseAbide.ts'
|
|
6
9
|
import {
|
|
7
10
|
type Position,
|
|
8
11
|
type PositionKind,
|
|
9
12
|
PositionTable,
|
|
10
13
|
} from './PositionTable.ts'
|
|
11
|
-
import type { AbideFile } from './parseAbide.ts'
|
|
12
14
|
import {
|
|
13
15
|
attributedImports,
|
|
14
16
|
IMPORT_ATTRIBUTES,
|
|
@@ -501,12 +503,17 @@ function project(file: AbideFile): Projection {
|
|
|
501
503
|
body(node.children, node.components)
|
|
502
504
|
continue
|
|
503
505
|
}
|
|
504
|
-
// 32.16 — the catch arm binds the failure, and the checker has to see the name.
|
|
506
|
+
// 32.16 — the catch arm binds the failure, and the checker has to see the name. Read once
|
|
507
|
+
// with `void`, as a `catch` binding is never reported unused. An arm that names none binds
|
|
508
|
+
// nothing.
|
|
505
509
|
if (node.block === 'try') {
|
|
506
510
|
const [tried, caught] = node.branches as [Branch, Branch]
|
|
507
511
|
text += '{\n'
|
|
508
512
|
inBody(tried)
|
|
509
|
-
text +=
|
|
513
|
+
text +=
|
|
514
|
+
node.error === ''
|
|
515
|
+
? '}\n{\n'
|
|
516
|
+
: `}\n{ const ${node.error}: any = undefined; void ${node.error}\n`
|
|
510
517
|
inBody(caught)
|
|
511
518
|
text += '}\n'
|
|
512
519
|
continue
|
|
@@ -4,18 +4,19 @@ import {
|
|
|
4
4
|
isVariableDeclaration,
|
|
5
5
|
} from 'typescript/unstable/ast/is'
|
|
6
6
|
import {
|
|
7
|
-
type Symbol as CheckedSymbol,
|
|
8
7
|
type Checker,
|
|
9
8
|
ObjectFlags,
|
|
10
9
|
type ObjectType,
|
|
11
10
|
type Project,
|
|
12
11
|
SignatureKind,
|
|
12
|
+
type Symbol as CheckedSymbol,
|
|
13
13
|
type Type,
|
|
14
14
|
TypeFlags,
|
|
15
15
|
type TypeParameter,
|
|
16
16
|
type TypeReference,
|
|
17
17
|
type UnionOrIntersectionType,
|
|
18
18
|
} from 'typescript/unstable/async'
|
|
19
|
+
|
|
19
20
|
import { aliasedSymbol } from './aliasedSymbol.ts'
|
|
20
21
|
|
|
21
22
|
// WHAT THE CHECKER SAYS A NAME HOLDS, asked by both of the session's jobs over one snapshot: the
|
package/src/compiler/readsIn.ts
CHANGED
|
@@ -26,13 +26,14 @@ import {
|
|
|
26
26
|
isVariableStatement,
|
|
27
27
|
} from 'typescript/unstable/ast/is'
|
|
28
28
|
import {
|
|
29
|
-
type Symbol as CheckedSymbol,
|
|
30
29
|
type Checker,
|
|
31
30
|
SignatureKind,
|
|
31
|
+
type Symbol as CheckedSymbol,
|
|
32
32
|
SymbolFlags,
|
|
33
33
|
type Type,
|
|
34
34
|
TypeFlags,
|
|
35
35
|
} from 'typescript/unstable/async'
|
|
36
|
+
|
|
36
37
|
import { aliasedSymbol } from './aliasedSymbol.ts'
|
|
37
38
|
import { CheckerAnswer } from './CheckerAnswer.ts'
|
|
38
39
|
import {
|
|
@@ -45,13 +46,13 @@ import {
|
|
|
45
46
|
TRANSPARENT,
|
|
46
47
|
} from './classifyRead.ts'
|
|
47
48
|
import type { Finding } from './Finding.ts'
|
|
49
|
+
import type { AbideFile } from './parseAbide.ts'
|
|
48
50
|
import {
|
|
49
51
|
holdsReactive,
|
|
50
52
|
type Position,
|
|
51
53
|
type PositionKind,
|
|
52
54
|
type PositionTable,
|
|
53
55
|
} from './PositionTable.ts'
|
|
54
|
-
import type { AbideFile } from './parseAbide.ts'
|
|
55
56
|
import { INLINE_PROPS, type Span } from './projectShadow.ts'
|
|
56
57
|
import { REACTIVE_MEMBERS } from './REACTIVE_MEMBERS.ts'
|
|
57
58
|
import { optionNamed, reactiveOptions } from './reactiveOptions.ts'
|
|
@@ -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
|
|