@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.10
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 +4 -3
- 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/dist/src/components.js
CHANGED
|
@@ -4,13 +4,12 @@
|
|
|
4
4
|
* and every dead declaration that falls with them, being dead only through them.
|
|
5
5
|
*/
|
|
6
6
|
import { OUTSIDE } from "./graph.js";
|
|
7
|
-
|
|
8
|
-
const ANALYZER = "deadset-ts";
|
|
7
|
+
import { PRODUCT } from "./product.js";
|
|
9
8
|
/** The fewest digits a component identifier's number is written with. */
|
|
10
9
|
const ID_DIGITS = 4;
|
|
11
10
|
/** The identifier of the component at one place of the run's order, counted from one. */
|
|
12
11
|
export function componentId(place) {
|
|
13
|
-
return `${
|
|
12
|
+
return `${PRODUCT}/c-${String(place).padStart(ID_DIGITS, "0")}`;
|
|
14
13
|
}
|
|
15
14
|
/** The index of a declaration the component walk has not reached. */
|
|
16
15
|
const UNVISITED = -1;
|
package/dist/src/config.js
CHANGED
|
@@ -1,74 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SEVERITIES } from "./contract/contract.generated.js";
|
|
2
|
+
import { LANGUAGE } from "./product.js";
|
|
2
3
|
/**
|
|
3
4
|
* The name of the repository configuration at the target root, which is also the
|
|
4
5
|
* document a finding about the configuration names where no document of the
|
|
5
6
|
* target supplied the setting it is about.
|
|
6
7
|
*/
|
|
7
8
|
export const REPOSITORY_DOCUMENT = "deadset.json";
|
|
8
|
-
/** The kind a target kind setting gives
|
|
9
|
-
export function kindOf(
|
|
10
|
-
return typeof
|
|
9
|
+
/** The kind a target kind setting gives this analyzer's declarations. */
|
|
10
|
+
export function kindOf(kind) {
|
|
11
|
+
return typeof kind === "string" ? kind : kind[LANGUAGE];
|
|
11
12
|
}
|
|
12
|
-
/** The severities ranked, the strongest highest. */
|
|
13
|
-
const SEVERITY_RANK = { allow: 0, warn: 1, deny: 2 };
|
|
14
13
|
/** Whether a finding at one severity fails a run under the failing severity. */
|
|
15
14
|
export function fails(severity, failOn) {
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* The configuration every documented default states. `targetKind` is empty: the
|
|
20
|
-
* field has no default and is never inferred.
|
|
21
|
-
*/
|
|
22
|
-
export function defaultConfig() {
|
|
23
|
-
return {
|
|
24
|
-
contractVersion: CONTRACT_VERSION,
|
|
25
|
-
targetKind: "",
|
|
26
|
-
analysis: {
|
|
27
|
-
languages: [],
|
|
28
|
-
minConfidence: "probable",
|
|
29
|
-
generatedFiles: "exclude",
|
|
30
|
-
consumerTests: "test",
|
|
31
|
-
configurations: [],
|
|
32
|
-
matrixComplete: false,
|
|
33
|
-
templateDirs: [],
|
|
34
|
-
templateDelimiters: { left: "{{", right: "}}" },
|
|
35
|
-
},
|
|
36
|
-
consumersComplete: false,
|
|
37
|
-
rootPatterns: [],
|
|
38
|
-
severity: new Map(),
|
|
39
|
-
exemptionsDisabled: [],
|
|
40
|
-
reporters: {
|
|
41
|
-
formats: ["text"],
|
|
42
|
-
sort: "position",
|
|
43
|
-
cascade: "roots",
|
|
44
|
-
maxFindings: 0,
|
|
45
|
-
failOn: "deny",
|
|
46
|
-
},
|
|
47
|
-
providers: {
|
|
48
|
-
analyzers: [
|
|
49
|
-
{ shape: "installed", name: "deadset-go", languages: ["go"], command: "deadset-go" },
|
|
50
|
-
{ shape: "installed", name: "deadset-ts", languages: ["ts"], command: "deadset-ts" },
|
|
51
|
-
],
|
|
52
|
-
},
|
|
53
|
-
ts: {
|
|
54
|
-
testFiles: [
|
|
55
|
-
"**/*.test.{ts,tsx,mts,cts}",
|
|
56
|
-
"**/*.test-d.{ts,tsx,mts,cts}",
|
|
57
|
-
"**/*.spec-d.{ts,tsx,mts,cts}",
|
|
58
|
-
"**/*.spec.*",
|
|
59
|
-
"**/__tests__/**",
|
|
60
|
-
"**/__mocks__/**",
|
|
61
|
-
"**/test-d/**",
|
|
62
|
-
],
|
|
63
|
-
entryFiles: [],
|
|
64
|
-
componentExtensions: [".vue", ".svelte", ".astro"],
|
|
65
|
-
disabledConventions: [],
|
|
66
|
-
injectionRegistrations: [],
|
|
67
|
-
lifecycleContracts: [],
|
|
68
|
-
serializers: [],
|
|
69
|
-
rootsComplete: false,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
15
|
+
return SEVERITIES.indexOf(severity) >= SEVERITIES.indexOf(failOn);
|
|
72
16
|
}
|
|
73
17
|
/**
|
|
74
18
|
* Renders the origin as the provenance value the Contract declares: `default`,
|
|
@@ -77,23 +21,3 @@ export function defaultConfig() {
|
|
|
77
21
|
export function renderOrigin(origin) {
|
|
78
22
|
return origin.source === "default" ? "default" : `${origin.source}: ${origin.label}`;
|
|
79
23
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Every refusal configuration resolution makes. Each maps to the usage exit
|
|
82
|
-
* code, and each is thrown rather than returned: a refusal ends the run, and the
|
|
83
|
-
* one caller that turns it into an exit code is the command line.
|
|
84
|
-
*/
|
|
85
|
-
export class ConfigError extends Error {
|
|
86
|
-
/** Which refusal this is. */
|
|
87
|
-
kind;
|
|
88
|
-
/**
|
|
89
|
-
* The key or field the message names, spelled as the document spells it. It is
|
|
90
|
-
* empty when the refusal names none.
|
|
91
|
-
*/
|
|
92
|
-
key;
|
|
93
|
-
constructor(kind, key, message) {
|
|
94
|
-
super(message);
|
|
95
|
-
this.name = "ConfigError";
|
|
96
|
-
this.kind = kind;
|
|
97
|
-
this.key = key;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
* uses it and each relative one that names a source file of the program roots the file.
|
|
8
8
|
*/
|
|
9
9
|
import { isCallExpression, isExportDeclaration, isIdentifier, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
10
|
-
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
11
|
-
|
|
10
|
+
import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
11
|
+
import { bytewise } from "./text/compare.js";
|
|
12
|
+
import { isRecord } from "./json-document.js";
|
|
13
|
+
import { filesIn } from "./fs/tree.js";
|
|
14
|
+
import { DEPENDENCY_SECTIONS, holdsManifest, inInstalledPackages, manifestAt, readJSON, } from "./fs/manifest.js";
|
|
15
|
+
import { shellWords } from "./text/shell.js";
|
|
12
16
|
/** A configuration module's name, each form with the spelling a root reports it by. */
|
|
13
17
|
const MODULE_FORMS = [
|
|
14
18
|
{ name: /^[\w-]+\.config\.(?:js|mjs|cjs|ts|mts|cts)$/u, form: "<stem>.config.<ext>" },
|
|
@@ -20,13 +24,6 @@ const MODULE_FORMS = [
|
|
|
20
24
|
];
|
|
21
25
|
/** A JSON configuration file's name. */
|
|
22
26
|
const DOCUMENT_NAME = /^(?:[\w-]+(?:\.[\w-]+)?\.config\.json|\.[\w-]+rc(?:\.json)?)$/u;
|
|
23
|
-
/** The manifest members a dependency is declared in. */
|
|
24
|
-
const DEPENDENCY_MEMBERS = [
|
|
25
|
-
"dependencies",
|
|
26
|
-
"devDependencies",
|
|
27
|
-
"peerDependencies",
|
|
28
|
-
"optionalDependencies",
|
|
29
|
-
];
|
|
30
27
|
/** The prefixes a string names a file with, relative to the file that writes it. */
|
|
31
28
|
const RELATIVE_PREFIXES = ["./", "../"];
|
|
32
29
|
/** The JavaScript extensions a relative specifier from a configuration file stands for TypeScript under. */
|
|
@@ -50,18 +47,14 @@ export const RESOLVED_EXTENSIONS = [
|
|
|
50
47
|
".mjs",
|
|
51
48
|
".cjs",
|
|
52
49
|
];
|
|
53
|
-
/** The directory a package manager installs packages into, whose files are no project's own. */
|
|
54
|
-
const MODULES_DIR = "node_modules";
|
|
55
50
|
/** The file's name, the last segment of its path. */
|
|
56
51
|
function basename(path) {
|
|
57
52
|
return path.slice(path.lastIndexOf("/") + 1);
|
|
58
53
|
}
|
|
59
54
|
/** Whether one path is the project's own: below the target root and in no `node_modules`. */
|
|
60
55
|
function isProjectPath(root, path) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
function isRecord(value) {
|
|
64
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
56
|
+
const below = relativePath(root, path);
|
|
57
|
+
return below !== undefined && !inInstalledPackages(below);
|
|
65
58
|
}
|
|
66
59
|
/** The form one file name is as a configuration module, or undefined where it is none. */
|
|
67
60
|
export function configurationModuleForm(name) {
|
|
@@ -75,122 +68,13 @@ export function isConfigurationDocumentName(name) {
|
|
|
75
68
|
const CONFIG_FLAGS = new Set(["--config", "-c"]);
|
|
76
69
|
/** The extensions of a module a configuration flag may name. */
|
|
77
70
|
const MODULE_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"];
|
|
78
|
-
/** The characters that end a word outside quotes and separate nothing: blanks. */
|
|
79
|
-
// Stryker disable next-line Regex: its \v to \V mutant is no valid escape under the u flag, so the whole module fails to parse
|
|
80
|
-
const BLANK = /[ \t\r\f\v]/u;
|
|
81
|
-
/** The characters that end a word outside quotes and begin an operator: newline included. */
|
|
82
|
-
const OPERATOR = /[\n;&|()<>]/u;
|
|
83
|
-
/** The characters a backslash escapes inside double quotes; before any other it stands for itself. */
|
|
84
|
-
const DOUBLE_QUOTE_ESCAPES = new Set(["$", "`", '"', "\\"]);
|
|
85
|
-
/**
|
|
86
|
-
* The words of one command line, as POSIX token recognition reads quotes, backslashes,
|
|
87
|
-
* line continuations, comments and operators. A word an expansion may change (an unquoted
|
|
88
|
-
* `$`, backquote, glob character or leading `~`, or a `$` or backquote in double quotes)
|
|
89
|
-
* is computed, and so is the rest of a line whose quote is never closed. A computed word
|
|
90
|
-
* and each operator are undefined, so a flag's file is never read across them.
|
|
91
|
-
*/
|
|
92
|
-
function shellWords(command) {
|
|
93
|
-
const words = [];
|
|
94
|
-
let word;
|
|
95
|
-
let computed = false;
|
|
96
|
-
const end = () => {
|
|
97
|
-
if (word !== undefined) {
|
|
98
|
-
words.push(computed ? undefined : word);
|
|
99
|
-
}
|
|
100
|
-
word = undefined;
|
|
101
|
-
computed = false;
|
|
102
|
-
};
|
|
103
|
-
for (let at = 0; at < command.length; at += 1) {
|
|
104
|
-
const char = command.charAt(at);
|
|
105
|
-
if (char === "\\" && command.charAt(at + 1) === "\n") {
|
|
106
|
-
at += 1;
|
|
107
|
-
}
|
|
108
|
-
else if (BLANK.test(char)) {
|
|
109
|
-
end();
|
|
110
|
-
}
|
|
111
|
-
else if (OPERATOR.test(char)) {
|
|
112
|
-
end();
|
|
113
|
-
words.push(undefined);
|
|
114
|
-
}
|
|
115
|
-
else if (char === "#" && word === undefined) {
|
|
116
|
-
const newline = command.indexOf("\n", at);
|
|
117
|
-
at = newline < 0 ? command.length : newline - 1;
|
|
118
|
-
}
|
|
119
|
-
else if (char === "\\") {
|
|
120
|
-
at += 1;
|
|
121
|
-
word = (word ?? "") + command.charAt(at);
|
|
122
|
-
}
|
|
123
|
-
else if (char === "'" || char === '"') {
|
|
124
|
-
const close = closingQuote(command, at);
|
|
125
|
-
if (close < 0) {
|
|
126
|
-
word ??= "";
|
|
127
|
-
computed = true;
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
const quoted = char === "'"
|
|
131
|
-
? { text: command.slice(at + 1, close), computed: false }
|
|
132
|
-
: doubleQuoted(command.slice(at + 1, close));
|
|
133
|
-
word = (word ?? "") + quoted.text;
|
|
134
|
-
computed ||= quoted.computed;
|
|
135
|
-
at = close;
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
computed ||= /[$`*?[]/u.test(char) || (char === "~" && word === undefined);
|
|
139
|
-
word = (word ?? "") + char;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
end();
|
|
143
|
-
return words;
|
|
144
|
-
}
|
|
145
|
-
/** The text between one pair of double quotes, unescaped, and whether an expansion may change it. */
|
|
146
|
-
function doubleQuoted(inner) {
|
|
147
|
-
let text = "";
|
|
148
|
-
let computed = false;
|
|
149
|
-
for (let at = 0; at < inner.length; at += 1) {
|
|
150
|
-
const char = inner.charAt(at);
|
|
151
|
-
const next = inner.charAt(at + 1);
|
|
152
|
-
if (char === "\\" && next === "\n") {
|
|
153
|
-
at += 1;
|
|
154
|
-
}
|
|
155
|
-
else if (char === "\\" && DOUBLE_QUOTE_ESCAPES.has(next)) {
|
|
156
|
-
text += next;
|
|
157
|
-
at += 1;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
computed ||= char === "$" || char === "`";
|
|
161
|
-
text += char;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return { text, computed };
|
|
165
|
-
}
|
|
166
|
-
/** The index of the quote that closes the one at `open`, or -1 where none does. */
|
|
167
|
-
function closingQuote(command, open) {
|
|
168
|
-
const quote = command.charAt(open);
|
|
169
|
-
for (let at = open + 1; at < command.length; at += 1) {
|
|
170
|
-
const char = command.charAt(at);
|
|
171
|
-
if (char === quote) {
|
|
172
|
-
return at;
|
|
173
|
-
}
|
|
174
|
-
if (quote === '"' && char === "\\") {
|
|
175
|
-
at += 1;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return -1;
|
|
179
|
-
}
|
|
180
71
|
/**
|
|
181
72
|
* The files the scripts of the manifest in one directory name after a configuration flag,
|
|
182
73
|
* `--config <file>`, `-c <file>` or `--config=<file>`, each a JSON file or a module below
|
|
183
74
|
* the target root and in no `node_modules`.
|
|
184
75
|
*/
|
|
185
76
|
function scriptConfigurations(host, root, dir) {
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
const manifest = JSON.parse(host.readFile(joinPath(dir, MANIFEST)));
|
|
189
|
-
scripts = isRecord(manifest) ? manifest["scripts"] : undefined;
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
return [];
|
|
193
|
-
}
|
|
77
|
+
const scripts = manifestAt(host, dir)?.["scripts"];
|
|
194
78
|
if (!isRecord(scripts)) {
|
|
195
79
|
return [];
|
|
196
80
|
}
|
|
@@ -217,15 +101,6 @@ function scriptConfigurations(host, root, dir) {
|
|
|
217
101
|
}
|
|
218
102
|
return found;
|
|
219
103
|
}
|
|
220
|
-
/** The decoded value of one JSON file, or undefined where it cannot be read or parsed. */
|
|
221
|
-
function readDocument(host, path) {
|
|
222
|
-
try {
|
|
223
|
-
return JSON.parse(host.readFile(path));
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
return undefined;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
104
|
/** Every string value one decoded JSON value holds at any depth. */
|
|
230
105
|
export function documentStrings(value) {
|
|
231
106
|
if (typeof value === "string") {
|
|
@@ -258,10 +133,10 @@ export function moduleStrings(file) {
|
|
|
258
133
|
export function configurationFiles(host, targetRoot, files, parse) {
|
|
259
134
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
260
135
|
const manifests = new Map();
|
|
261
|
-
const
|
|
136
|
+
const manifestHeld = (dir) => {
|
|
262
137
|
let held = manifests.get(dir);
|
|
263
138
|
if (held === undefined) {
|
|
264
|
-
held = host
|
|
139
|
+
held = holdsManifest(host, dir);
|
|
265
140
|
manifests.set(dir, held);
|
|
266
141
|
}
|
|
267
142
|
return held;
|
|
@@ -271,30 +146,23 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
271
146
|
for (const file of files.byPath.values()) {
|
|
272
147
|
const form = configurationModuleForm(basename(file.fileName));
|
|
273
148
|
const fileDir = dirnamePath(file.fileName);
|
|
274
|
-
if (form !== undefined &&
|
|
149
|
+
if (form !== undefined && manifestHeld(fileDir)) {
|
|
275
150
|
modules.push({ file, form, manifestDir: fileDir });
|
|
276
151
|
}
|
|
277
|
-
for (
|
|
152
|
+
for (const dir of ancestors(dirnamePath(file.fileName), root)) {
|
|
153
|
+
if (dirs.has(dir)) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
278
156
|
dirs.add(dir);
|
|
279
157
|
}
|
|
280
158
|
}
|
|
281
159
|
const pending = [];
|
|
282
160
|
const named = [];
|
|
283
|
-
for (const dir of [...dirs].sort()) {
|
|
284
|
-
if (!
|
|
161
|
+
for (const dir of [...dirs].sort(bytewise)) {
|
|
162
|
+
if (!manifestHeld(dir)) {
|
|
285
163
|
continue;
|
|
286
164
|
}
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
names = host
|
|
290
|
-
.readDirectory(dir)
|
|
291
|
-
.filter((entry) => !entry.directory && isConfigurationDocumentName(entry.name))
|
|
292
|
-
.map((entry) => entry.name)
|
|
293
|
-
.sort();
|
|
294
|
-
}
|
|
295
|
-
catch {
|
|
296
|
-
names = [];
|
|
297
|
-
}
|
|
165
|
+
const names = filesIn(host, dir, isConfigurationDocumentName);
|
|
298
166
|
pending.push(...names.map((name) => ({ path: joinPath(dir, name), manifestDir: dir })));
|
|
299
167
|
for (const path of scriptConfigurations(host, root, dir)) {
|
|
300
168
|
const held = files.byName.get(path);
|
|
@@ -318,7 +186,7 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
318
186
|
continue;
|
|
319
187
|
}
|
|
320
188
|
if (document === undefined) {
|
|
321
|
-
const value =
|
|
189
|
+
const value = readJSON(host, next.path);
|
|
322
190
|
if (value === undefined) {
|
|
323
191
|
unreadable.add(next.path);
|
|
324
192
|
continue;
|
|
@@ -343,7 +211,7 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
343
211
|
documents: [...documents.values()],
|
|
344
212
|
outside: parse === undefined
|
|
345
213
|
? []
|
|
346
|
-
: outsideModules(host, root, [...dirs], files,
|
|
214
|
+
: outsideModules(host, root, [...dirs], files, manifestHeld, parse, named),
|
|
347
215
|
};
|
|
348
216
|
}
|
|
349
217
|
/** The literal module specifier one `require(...)` or `import(...)` call names, if any. */
|
|
@@ -429,23 +297,13 @@ export function resolveRelativeSpecifier(host, dir, specifier) {
|
|
|
429
297
|
* does not hold, and the project files below `root` they import by a relative specifier,
|
|
430
298
|
* until no further file joins. A file that cannot be read is skipped.
|
|
431
299
|
*/
|
|
432
|
-
function outsideModules(host, root, dirs, files,
|
|
300
|
+
function outsideModules(host, root, dirs, files, manifestHeld, parse, named) {
|
|
433
301
|
const pending = [...named];
|
|
434
|
-
for (const dir of [...dirs].sort()) {
|
|
435
|
-
if (!
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
let names;
|
|
439
|
-
try {
|
|
440
|
-
names = host
|
|
441
|
-
.readDirectory(dir)
|
|
442
|
-
.filter((entry) => !entry.directory && configurationModuleForm(entry.name) !== undefined)
|
|
443
|
-
.map((entry) => entry.name)
|
|
444
|
-
.sort();
|
|
445
|
-
}
|
|
446
|
-
catch {
|
|
302
|
+
for (const dir of [...dirs].sort(bytewise)) {
|
|
303
|
+
if (!manifestHeld(dir)) {
|
|
447
304
|
continue;
|
|
448
305
|
}
|
|
306
|
+
const names = filesIn(host, dir, (name) => configurationModuleForm(name) !== undefined);
|
|
449
307
|
pending.push(...names.map((name) => ({ path: joinPath(dir, name), manifestDir: dir })));
|
|
450
308
|
}
|
|
451
309
|
const found = new Map();
|
|
@@ -481,18 +339,12 @@ function outsideModules(host, root, dirs, files, holdsManifest, parse, named) {
|
|
|
481
339
|
}
|
|
482
340
|
/** Every dependency the manifest in one directory declares, or none where it holds none to read. */
|
|
483
341
|
export function declaredIn(host, dir) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
value = JSON.parse(host.readFile(joinPath(dir, MANIFEST)));
|
|
487
|
-
}
|
|
488
|
-
catch {
|
|
489
|
-
return [];
|
|
490
|
-
}
|
|
491
|
-
if (!isRecord(value)) {
|
|
342
|
+
const value = manifestAt(host, dir);
|
|
343
|
+
if (value === undefined) {
|
|
492
344
|
return [];
|
|
493
345
|
}
|
|
494
346
|
const names = new Set();
|
|
495
|
-
for (const member of
|
|
347
|
+
for (const member of DEPENDENCY_SECTIONS) {
|
|
496
348
|
const section = value[member];
|
|
497
349
|
if (isRecord(section)) {
|
|
498
350
|
Object.keys(section).forEach((name) => names.add(name));
|
|
@@ -179,19 +179,19 @@ export function resolveEntries(project, held, entries) {
|
|
|
179
179
|
for (const symbol of held.symbols) {
|
|
180
180
|
byRef.set(symbol.ref, [...(byRef.get(symbol.ref) ?? []), symbol.id]);
|
|
181
181
|
}
|
|
182
|
-
const modules = modulesNamed(project, new Set(entries.flatMap((entry) => (
|
|
182
|
+
const modules = modulesNamed(project, new Set(entries.flatMap((entry) => ("module" in entry ? [entry.module] : []))));
|
|
183
183
|
return entries.map((entry) => {
|
|
184
|
-
if (
|
|
184
|
+
if ("symbol" in entry) {
|
|
185
185
|
return { entry, handles: new Set(), ids: new Set(byRef.get(entry.symbol) ?? []) };
|
|
186
186
|
}
|
|
187
|
-
const { components, static: isStatic } = componentsOf(
|
|
187
|
+
const { components, static: isStatic } = componentsOf("module" in entry ? entry.name : entry.global);
|
|
188
188
|
const [first = "", ...rest] = components;
|
|
189
189
|
let start;
|
|
190
190
|
if (isStatic && rest.length === 0) {
|
|
191
191
|
// Only a member is static, and a path of one component names no member.
|
|
192
192
|
start = undefined;
|
|
193
193
|
}
|
|
194
|
-
else if (
|
|
194
|
+
else if ("module" in entry) {
|
|
195
195
|
const module = modules.get(entry.module);
|
|
196
196
|
start = module === undefined ? undefined : memberOf(project, module, first, false);
|
|
197
197
|
}
|
|
@@ -246,14 +246,10 @@ export function entriesAt(project, chains, nodes, resolved) {
|
|
|
246
246
|
* program the display name the inventory gives it.
|
|
247
247
|
*/
|
|
248
248
|
export function spelledEntry(entry, held) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
|
|
252
|
-
case "module":
|
|
253
|
-
return entry.name;
|
|
254
|
-
case "global":
|
|
255
|
-
return entry.global;
|
|
249
|
+
if ("symbol" in entry) {
|
|
250
|
+
return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
|
|
256
251
|
}
|
|
252
|
+
return "module" in entry ? entry.name : entry.global;
|
|
257
253
|
}
|
|
258
254
|
/** The key under which a lifecycle contract's declarations are configured. */
|
|
259
255
|
const LIFECYCLE_KEY = "ts.lifecycle_contracts";
|
|
@@ -264,11 +260,14 @@ const LIFECYCLE_KEY = "ts.lifecycle_contracts";
|
|
|
264
260
|
*/
|
|
265
261
|
export function configuredDeclarations(config) {
|
|
266
262
|
return [
|
|
267
|
-
...config.ts.
|
|
263
|
+
...config.ts.injection_registrations.map((entry) => ({
|
|
268
264
|
key: "ts.injection_registrations",
|
|
269
265
|
entry,
|
|
270
266
|
})),
|
|
271
|
-
...config.ts.
|
|
267
|
+
...config.ts.lifecycle_contracts.flatMap((contract) => [...(contract.components ?? []), ...(contract.bases ?? [])].map((entry) => ({
|
|
268
|
+
key: LIFECYCLE_KEY,
|
|
269
|
+
entry,
|
|
270
|
+
}))),
|
|
272
271
|
...config.ts.serializers.map((entry) => ({ key: "ts.serializers", entry })),
|
|
273
272
|
];
|
|
274
273
|
}
|
|
@@ -294,12 +293,8 @@ export function declarationsNamed(project, held, configured) {
|
|
|
294
293
|
* sign and its name, and a global entry's path after a number sign.
|
|
295
294
|
*/
|
|
296
295
|
export function configuredRef(entry) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
return entry.symbol;
|
|
300
|
-
case "module":
|
|
301
|
-
return `${entry.module}#${entry.name}`;
|
|
302
|
-
case "global":
|
|
303
|
-
return `#${entry.global}`;
|
|
296
|
+
if ("symbol" in entry) {
|
|
297
|
+
return entry.symbol;
|
|
304
298
|
}
|
|
299
|
+
return "module" in entry ? `${entry.module}#${entry.name}` : `#${entry.global}`;
|
|
305
300
|
}
|
|
@@ -4,7 +4,7 @@ export const RECORD = {
|
|
|
4
4
|
conformance: {
|
|
5
5
|
corpusVersion: "6.0.0",
|
|
6
6
|
result: "pass",
|
|
7
|
-
digest: "sha256:
|
|
7
|
+
digest: "sha256:0d33564807a356fd3e2760ddaa0645d758bdb9a96ee74a392675e444343bbf20",
|
|
8
8
|
},
|
|
9
9
|
gaps: [],
|
|
10
10
|
};
|
package/dist/src/consumers.js
CHANGED
|
@@ -1,34 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* The consumers a scope declares, loaded beside the target: each a directory on the
|
|
3
|
-
* local filesystem whose compiler configurations open as projects of the run's one
|
|
4
|
-
* snapshot, and whose references to the target's declarations count against them.
|
|
5
|
-
*/
|
|
6
|
-
import { consumerProjects } from "./discover.js";
|
|
7
|
-
import { inventory, packageScope } from "./inventory.js";
|
|
1
|
+
import { inventory } from "./inventory.js";
|
|
8
2
|
import { relativePath } from "./paths.js";
|
|
9
3
|
import { references } from "./references.js";
|
|
10
4
|
import { narrowedTo } from "./session.js";
|
|
11
|
-
/** The name one module publishes itself under: the scope's, else its manifest's. */
|
|
12
|
-
export function moduleIdentity(host, module) {
|
|
13
|
-
return module.id === "" ? packageScope(host, module.path)("index.ts").package : module.id;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Every consumer the scope declares, in its order, with the projects it opens as. A
|
|
17
|
-
* configuration that is also one of the target's projects, or an earlier consumer's, is
|
|
18
|
-
* that project's alone. A consumer whose directory is absent, is no directory or holds
|
|
19
|
-
* no configuration ends the run naming it, before any project opens.
|
|
20
|
-
*/
|
|
21
|
-
export function consumerLoads(engine, host, scope, targetConfigFiles) {
|
|
22
|
-
const opened = new Set(targetConfigFiles);
|
|
23
|
-
return scope.consumers.map((module) => {
|
|
24
|
-
const configFiles = consumerProjects(engine, host, module.path).filter((configFile) => {
|
|
25
|
-
const fresh = !opened.has(configFile);
|
|
26
|
-
opened.add(configFile);
|
|
27
|
-
return fresh;
|
|
28
|
-
});
|
|
29
|
-
return { consumer: { id: moduleIdentity(host, module), path: module.path }, configFiles };
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
5
|
/**
|
|
33
6
|
* Every reference one consumer project's own files make to a declaration of the target.
|
|
34
7
|
* The target's declarations are enumerated over the target's files the consumer's
|
|
@@ -39,10 +12,10 @@ export function consumerLoads(engine, host, scope, targetConfigFiles) {
|
|
|
39
12
|
export function consumerReferences(project, host, targetRoot, consumer, config) {
|
|
40
13
|
const held = inventory(narrowedTo(project, (file) => relativePath(targetRoot, file.fileName) !== undefined), host, targetRoot);
|
|
41
14
|
const found = references(project, held, targetRoot, {
|
|
42
|
-
testFiles: config.ts.
|
|
15
|
+
testFiles: config.ts.test_files,
|
|
43
16
|
consumer,
|
|
44
17
|
}).references;
|
|
45
|
-
if (config.analysis.
|
|
18
|
+
if (config.analysis.consumer_tests !== "production") {
|
|
46
19
|
return found;
|
|
47
20
|
}
|
|
48
21
|
return found.map((reference) => (reference.test ? { ...reference, test: false } : reference));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The uses contextual types make of the target's members, which no name node resolves to.
|
|
3
3
|
* An object-literal property writes the member of its name on each constituent of the
|
|
4
|
-
* literal's contextual type that declares one
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
4
|
+
* literal's contextual type that declares one, and so does a property of a literal whose
|
|
5
|
+
* type is inferred, where a value of that type reaches such a position. A value reaching a
|
|
6
|
+
* position typed by another object type reads each of its members that type declares,
|
|
7
|
+
* through array elements and the parameters and results of functions and constructors
|
|
8
|
+
* too; a class instance there is the interface-satisfaction class's evidence instead.
|
|
8
9
|
*/
|
|
9
10
|
import { isClassDeclaration, isClassExpression, isComputedPropertyName, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isMethodSignatureDeclaration, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isPropertyAssignment, isSetAccessorDeclaration, isShorthandPropertyAssignment, isSpreadAssignment, isStringLiteral, } from "@typescript/native/ast";
|
|
10
11
|
import { SignatureKind } from "@typescript/native/sync";
|
|
@@ -164,6 +165,33 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
164
165
|
const members = [...membersOf(part).values()];
|
|
165
166
|
return members.length > 0 && members.every((member) => !member.own);
|
|
166
167
|
};
|
|
168
|
+
const literalTypes = new Map();
|
|
169
|
+
/**
|
|
170
|
+
* Whether an object type is an object literal's own, inferred where no contextual type
|
|
171
|
+
* names it: every member a property an object literal of the target writes.
|
|
172
|
+
*/
|
|
173
|
+
const literalTyped = (part) => {
|
|
174
|
+
let known = literalTypes.get(part.id);
|
|
175
|
+
if (known === undefined) {
|
|
176
|
+
const members = [...membersOf(part).values()];
|
|
177
|
+
known =
|
|
178
|
+
members.length > 0 &&
|
|
179
|
+
members.every((member) => member.ids.length === 0 &&
|
|
180
|
+
member.declarations.length > 0 &&
|
|
181
|
+
member.declarations.every((handle) => literalProperty(handle) !== undefined));
|
|
182
|
+
literalTypes.set(part.id, known);
|
|
183
|
+
}
|
|
184
|
+
return known;
|
|
185
|
+
};
|
|
186
|
+
/** The name node of the object literal property one declaration handle names, if it is one. */
|
|
187
|
+
const literalProperty = (handle) => {
|
|
188
|
+
const node = project.declarationAt(handle)?.node;
|
|
189
|
+
return node !== undefined &&
|
|
190
|
+
(isPropertyAssignment(node) || isShorthandPropertyAssignment(node)) &&
|
|
191
|
+
isObjectLiteralExpression(node.parent)
|
|
192
|
+
? node.name
|
|
193
|
+
: undefined;
|
|
194
|
+
};
|
|
167
195
|
const elements = new Map();
|
|
168
196
|
/** The element type of an array type, read once per type. */
|
|
169
197
|
const elementOf = (type) => {
|
|
@@ -246,10 +274,13 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
246
274
|
const sources = partsOf(source).filter((one) => !targets.some((part) => part.id === one.id));
|
|
247
275
|
for (const one of sources) {
|
|
248
276
|
if (carries(one)) {
|
|
249
|
-
into.push({ source: one, targets,
|
|
277
|
+
into.push({ source: one, targets, sourceKind: "members" });
|
|
250
278
|
}
|
|
251
279
|
else if (outside(one)) {
|
|
252
|
-
into.push({ source: one, targets,
|
|
280
|
+
into.push({ source: one, targets, sourceKind: "outside" });
|
|
281
|
+
}
|
|
282
|
+
else if (literalTyped(one)) {
|
|
283
|
+
into.push({ source: one, targets, sourceKind: "literal" });
|
|
253
284
|
}
|
|
254
285
|
const element = elementOf(one);
|
|
255
286
|
const called = element === undefined ? callsOf(one) : [];
|
|
@@ -290,7 +321,7 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
290
321
|
}
|
|
291
322
|
seen.add(type.id);
|
|
292
323
|
return partsOf(type).some((part) => {
|
|
293
|
-
if (carries(part) || outside(part)) {
|
|
324
|
+
if (carries(part) || outside(part) || literalTyped(part)) {
|
|
294
325
|
return true;
|
|
295
326
|
}
|
|
296
327
|
const element = elementOf(part);
|
|
@@ -307,6 +338,8 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
307
338
|
batches += batchesOf(sourced.length);
|
|
308
339
|
const positions = queries.contextualTypes(sourced.map((one) => one.flow.value), cap);
|
|
309
340
|
const reads = [];
|
|
341
|
+
// A literal property writes a position's members once, however many values carry it there.
|
|
342
|
+
const carriedWrites = new Set();
|
|
310
343
|
sourced.forEach(({ flow, type }, index) => {
|
|
311
344
|
const position = positions[index];
|
|
312
345
|
if (position === undefined || !isAnswered(position)) {
|
|
@@ -315,10 +348,24 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
315
348
|
const checked = [];
|
|
316
349
|
compared(type, position, checked, new Set());
|
|
317
350
|
const ids = new Set();
|
|
318
|
-
for (const { source, targets,
|
|
351
|
+
for (const { source, targets, sourceKind } of checked) {
|
|
319
352
|
const tables = targets.map(membersOf);
|
|
320
353
|
const sourceMembers = membersOf(source);
|
|
321
|
-
if (
|
|
354
|
+
if (sourceKind === "literal") {
|
|
355
|
+
for (const [name, member] of sourceMembers) {
|
|
356
|
+
const written = [...new Set(tables.flatMap((table) => table.get(name)?.ids ?? []))];
|
|
357
|
+
for (const at of written.length === 0 ? [] : member.declarations) {
|
|
358
|
+
const node = literalProperty(at);
|
|
359
|
+
const key = `${at.path}#${String(at.index)}>${written.join(",")}`;
|
|
360
|
+
if (node !== undefined && !carriedWrites.has(key)) {
|
|
361
|
+
carriedWrites.add(key);
|
|
362
|
+
writes.push({ at: node, from: flow.from, ids: written });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (sourceKind === "outside") {
|
|
322
369
|
for (const table of tables) {
|
|
323
370
|
for (const [name, member] of table) {
|
|
324
371
|
if (sourceMembers.has(name)) {
|