@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11
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/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +62 -64
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/src/json-document.ts
CHANGED
|
@@ -1,122 +1,276 @@
|
|
|
1
|
+
import type { Position } from "./position.ts";
|
|
2
|
+
|
|
3
|
+
/** A place inside a document: each member name a string, each array index a number. */
|
|
4
|
+
export type Place = readonly (string | number)[];
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
|
-
* A
|
|
3
|
-
*
|
|
7
|
+
* A text that is not one strict JSON document: a syntax error, data after the value, a
|
|
8
|
+
* member one object writes twice, or nesting past {@link MAX_DEPTH}. `place` is the
|
|
9
|
+
* value the fault is in, followed by the member written twice.
|
|
4
10
|
*/
|
|
11
|
+
export class StrictJSONError extends Error {
|
|
12
|
+
readonly offset: number;
|
|
13
|
+
readonly place: Place;
|
|
5
14
|
|
|
6
|
-
|
|
15
|
+
constructor(reason: string, offset: number, place: Place) {
|
|
16
|
+
super(reason);
|
|
17
|
+
this.name = "StrictJSONError";
|
|
18
|
+
this.offset = offset;
|
|
19
|
+
this.place = place;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
7
22
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
readonly
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
/** One strict JSON document, read whole. */
|
|
24
|
+
interface StrictDocument {
|
|
25
|
+
readonly value: unknown;
|
|
26
|
+
/**
|
|
27
|
+
* The text of every number written with a fraction or an exponent, by its JSON
|
|
28
|
+
* Pointer: a value reads `100.0` as the integer 100, so only the text tells them apart.
|
|
29
|
+
*/
|
|
30
|
+
readonly fractions: ReadonlyMap<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* The offset of each value inside the top-level member a read names, by its index or
|
|
33
|
+
* its member name, in document order.
|
|
34
|
+
*/
|
|
35
|
+
readonly starts: ReadonlyMap<string | number, number>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* How deep values may nest. A document this analyzer reads nests a few levels, and a
|
|
40
|
+
* bound turns a document built to exhaust the stack into a refusal.
|
|
41
|
+
*/
|
|
42
|
+
const MAX_DEPTH = 1000;
|
|
43
|
+
|
|
44
|
+
const SPACE = /[ \t\n\r]*/uy;
|
|
45
|
+
const STRING =
|
|
46
|
+
/"(?:[ !#$%&'()*+,\-./0-9:;<=>?@A-Z[\]-\u{10FFFF}]|\\(?:["\\/bfnrt]|u[0-9A-Fa-f]{4}))*"/uy;
|
|
47
|
+
const NUMBER = /-?(?:0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/uy;
|
|
48
|
+
const LITERALS: ReadonlyMap<string, unknown> = new Map<string, unknown>([
|
|
49
|
+
["true", true],
|
|
50
|
+
["false", false],
|
|
51
|
+
["null", null],
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
/** One member name spelled as a JSON Pointer token. */
|
|
55
|
+
const escaped = (name: string): string => name.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
56
|
+
|
|
57
|
+
/** The member names and indexes a JSON Pointer holds, unescaped. */
|
|
58
|
+
export function pointerSegments(pointer: string): string[] {
|
|
59
|
+
return pointer === ""
|
|
60
|
+
? []
|
|
61
|
+
: pointer
|
|
62
|
+
.slice(1)
|
|
63
|
+
.split("/")
|
|
64
|
+
.map((token) => token.replaceAll("~1", "/").replaceAll("~0", "~"));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The JSON Pointer of a place written as segments. */
|
|
68
|
+
export function pointerOf(segments: readonly string[]): string {
|
|
69
|
+
return segments.map((segment) => `/${escaped(segment)}`).join("");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The place a JSON Pointer names inside `value`, followed by `member` where it is not
|
|
74
|
+
* empty: a pointer's text does not say whether a segment of digits is an index, so the
|
|
75
|
+
* value it points into does.
|
|
76
|
+
*/
|
|
77
|
+
export function placeIn(value: unknown, pointer: string, member = ""): (string | number)[] {
|
|
78
|
+
const place: (string | number)[] = [];
|
|
79
|
+
let held = value;
|
|
80
|
+
for (const segment of pointerSegments(pointer)) {
|
|
81
|
+
if (Array.isArray(held)) {
|
|
82
|
+
place.push(Number(segment));
|
|
83
|
+
held = held[Number(segment)];
|
|
84
|
+
} else {
|
|
85
|
+
place.push(segment);
|
|
86
|
+
held = isRecord(held) ? held[segment] : undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return member === "" ? place : [...place, member];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A place as a refusal names it: member names joined by full stops and an index in
|
|
94
|
+
* square brackets, as `ts.serializers[0].returns`.
|
|
95
|
+
*/
|
|
96
|
+
export function dotted(place: Place): string {
|
|
97
|
+
let path = "";
|
|
98
|
+
for (const segment of place) {
|
|
99
|
+
path +=
|
|
100
|
+
typeof segment === "number" ? `[${String(segment)}]` : path === "" ? segment : `.${segment}`;
|
|
101
|
+
}
|
|
102
|
+
return path;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
class Reader {
|
|
15
106
|
private at = 0;
|
|
107
|
+
private readonly path: (string | number)[] = [];
|
|
108
|
+
readonly fractions = new Map<string, string>();
|
|
109
|
+
readonly starts = new Map<string | number, number>();
|
|
110
|
+
private readonly text: string;
|
|
111
|
+
private readonly records: string | undefined;
|
|
16
112
|
|
|
17
|
-
constructor(text: string,
|
|
113
|
+
constructor(text: string, records: string | undefined) {
|
|
18
114
|
this.text = text;
|
|
19
|
-
this.
|
|
115
|
+
this.records = records;
|
|
20
116
|
}
|
|
21
117
|
|
|
22
|
-
|
|
23
|
-
this.value(
|
|
118
|
+
read(): unknown {
|
|
119
|
+
const value = this.value(0);
|
|
120
|
+
this.match(SPACE);
|
|
121
|
+
if (this.at < this.text.length) {
|
|
122
|
+
this.fail("data follows the document's value");
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
24
125
|
}
|
|
25
126
|
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
127
|
+
private fail(reason: string, offset = this.at, member = ""): never {
|
|
128
|
+
throw new StrictJSONError(
|
|
129
|
+
reason,
|
|
130
|
+
offset,
|
|
131
|
+
member === "" ? [...this.path] : [...this.path, member],
|
|
132
|
+
);
|
|
30
133
|
}
|
|
31
134
|
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
135
|
+
private match(pattern: RegExp): string | undefined {
|
|
136
|
+
pattern.lastIndex = this.at;
|
|
137
|
+
const found = pattern.exec(this.text)?.[0];
|
|
138
|
+
this.at = found === undefined ? this.at : pattern.lastIndex;
|
|
139
|
+
return found;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private expect(char: string, what: string): void {
|
|
143
|
+
this.match(SPACE);
|
|
144
|
+
if (this.text[this.at] !== char) {
|
|
145
|
+
this.fail(`${what} is expected`);
|
|
37
146
|
}
|
|
38
147
|
this.at += 1;
|
|
39
|
-
return JSON.parse(this.text.slice(start, this.at)) as string;
|
|
40
148
|
}
|
|
41
149
|
|
|
42
|
-
private value(
|
|
43
|
-
this.
|
|
44
|
-
if (
|
|
45
|
-
this.
|
|
150
|
+
private value(depth: number): unknown {
|
|
151
|
+
this.match(SPACE);
|
|
152
|
+
if (depth > MAX_DEPTH) {
|
|
153
|
+
this.fail(`values nest deeper than ${String(MAX_DEPTH)} levels`);
|
|
46
154
|
}
|
|
47
155
|
const char = this.text[this.at];
|
|
48
156
|
if (char === "{") {
|
|
49
|
-
this.object(
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
157
|
+
return this.object(depth);
|
|
158
|
+
}
|
|
159
|
+
if (char === "[") {
|
|
160
|
+
return this.array(depth);
|
|
161
|
+
}
|
|
162
|
+
const string = this.match(STRING);
|
|
163
|
+
if (string !== undefined) {
|
|
164
|
+
return JSON.parse(string) as string;
|
|
165
|
+
}
|
|
166
|
+
NUMBER.lastIndex = this.at;
|
|
167
|
+
const number = NUMBER.exec(this.text);
|
|
168
|
+
if (number !== null) {
|
|
169
|
+
this.at = NUMBER.lastIndex;
|
|
170
|
+
if (number[1] !== undefined || number[2] !== undefined) {
|
|
171
|
+
this.fractions.set(pointerOf(this.path.map(String)), number[0]);
|
|
57
172
|
}
|
|
173
|
+
return Number(number[0]);
|
|
58
174
|
}
|
|
175
|
+
for (const [literal, held] of LITERALS) {
|
|
176
|
+
if (this.text.startsWith(literal, this.at)) {
|
|
177
|
+
this.at += literal.length;
|
|
178
|
+
return held;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return this.fail(
|
|
182
|
+
char === undefined ? "the document ends where a value is expected" : "a value is expected",
|
|
183
|
+
);
|
|
59
184
|
}
|
|
60
185
|
|
|
61
|
-
private object(
|
|
62
|
-
const
|
|
186
|
+
private object(depth: number): Record<string, unknown> {
|
|
187
|
+
const held: Record<string, unknown> = {};
|
|
63
188
|
this.at += 1;
|
|
189
|
+
this.match(SPACE);
|
|
190
|
+
if (this.text[this.at] === "}") {
|
|
191
|
+
this.at += 1;
|
|
192
|
+
return held;
|
|
193
|
+
}
|
|
64
194
|
for (;;) {
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
195
|
+
this.match(SPACE);
|
|
196
|
+
const offset = this.at;
|
|
197
|
+
const spelled = this.match(STRING);
|
|
198
|
+
if (spelled === undefined) {
|
|
199
|
+
return this.fail("a member name is expected");
|
|
69
200
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.
|
|
201
|
+
const name = JSON.parse(spelled) as string;
|
|
202
|
+
if (Object.hasOwn(held, name)) {
|
|
203
|
+
this.fail(
|
|
204
|
+
`the member ${JSON.stringify(name)} is written twice, so neither value is chosen`,
|
|
205
|
+
offset,
|
|
206
|
+
name,
|
|
207
|
+
);
|
|
73
208
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.
|
|
209
|
+
this.expect(":", "a colon after the member name");
|
|
210
|
+
if (depth === 1 && this.path[0] === this.records) {
|
|
211
|
+
this.match(SPACE);
|
|
212
|
+
this.starts.set(name, this.at);
|
|
78
213
|
}
|
|
79
|
-
|
|
80
|
-
|
|
214
|
+
this.path.push(name);
|
|
215
|
+
Object.defineProperty(held, name, {
|
|
216
|
+
value: this.value(depth + 1),
|
|
217
|
+
enumerable: true,
|
|
218
|
+
writable: true,
|
|
219
|
+
configurable: true,
|
|
220
|
+
});
|
|
221
|
+
this.path.pop();
|
|
222
|
+
this.match(SPACE);
|
|
223
|
+
const next = this.text[this.at];
|
|
81
224
|
this.at += 1;
|
|
82
|
-
|
|
225
|
+
if (next === "}") {
|
|
226
|
+
return held;
|
|
227
|
+
}
|
|
228
|
+
if (next !== ",") {
|
|
229
|
+
this.at -= 1;
|
|
230
|
+
this.fail("a comma or a closing brace is expected");
|
|
231
|
+
}
|
|
83
232
|
}
|
|
84
233
|
}
|
|
85
234
|
|
|
86
|
-
private
|
|
235
|
+
private array(depth: number): unknown[] {
|
|
236
|
+
const held: unknown[] = [];
|
|
237
|
+
const records = depth === 1 && this.path[0] === this.records;
|
|
87
238
|
this.at += 1;
|
|
239
|
+
this.match(SPACE);
|
|
240
|
+
if (this.text[this.at] === "]") {
|
|
241
|
+
this.at += 1;
|
|
242
|
+
return held;
|
|
243
|
+
}
|
|
88
244
|
for (;;) {
|
|
89
|
-
this.
|
|
90
|
-
if (
|
|
91
|
-
this.at
|
|
92
|
-
|
|
245
|
+
this.match(SPACE);
|
|
246
|
+
if (records) {
|
|
247
|
+
this.starts.set(held.length, this.at);
|
|
248
|
+
}
|
|
249
|
+
this.path.push(held.length);
|
|
250
|
+
held.push(this.value(depth + 1));
|
|
251
|
+
this.path.pop();
|
|
252
|
+
this.match(SPACE);
|
|
253
|
+
const next = this.text[this.at];
|
|
254
|
+
this.at += 1;
|
|
255
|
+
if (next === "]") {
|
|
256
|
+
return held;
|
|
93
257
|
}
|
|
94
|
-
if (
|
|
95
|
-
this.at
|
|
258
|
+
if (next !== ",") {
|
|
259
|
+
this.at -= 1;
|
|
260
|
+
this.fail("a comma or a closing bracket is expected");
|
|
96
261
|
}
|
|
97
|
-
this.value(element);
|
|
98
262
|
}
|
|
99
263
|
}
|
|
100
264
|
}
|
|
101
265
|
|
|
102
|
-
/** What a walk of one document found. */
|
|
103
|
-
interface DocumentWalk {
|
|
104
|
-
/** The offset of each element of the document's array member, in order: an object's `{`. */
|
|
105
|
-
readonly starts: readonly number[];
|
|
106
|
-
/** The first member one object writes twice, at any depth, as the offset of its name. */
|
|
107
|
-
readonly twice: { readonly name: string; readonly offset: number } | undefined;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
266
|
/**
|
|
111
|
-
*
|
|
112
|
-
* records
|
|
113
|
-
* later value silently replacing the earlier one, so a document holding one is one a
|
|
114
|
-
* reader names rather than reads.
|
|
267
|
+
* Reads `text` as one strict JSON document, or throws a {@link StrictJSONError}.
|
|
268
|
+
* `records` names the top-level member whose values' offsets the read keeps.
|
|
115
269
|
*/
|
|
116
|
-
export function
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
return {
|
|
270
|
+
export function readStrict(text: string, records?: string): StrictDocument {
|
|
271
|
+
const reader = new Reader(text, records);
|
|
272
|
+
const value = reader.read();
|
|
273
|
+
return { value, fractions: reader.fractions, starts: reader.starts };
|
|
120
274
|
}
|
|
121
275
|
|
|
122
276
|
/**
|
|
@@ -128,3 +282,14 @@ export function positionAt(text: string, file: string, offset: number): Position
|
|
|
128
282
|
const lineStart = before.lastIndexOf("\n") + 1;
|
|
129
283
|
return { path: file, line: before.split("\n").length, column: offset - lineStart + 1 };
|
|
130
284
|
}
|
|
285
|
+
|
|
286
|
+
/** A strict read's fault with the line and column it is at, as a refusal states it. */
|
|
287
|
+
export function locatedFault(text: string, error: StrictJSONError): string {
|
|
288
|
+
const { line, column } = positionAt(text, "", error.offset);
|
|
289
|
+
return `line ${String(line)}, column ${String(column)}: ${error.message}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Whether a decoded JSON value is an object: not `null` and not an array. */
|
|
293
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
294
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
295
|
+
}
|
package/src/keyof-comparisons.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
isCaseClause,
|
|
11
11
|
isElementAccessExpression,
|
|
12
12
|
isIdentifier,
|
|
13
|
-
isParenthesizedExpression,
|
|
14
13
|
isPropertyAccessExpression,
|
|
15
14
|
isSwitchStatement,
|
|
16
15
|
isTypeOperatorNode,
|
|
@@ -27,6 +26,7 @@ import { renderPosition } from "./position.ts";
|
|
|
27
26
|
import { UNANSWERED } from "./query.ts";
|
|
28
27
|
import type { Reference } from "./references.ts";
|
|
29
28
|
import type { ProjectView } from "./session.ts";
|
|
29
|
+
import { parenthesesRemoved } from "./unwrap.ts";
|
|
30
30
|
|
|
31
31
|
/** The operators that compare their operands for equality. */
|
|
32
32
|
const EQUALITIES: ReadonlySet<SyntaxKind> = new Set([
|
|
@@ -44,13 +44,9 @@ interface Comparison {
|
|
|
44
44
|
readonly other: Expression;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function unwrapped(node: Expression): Expression {
|
|
48
|
-
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
47
|
/** The name node an operand's symbol is read at: an identifier, or a property access's name. */
|
|
52
48
|
function nameOf(node: Expression): Node | undefined {
|
|
53
|
-
const at =
|
|
49
|
+
const at = parenthesesRemoved(node);
|
|
54
50
|
if (isIdentifier(at)) {
|
|
55
51
|
return at;
|
|
56
52
|
}
|
|
@@ -132,7 +128,7 @@ export function keyofComparisonReferences<Brand>(
|
|
|
132
128
|
}
|
|
133
129
|
const named = comparisons.flatMap((one) => {
|
|
134
130
|
const name = nameOf(one.keyed);
|
|
135
|
-
return name === undefined || isElementAccessExpression(
|
|
131
|
+
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
136
132
|
? []
|
|
137
133
|
: [{ ...one, name }];
|
|
138
134
|
});
|