@cplieger/deadset-ts 8.0.0-dev.14 → 8.0.0-dev.16
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/dist/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +43 -38
- package/dist/src/commands/read.js +6 -7
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +4 -2
- package/dist/src/contract/config.generated.js +1 -1
- package/dist/src/contract/contract.generated.js +120 -2
- package/dist/src/dependencies.js +81 -49
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +20 -58
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
- package/dist/src/entries/conventions.js +739 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +3 -3
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +2 -2
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +669 -0
- package/dist/src/entries/test-runners.js +684 -0
- package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/file-facts.js +1 -1
- package/dist/src/findings/emitters.js +5 -8
- package/dist/src/findings/self-check.js +36 -20
- package/dist/src/findings-pass.js +5 -6
- package/dist/src/framework-lifecycle.js +1 -1
- package/dist/src/fs/manifest.js +15 -20
- package/dist/src/generated-file.js +172 -29
- package/dist/src/graph.js +2 -5
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/inventory/commonjs.js +507 -0
- package/dist/src/inventory/enumerate.js +167 -0
- package/dist/src/inventory/syntax.js +236 -0
- package/dist/src/inventory.js +305 -705
- package/dist/src/javascript-file.js +26 -11
- package/dist/src/load/classify.js +28 -9
- package/dist/src/load/diagnostics.js +8 -1
- package/dist/src/load/discover.js +16 -8
- package/dist/src/load/package-scope.js +56 -0
- package/dist/src/load/plan.js +2 -2
- package/dist/src/load/synthesize.js +5 -5
- package/dist/src/load/workspace-manifests.js +1 -1
- package/dist/src/matrix.js +6 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/paths.js +16 -0
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +140 -0
- package/dist/src/position.js +41 -0
- package/dist/src/ref.js +11 -20
- package/dist/src/references.js +281 -50
- package/dist/src/resolve.js +1 -1
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +4 -4
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
- package/dist/src/{manifest.js → roots/manifest.js} +4 -4
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +489 -0
- package/dist/src/roots/unseen-loads.js +208 -0
- package/dist/src/serialization-contract.js +1 -1
- package/dist/src/session.js +3 -0
- package/dist/src/source-files.js +5 -9
- package/dist/src/suppress-inline.js +33 -81
- package/dist/src/suppress.js +30 -32
- package/dist/src/test-support.js +4 -2
- package/dist/src/verbs/analyze.js +13 -1
- package/dist/src/verbs/describe.js +5 -3
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +77 -53
- package/src/automatic-types.ts +1 -1
- package/src/commands/read.ts +6 -8
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +5 -1
- package/src/contract/config.generated.ts +1 -1
- package/src/contract/contract.generated.ts +139 -2
- package/src/dependencies.ts +98 -50
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +23 -75
- package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
- package/src/{conventions.ts → entries/conventions.ts} +347 -522
- package/src/{html-entries.ts → entries/html-entries.ts} +5 -5
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +4 -4
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +857 -0
- package/src/entries/test-runners.ts +868 -0
- package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
- package/src/exempt.ts +3 -2
- package/src/extensions.ts +45 -0
- package/src/facts.ts +44 -0
- package/src/file-facts.ts +2 -2
- package/src/findings/emitters.ts +14 -10
- package/src/findings/self-check.ts +41 -22
- package/src/findings/visibility-narrowing.ts +1 -1
- package/src/findings-pass.ts +5 -12
- package/src/framework-lifecycle.ts +1 -1
- package/src/fs/manifest.ts +25 -21
- package/src/generated-file.ts +240 -36
- package/src/graph.ts +3 -6
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/inventory/commonjs.ts +696 -0
- package/src/inventory/enumerate.ts +203 -0
- package/src/inventory/syntax.ts +311 -0
- package/src/inventory.ts +424 -878
- package/src/javascript-file.ts +29 -12
- package/src/load/classify.ts +49 -13
- package/src/load/diagnostics.ts +11 -1
- package/src/load/discover.ts +28 -12
- package/src/load/package-scope.ts +74 -0
- package/src/load/plan.ts +11 -2
- package/src/load/synthesize.ts +6 -6
- package/src/load/test-globals.ts +1 -1
- package/src/load/workspace-manifests.ts +1 -1
- package/src/load/workspace-run.ts +1 -1
- package/src/matrix.ts +7 -14
- package/src/outside-tests.ts +1 -1
- package/src/paths.ts +18 -0
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +205 -0
- package/src/position.ts +42 -0
- package/src/ref.ts +11 -20
- package/src/references.ts +383 -73
- package/src/report.ts +1 -1
- package/src/resolve.ts +1 -1
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +7 -7
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
- package/src/{manifest.ts → roots/manifest.ts} +5 -5
- package/src/roots/published-reach.ts +481 -0
- package/src/{roots.ts → roots/root-set.ts} +203 -630
- package/src/roots/unseen-loads.ts +307 -0
- package/src/serialization-contract.ts +1 -1
- package/src/session.ts +17 -1
- package/src/source-files.ts +5 -10
- package/src/suppress-inline.ts +53 -89
- package/src/suppress.ts +48 -33
- package/src/test-support.ts +8 -3
- package/src/verbs/analyze.ts +17 -4
- package/src/verbs/describe.ts +5 -2
- package/src/verbs/explain.ts +1 -1
- package/dist/src/build-scripts.js +0 -418
- package/dist/src/conventions.js +0 -907
- package/dist/src/entry-points.js +0 -513
- package/dist/src/roots.js +0 -865
- package/dist/src/unseen-loads.js +0 -100
- package/src/build-scripts.ts +0 -536
- package/src/entry-points.ts +0 -661
- package/src/unseen-loads.ts +0 -134
package/src/javascript-file.ts
CHANGED
|
@@ -1,29 +1,46 @@
|
|
|
1
|
+
import { LANGUAGE_ROWS } from "./contract/contract.generated.ts";
|
|
2
|
+
import { globExpression } from "./text/glob.ts";
|
|
3
|
+
|
|
1
4
|
export const JAVASCRIPT_FILE = /\.(?:js|jsx|mjs|cjs)$/u;
|
|
2
5
|
|
|
3
|
-
/**
|
|
4
|
-
const
|
|
6
|
+
/** The patterns the TypeScript row of `languages.json` skips files by, each matched against a name alone. */
|
|
7
|
+
const MINIFIED_NAMES = LANGUAGE_ROWS.ts.skippedFiles.map((pattern) => ({
|
|
8
|
+
pattern,
|
|
9
|
+
expression: globExpression(pattern),
|
|
10
|
+
}));
|
|
5
11
|
const MINIFIED_BYTES = 10_240;
|
|
6
12
|
const MINIFIED_LINE = 500;
|
|
7
13
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
/** What a minified file is decided by where its text is not read. */
|
|
15
|
+
const LONG_LINES = "long lines";
|
|
16
|
+
|
|
17
|
+
/** The skipped-file pattern one file's name matches, which decides it minified without its text. */
|
|
18
|
+
export function minifiedByName(fileName: string): string | undefined {
|
|
19
|
+
const name = fileName.slice(fileName.lastIndexOf("/") + 1);
|
|
20
|
+
return MINIFIED_NAMES.find((one) => one.expression.test(name))?.pattern;
|
|
11
21
|
}
|
|
12
22
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
23
|
+
/**
|
|
24
|
+
* What decides one JavaScript file minified: the skipped-file pattern its name matches, or
|
|
25
|
+
* {@link LONG_LINES} for a text of the size whose mean line is longer than the limit;
|
|
26
|
+
* undefined for a file that is not minified.
|
|
27
|
+
*/
|
|
28
|
+
export function minifiedBy(fileName: string, text: string): string | undefined {
|
|
15
29
|
if (!JAVASCRIPT_FILE.test(fileName)) {
|
|
16
|
-
return
|
|
30
|
+
return undefined;
|
|
17
31
|
}
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
const named = minifiedByName(fileName);
|
|
33
|
+
if (named !== undefined) {
|
|
34
|
+
return named;
|
|
20
35
|
}
|
|
21
36
|
// A UTF-16 unit encodes to at most three UTF-8 bytes, so a shorter text is below the size.
|
|
22
37
|
if (text.length * 3 < MINIFIED_BYTES) {
|
|
23
|
-
return
|
|
38
|
+
return undefined;
|
|
24
39
|
}
|
|
25
40
|
const lines = text.split(/\r?\n/u).length;
|
|
26
|
-
return utf8Bytes(text) >= MINIFIED_BYTES && text.length / lines > MINIFIED_LINE
|
|
41
|
+
return utf8Bytes(text) >= MINIFIED_BYTES && text.length / lines > MINIFIED_LINE
|
|
42
|
+
? LONG_LINES
|
|
43
|
+
: undefined;
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
/** The length of a text in UTF-8, a lone surrogate counting as the three bytes of U+FFFD. */
|
package/src/load/classify.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SourceFile } from "@typescript/native/ast";
|
|
2
|
-
import { conventionFailuresFor } from "../conventions.ts";
|
|
2
|
+
import { conventionFailuresFor } from "../entries/conventions.ts";
|
|
3
3
|
import type { Host } from "../host.ts";
|
|
4
4
|
import { relativePath, resolvePath } from "../paths.ts";
|
|
5
5
|
import { diagnosticsOf, ownedAs, type Engine, type ProjectView } from "../session.ts";
|
|
@@ -11,7 +11,13 @@ import {
|
|
|
11
11
|
renderDiagnostic,
|
|
12
12
|
type LocatedDiagnostic,
|
|
13
13
|
} from "./diagnostics.ts";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
targetRelative,
|
|
16
|
+
unreadableDetail,
|
|
17
|
+
type LoadedConsumer,
|
|
18
|
+
type NotBuilt,
|
|
19
|
+
type Origin,
|
|
20
|
+
} from "./discover.ts";
|
|
15
21
|
import { missingModule, pathsOf, type ModuleContext } from "./expected-module.ts";
|
|
16
22
|
import { FailureExit, failureLine, type Failure } from "../failure.ts";
|
|
17
23
|
import type { ProgramPlan, RunPlan } from "./plan.ts";
|
|
@@ -21,8 +27,12 @@ import { ownErrors } from "./workspace-run.ts";
|
|
|
21
27
|
|
|
22
28
|
/** What keeps one configuration from being analyzed. */
|
|
23
29
|
type Failed =
|
|
24
|
-
/** Its project carries an error that leaves it unloaded. */
|
|
25
|
-
| {
|
|
30
|
+
/** Its project carries an error that leaves it unloaded, its configuration unreadable or not. */
|
|
31
|
+
| {
|
|
32
|
+
readonly kind: "load";
|
|
33
|
+
readonly errors: readonly [LocatedDiagnostic, ...LocatedDiagnostic[]];
|
|
34
|
+
readonly unreadable: boolean;
|
|
35
|
+
}
|
|
26
36
|
| { readonly kind: "setup"; readonly failures: readonly [Failure, ...Failure[]] }
|
|
27
37
|
/** The snapshot opened no project for it. */
|
|
28
38
|
| { readonly kind: "unopened" };
|
|
@@ -162,7 +172,7 @@ function classifyTarget<Brand>(
|
|
|
162
172
|
const raw = projectErrors(diagnosticsOf(project));
|
|
163
173
|
const load = nonEmpty(raw.load);
|
|
164
174
|
if (load !== undefined) {
|
|
165
|
-
return { kind: "load", errors: load };
|
|
175
|
+
return { kind: "load", errors: load, unreadable: raw.unreadable };
|
|
166
176
|
}
|
|
167
177
|
const reading = readErrors(project, plan, ownErrors(raw.check, referenceOnly), {
|
|
168
178
|
host,
|
|
@@ -236,7 +246,7 @@ function classifyConsumer<Brand>(
|
|
|
236
246
|
const raw = projectErrors(diagnosticsOf(project));
|
|
237
247
|
const load = nonEmpty(raw.load);
|
|
238
248
|
if (load !== undefined) {
|
|
239
|
-
return { kind: "load", errors: load };
|
|
249
|
+
return { kind: "load", errors: load, unreadable: raw.unreadable };
|
|
240
250
|
}
|
|
241
251
|
let faulted: string | undefined;
|
|
242
252
|
const reading = readErrors(project, plan, raw.check, {
|
|
@@ -283,7 +293,8 @@ function ending(plan: ProgramPlan, failed: Failed, root: string): readonly Failu
|
|
|
283
293
|
return plan.consumer === undefined
|
|
284
294
|
? [
|
|
285
295
|
{
|
|
286
|
-
failureClass:
|
|
296
|
+
failureClass:
|
|
297
|
+
failed.kind === "load" && failed.unreadable ? "unreadable-configuration" : "no-program",
|
|
287
298
|
detail: `${what}: correct the configuration, or name the configurations to analyze`,
|
|
288
299
|
},
|
|
289
300
|
]
|
|
@@ -295,6 +306,30 @@ function ending(plan: ProgramPlan, failed: Failed, root: string): readonly Failu
|
|
|
295
306
|
];
|
|
296
307
|
}
|
|
297
308
|
|
|
309
|
+
/** The setup failures a dropped derived configuration counts as, where it counts as any. */
|
|
310
|
+
function droppedFailures(
|
|
311
|
+
failed: Failed,
|
|
312
|
+
plan: ProgramPlan,
|
|
313
|
+
root: string,
|
|
314
|
+
): { readonly failures?: readonly [Failure, ...Failure[]] } {
|
|
315
|
+
if (failed.kind === "setup") {
|
|
316
|
+
return { failures: failed.failures };
|
|
317
|
+
}
|
|
318
|
+
return failed.kind === "load" && failed.unreadable
|
|
319
|
+
? {
|
|
320
|
+
failures: [
|
|
321
|
+
{
|
|
322
|
+
failureClass: "unreadable-configuration",
|
|
323
|
+
detail: unreadableDetail(
|
|
324
|
+
plan.id,
|
|
325
|
+
`cannot be read as a compiler configuration: ${droppedBy(failed, root)}`,
|
|
326
|
+
),
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
}
|
|
330
|
+
: {};
|
|
331
|
+
}
|
|
332
|
+
|
|
298
333
|
/** The first line of the error that drops one derived configuration. */
|
|
299
334
|
function droppedBy(failed: Failed, root: string): string {
|
|
300
335
|
return failed.kind === "load"
|
|
@@ -326,7 +361,6 @@ export function classify(
|
|
|
326
361
|
const analyzed: Analyzed<unknown>[] = [];
|
|
327
362
|
const consumed: Consumed<unknown>[] = [];
|
|
328
363
|
const ended = new Map<Failed["kind"], { plan: ProgramPlan; failed: Failed }[]>();
|
|
329
|
-
const dropped: Failed[] = [];
|
|
330
364
|
const unopened = run.plans.filter((plan) => !views.has(plan.configFile));
|
|
331
365
|
const outcomes = [
|
|
332
366
|
...run.plans.flatMap((plan) => {
|
|
@@ -355,8 +389,12 @@ export function classify(
|
|
|
355
389
|
} else if (POLICY[plan.origin][outcome.kind] === "end") {
|
|
356
390
|
ended.set(outcome.kind, [...(ended.get(outcome.kind) ?? []), { plan, failed: outcome }]);
|
|
357
391
|
} else {
|
|
358
|
-
|
|
359
|
-
|
|
392
|
+
notBuilt.push({
|
|
393
|
+
id: plan.id,
|
|
394
|
+
configFile: plan.configFile,
|
|
395
|
+
error: droppedBy(outcome, root),
|
|
396
|
+
...droppedFailures(outcome, plan, root),
|
|
397
|
+
});
|
|
360
398
|
}
|
|
361
399
|
}
|
|
362
400
|
for (const kind of ["unopened", "setup", "load"] as const) {
|
|
@@ -369,9 +407,7 @@ export function classify(
|
|
|
369
407
|
);
|
|
370
408
|
}
|
|
371
409
|
}
|
|
372
|
-
const setupDropped = nonEmpty(
|
|
373
|
-
dropped.flatMap((one) => (one.kind === "setup" ? one.failures : [])),
|
|
374
|
-
);
|
|
410
|
+
const setupDropped = nonEmpty(notBuilt.flatMap((one) => one.failures ?? []));
|
|
375
411
|
const [first] = notBuilt;
|
|
376
412
|
if (analyzed.length === 0 && first !== undefined) {
|
|
377
413
|
throw new FailureExit(
|
package/src/load/diagnostics.ts
CHANGED
|
@@ -121,10 +121,18 @@ export function diagnosticErrors(sets: {
|
|
|
121
121
|
return [...sets.configParsing, ...sets.syntactic, ...sets.semantic].filter(isError);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* The code of a configuration whose only parse error is that its inputs name no file: it reads
|
|
126
|
+
* as written and holds no program.
|
|
127
|
+
*/
|
|
128
|
+
const NO_INPUTS = 18003;
|
|
129
|
+
|
|
124
130
|
/** Why one open project cannot be analyzed as it stands, split by whether it loaded at all. */
|
|
125
131
|
interface ProjectErrors {
|
|
126
132
|
/** The configuration's own errors and the errors in no file the table reads as a load failure. */
|
|
127
133
|
readonly load: readonly LocatedDiagnostic[];
|
|
134
|
+
/** Whether the compiler cannot read the configuration: a parse error other than {@link NO_INPUTS}. */
|
|
135
|
+
readonly unreadable: boolean;
|
|
128
136
|
/** The errors in the files of a project that loaded. */
|
|
129
137
|
readonly check: readonly LocatedDiagnostic[];
|
|
130
138
|
/** The libraries the configuration names that the compiler cannot find, of a project that loaded. */
|
|
@@ -135,9 +143,11 @@ interface ProjectErrors {
|
|
|
135
143
|
export function projectErrors(sets: ProjectDiagnostics): ProjectErrors {
|
|
136
144
|
const unplaced = (kind: DiagnosticClass): readonly Diagnostic[] =>
|
|
137
145
|
sets.unplaced.filter((diagnostic) => classOf(diagnostic, false) === kind);
|
|
138
|
-
const
|
|
146
|
+
const parsing = sets.configParsing.filter(isError);
|
|
147
|
+
const load = [...parsing, ...unplaced("load")];
|
|
139
148
|
return {
|
|
140
149
|
load: locatedIn(load, sets.locate),
|
|
150
|
+
unreadable: parsing.some(({ code }) => code !== NO_INPUTS),
|
|
141
151
|
check: load.length > 0 ? [] : locatedIn(diagnosticErrors(sets), sets.locate),
|
|
142
152
|
unfound: load.length > 0 ? [] : locatedIn(unplaced("unfound-library"), sets.locate),
|
|
143
153
|
};
|
package/src/load/discover.ts
CHANGED
|
@@ -2,13 +2,13 @@ import type { BuildConfiguration, ProjectConfiguration } from "../config.ts";
|
|
|
2
2
|
import { LANGUAGE_ROWS } from "../contract/contract.generated.ts";
|
|
3
3
|
import { filesBelow, filesIn } from "../fs/tree.ts";
|
|
4
4
|
import type { Host } from "../host.ts";
|
|
5
|
-
import { packageScope } from "
|
|
5
|
+
import { packageScope } from "./package-scope.ts";
|
|
6
6
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "../paths.ts";
|
|
7
7
|
import { LANGUAGE, message } from "../product.ts";
|
|
8
8
|
import type { Module, Scope } from "../scope.ts";
|
|
9
9
|
import type { Engine } from "../session.ts";
|
|
10
10
|
import { globExpression } from "../text/glob.ts";
|
|
11
|
-
import { failureExit, type FailureExit } from "../failure.ts";
|
|
11
|
+
import { failureExit, type Failure, type FailureExit } from "../failure.ts";
|
|
12
12
|
import { MANIFEST } from "../fs/manifest.ts";
|
|
13
13
|
|
|
14
14
|
/** The names of the files a program of the language is read from. */
|
|
@@ -72,6 +72,8 @@ export interface NotBuilt {
|
|
|
72
72
|
readonly configFile: string;
|
|
73
73
|
/** The error's first line, every path below the target root spelled relative to it. */
|
|
74
74
|
readonly error: string;
|
|
75
|
+
/** The setup failures that dropped it, where one did. */
|
|
76
|
+
readonly failures?: readonly [Failure, ...Failure[]];
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
/**
|
|
@@ -165,11 +167,9 @@ function readConfiguration(
|
|
|
165
167
|
try {
|
|
166
168
|
const parsed = engine.parseConfigFile(configFile);
|
|
167
169
|
const circular = parsed.errors.find(({ code }) => code === CIRCULAR_EXTENDS);
|
|
168
|
-
// The compiler leaves the message's `{0}` unfilled:
|
|
169
|
-
// https://github.com/microsoft/TypeScript/pull/64755
|
|
170
170
|
return circular === undefined
|
|
171
171
|
? { parsed }
|
|
172
|
-
: { reason: `TS${String(circular.code)}: ${circular.text
|
|
172
|
+
: { reason: `TS${String(circular.code)}: ${circular.text}` };
|
|
173
173
|
} catch (error: unknown) {
|
|
174
174
|
return { reason: message(error) };
|
|
175
175
|
}
|
|
@@ -274,11 +274,17 @@ export function targetWithoutSources(target: string): FailureExit {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
/** A target configuration a matrix or the invocation names that cannot be built, ending the run. */
|
|
277
|
-
function unbuilt(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
277
|
+
function unbuilt(
|
|
278
|
+
named: string,
|
|
279
|
+
reason: string,
|
|
280
|
+
failureClass: Failure["failureClass"] = "no-program",
|
|
281
|
+
): never {
|
|
282
|
+
throw failureExit(failureClass, unreadableDetail(named, reason));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** What fails and the fix, for a configuration `named` that `reason` keeps from being built. */
|
|
286
|
+
export function unreadableDetail(named: string, reason: string): string {
|
|
287
|
+
return `${named} ${reason}: correct the configuration, or name the configurations to analyze`;
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
/**
|
|
@@ -321,7 +327,11 @@ function declaredProjects(
|
|
|
321
327
|
}
|
|
322
328
|
const read = readConfiguration(engine, configFile);
|
|
323
329
|
if ("reason" in read) {
|
|
324
|
-
unbuilt(
|
|
330
|
+
unbuilt(
|
|
331
|
+
named,
|
|
332
|
+
`cannot be read as a compiler configuration: ${read.reason}`,
|
|
333
|
+
"unreadable-configuration",
|
|
334
|
+
);
|
|
325
335
|
}
|
|
326
336
|
references.set(configFile, referencesOf(read.parsed));
|
|
327
337
|
const configuration: Configuration = {
|
|
@@ -377,13 +387,19 @@ function derivedProjects(
|
|
|
377
387
|
[...fromScope, ...configFilesUnder(host, targetRoot)],
|
|
378
388
|
(reference) => relativePath(rootDir, reference) !== undefined,
|
|
379
389
|
(configFile, reason) => {
|
|
390
|
+
const why = `cannot be read as a compiler configuration: ${reason}`;
|
|
380
391
|
if (fromScope.includes(configFile)) {
|
|
381
|
-
unbuilt(configFile,
|
|
392
|
+
unbuilt(configFile, why, "unreadable-configuration");
|
|
382
393
|
}
|
|
394
|
+
const failure: Failure = {
|
|
395
|
+
failureClass: "unreadable-configuration",
|
|
396
|
+
detail: targetRelative(unreadableDetail(idOf(configFile), why), absoluteRoot),
|
|
397
|
+
};
|
|
383
398
|
notBuilt.push({
|
|
384
399
|
id: idOf(configFile),
|
|
385
400
|
configFile,
|
|
386
401
|
error: targetRelative(reason, absoluteRoot),
|
|
402
|
+
failures: [failure],
|
|
387
403
|
});
|
|
388
404
|
},
|
|
389
405
|
);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { manifestAt } from "../fs/manifest.ts";
|
|
2
|
+
import type { Host } from "../host.ts";
|
|
3
|
+
import { dirnamePath, joinPath } from "../paths.ts";
|
|
4
|
+
import type { Module } from "../ref.ts";
|
|
5
|
+
|
|
6
|
+
/** One manifest that names a package, and the directory below the root that holds it. */
|
|
7
|
+
interface Named {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly at: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolves each file's package scope: the name of the nearest manifest at or above its
|
|
14
|
+
* directory up to the target root, and the file's path below that manifest. Where no
|
|
15
|
+
* manifest up to the root carries a name, the package is a single dot and the path is read
|
|
16
|
+
* from the root, which cannot collide, because a package name never starts with a dot. One
|
|
17
|
+
* directory is read at most once.
|
|
18
|
+
*/
|
|
19
|
+
export function packageScope(host: Host, root: string): (path: string) => Module {
|
|
20
|
+
const named = new Map<string, Named | undefined>();
|
|
21
|
+
|
|
22
|
+
const manifestName = (dir: string): string | undefined => {
|
|
23
|
+
const name = manifestAt(host, joinPath(root, dir))?.["name"];
|
|
24
|
+
return typeof name === "string" && name !== "" ? name : undefined;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const nearest = (dir: string): Named | undefined => {
|
|
28
|
+
if (named.has(dir)) {
|
|
29
|
+
return named.get(dir);
|
|
30
|
+
}
|
|
31
|
+
const name = manifestName(dir);
|
|
32
|
+
const found =
|
|
33
|
+
name !== undefined ? { name, at: dir } : dir === "" ? undefined : nearest(parentOf(dir));
|
|
34
|
+
named.set(dir, found);
|
|
35
|
+
return found;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (path) => {
|
|
39
|
+
const found = nearest(parentOf(path));
|
|
40
|
+
if (found === undefined) {
|
|
41
|
+
return { package: ".", path };
|
|
42
|
+
}
|
|
43
|
+
return { package: found.name, path: found.at === "" ? path : path.slice(found.at.length + 1) };
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether Node reads each file's `.js` siblings as ECMAScript modules: the `type` of the
|
|
49
|
+
* nearest manifest at or above its directory up to the target root is `module`
|
|
50
|
+
* (https://nodejs.org/api/packages.html#type). One directory is read at most once.
|
|
51
|
+
*/
|
|
52
|
+
export function packageTypeIsModule(host: Host, root: string): (path: string) => boolean {
|
|
53
|
+
const typed = new Map<string, boolean>();
|
|
54
|
+
|
|
55
|
+
const nearest = (dir: string): boolean => {
|
|
56
|
+
const known = typed.get(dir);
|
|
57
|
+
if (known !== undefined) {
|
|
58
|
+
return known;
|
|
59
|
+
}
|
|
60
|
+
const manifest = manifestAt(host, joinPath(root, dir));
|
|
61
|
+
const found =
|
|
62
|
+
manifest !== undefined ? manifest["type"] === "module" : dir !== "" && nearest(parentOf(dir));
|
|
63
|
+
typed.set(dir, found);
|
|
64
|
+
return found;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (path) => nearest(parentOf(path));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The directory one path below the root sits in, and the root itself as the empty path. */
|
|
71
|
+
function parentOf(path: string): string {
|
|
72
|
+
const dir = dirnamePath(path);
|
|
73
|
+
return dir === "." ? "" : dir;
|
|
74
|
+
}
|
package/src/load/plan.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { CreateSnapshotProgramParams } from "@typescript/native/sync";
|
|
2
2
|
import type { PartialConfig } from "../config.ts";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
readConventions,
|
|
5
|
+
type ConventionManifest,
|
|
6
|
+
type Conventions,
|
|
7
|
+
} from "../entries/conventions.ts";
|
|
4
8
|
import { holdsManifest, inInstalledPackages, MANIFEST, readManifestAt } from "../fs/manifest.ts";
|
|
5
9
|
import { gitIgnored } from "../git-ignore.ts";
|
|
6
10
|
import type { Host } from "../host.ts";
|
|
7
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
packageEntries,
|
|
13
|
+
readManifest,
|
|
14
|
+
type Manifest,
|
|
15
|
+
type ManifestEntry,
|
|
16
|
+
} from "../roots/manifest.ts";
|
|
8
17
|
import {
|
|
9
18
|
ancestors,
|
|
10
19
|
dirnamePath,
|
package/src/load/synthesize.ts
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { readCommands } from "../commands/read.ts";
|
|
9
|
-
import { CONVENTION_ROWS } from "../convention-rows.ts";
|
|
10
|
-
import type { Conventions } from "../conventions.ts";
|
|
9
|
+
import { CONVENTION_ROWS } from "../entries/convention-rows.ts";
|
|
10
|
+
import type { Conventions } from "../entries/conventions.ts";
|
|
11
11
|
import { holdsManifest, MANIFEST, readManifestAt, type ManifestRead } from "../fs/manifest.ts";
|
|
12
12
|
import { filesBelow } from "../fs/tree.ts";
|
|
13
13
|
import type { Host } from "../host.ts";
|
|
14
|
-
import { JAVASCRIPT_FILE,
|
|
14
|
+
import { JAVASCRIPT_FILE, minifiedBy, minifiedByName } from "../javascript-file.ts";
|
|
15
15
|
import { isRecord } from "../json-document.ts";
|
|
16
|
-
import { manifestCommands } from "../manifest.ts";
|
|
16
|
+
import { manifestCommands } from "../roots/manifest.ts";
|
|
17
17
|
import { ancestors, dirnamePath, joinPath, relativePath } from "../paths.ts";
|
|
18
18
|
import type { Engine } from "../session.ts";
|
|
19
19
|
import { bytewise } from "../text/compare.ts";
|
|
@@ -50,11 +50,11 @@ interface CandidateContext {
|
|
|
50
50
|
|
|
51
51
|
/** Whether one file is minified build output; one that cannot be read is left to the compiler. */
|
|
52
52
|
function minifiedFile(host: Host, file: string): boolean {
|
|
53
|
-
if (minifiedByName(file)) {
|
|
53
|
+
if (minifiedByName(file) !== undefined) {
|
|
54
54
|
return true;
|
|
55
55
|
}
|
|
56
56
|
try {
|
|
57
|
-
return
|
|
57
|
+
return minifiedBy(file, host.readFile(file)) !== undefined;
|
|
58
58
|
} catch {
|
|
59
59
|
return false;
|
|
60
60
|
}
|
package/src/load/test-globals.ts
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
type Node,
|
|
24
24
|
type SourceFile,
|
|
25
25
|
} from "@typescript/native/ast";
|
|
26
|
-
import type { ParseFile } from "../configuration-files.ts";
|
|
26
|
+
import type { ParseFile } from "../entries/configuration-files.ts";
|
|
27
27
|
import type { Host } from "../host.ts";
|
|
28
28
|
import { isRecord } from "../json-document.ts";
|
|
29
29
|
import { ancestors, dirnamePath, joinPath } from "../paths.ts";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { emittedFrom } from "./emit-map.ts";
|
|
10
10
|
import type { Host } from "../host.ts";
|
|
11
|
-
import { readManifest, type Manifest, type ManifestEntry } from "../manifest.ts";
|
|
11
|
+
import { readManifest, type Manifest, type ManifestEntry } from "../roots/manifest.ts";
|
|
12
12
|
import { joinPath, relativePath } from "../paths.ts";
|
|
13
13
|
import type { WorkspaceMember } from "./workspace.ts";
|
|
14
14
|
import type { WorkspaceResolver } from "./workspace-resolution.ts";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Host } from "../host.ts";
|
|
9
|
-
import type { Manifest, ManifestEntry } from "../manifest.ts";
|
|
9
|
+
import type { Manifest, ManifestEntry } from "../roots/manifest.ts";
|
|
10
10
|
import { relativePath } from "../paths.ts";
|
|
11
11
|
import type { Engine } from "../session.ts";
|
|
12
12
|
import type { LocatedDiagnostic } from "./diagnostics.ts";
|
package/src/matrix.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { graphOf, OUTSIDE, type Graph } from "./graph.ts";
|
|
|
9
9
|
import type { InventorySymbol } from "./inventory.ts";
|
|
10
10
|
import { byPosition } from "./position.ts";
|
|
11
11
|
import type { Reference } from "./references.ts";
|
|
12
|
-
import type { Root } from "./roots.ts";
|
|
12
|
+
import type { Root } from "./roots/root-set.ts";
|
|
13
13
|
import {
|
|
14
14
|
reachedWithTheTests,
|
|
15
15
|
sweep,
|
|
@@ -198,19 +198,12 @@ export function matrixOf(per: readonly Configured[]): Matrix {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* reference under one configuration is a reference, so what one project uses is never
|
|
208
|
-
* reported, and a configuration that does not hold a declaration says nothing about it.
|
|
209
|
-
* The candidate's relation is reference counting where every configuration found it so,
|
|
210
|
-
* and reachability otherwise; its counts are the union's, one per reference.
|
|
211
|
-
*
|
|
212
|
-
* The components are computed once, over the union and the intersected dead set, so one
|
|
213
|
-
* dead component is one entry of the run with one identifier.
|
|
201
|
+
* Which declarations of the run are dead in every configuration that holds them. Each
|
|
202
|
+
* configuration is swept on its own graph, since a file it does not compile references nothing
|
|
203
|
+
* there and a root it does not hold seeds nothing. A declaration is a candidate when every
|
|
204
|
+
* configuration holding it judged it one; its relation is reference counting where every one
|
|
205
|
+
* found it so, else reachability, and its counts are the union's. Components are computed once,
|
|
206
|
+
* over the union and the intersected dead set, so one dead component has one identifier.
|
|
214
207
|
*/
|
|
215
208
|
export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
216
209
|
const per = matrix.graphs.map((graph) => sweep(graph, input));
|
package/src/outside-tests.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
type Node,
|
|
20
20
|
type SourceFile,
|
|
21
21
|
} from "@typescript/native/ast";
|
|
22
|
-
import { RESOLVED_EXTENSIONS, type ParseFile } from "./configuration-files.ts";
|
|
22
|
+
import { RESOLVED_EXTENSIONS, type ParseFile } from "./entries/configuration-files.ts";
|
|
23
23
|
import { emittedFrom } from "./load/emit-map.ts";
|
|
24
24
|
import type { Host } from "./host.ts";
|
|
25
25
|
import type { Inventory } from "./inventory.ts";
|
package/src/paths.ts
CHANGED
|
@@ -97,6 +97,24 @@ export function resolvePath(against: string, path: string): string {
|
|
|
97
97
|
return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* A URL reference's scheme. A reference with none that does not start with a solidus is
|
|
102
|
+
* relative, the bare name included: https://url.spec.whatwg.org/#relative-url-string
|
|
103
|
+
*/
|
|
104
|
+
const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The path a relative URL reference names against `dir`, its query and its fragment
|
|
108
|
+
* removed, which name no part of the file; undefined where the reference is not relative.
|
|
109
|
+
*/
|
|
110
|
+
export function relativeURLPath(dir: string, reference: string): string | undefined {
|
|
111
|
+
if (reference === "" || reference.startsWith("/") || URL_SCHEME.test(reference)) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
const cut = reference.search(/[?#]/u);
|
|
115
|
+
return resolvePath(dir, cut < 0 ? reference : reference.slice(0, cut));
|
|
116
|
+
}
|
|
117
|
+
|
|
100
118
|
/**
|
|
101
119
|
* The relative path before the query of a relative module specifier such as
|
|
102
120
|
* `./worker.ts?worker`, the module a bundler loads; undefined for a specifier with no
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which calls are the platform's `require` and which constructions its `URL` (roots.23,
|
|
3
|
+
* references-ts.1), decided once over the bindings of the names a callee is written with: the
|
|
4
|
+
* checker's in a program, the file's own scopes in a module read outside every program.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
isBindingElement,
|
|
9
|
+
isCallExpression,
|
|
10
|
+
isExportSpecifier,
|
|
11
|
+
isIdentifier,
|
|
12
|
+
isImportSpecifier,
|
|
13
|
+
isPropertyAccessExpression,
|
|
14
|
+
isStringLiteral,
|
|
15
|
+
isVariableDeclaration,
|
|
16
|
+
type Identifier,
|
|
17
|
+
type Node,
|
|
18
|
+
} from "@typescript/native/ast";
|
|
19
|
+
import { parenthesesRemoved, unwrapped } from "./unwrap.ts";
|
|
20
|
+
|
|
21
|
+
/** A name of the platform the analysis reads calls of. */
|
|
22
|
+
type PlatformName = "require" | "createRequire" | "URL";
|
|
23
|
+
|
|
24
|
+
/** What one written name is bound to, as far as its reader can tell. */
|
|
25
|
+
export interface Bound {
|
|
26
|
+
/** Its own name, and each one an import, a re-export or a destructuring carries it by. */
|
|
27
|
+
readonly names: ReadonlySet<string>;
|
|
28
|
+
/** The declarations of the target it resolves to, a variable's initializer read through. */
|
|
29
|
+
readonly own: readonly Node[];
|
|
30
|
+
/** Whether the type checker proves it the target's own, a function or a class it writes with a body. */
|
|
31
|
+
readonly provenOwn: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The binding of each written name, `undefined` where nothing answers which declaration it names. */
|
|
35
|
+
export type BindingOf = (written: Identifier) => Bound | undefined;
|
|
36
|
+
|
|
37
|
+
/** The name one declaration carries a binding by beside its own: the export an import or a re-export names, or the property a destructuring reads. */
|
|
38
|
+
export function carriedNames(node: Node): readonly string[] {
|
|
39
|
+
if (isImportSpecifier(node) || isExportSpecifier(node)) {
|
|
40
|
+
return [(node.propertyName ?? node.name).text];
|
|
41
|
+
}
|
|
42
|
+
const property = isBindingElement(node) ? node.propertyName : undefined;
|
|
43
|
+
return property !== undefined && (isIdentifier(property) || isStringLiteral(property))
|
|
44
|
+
? [property.text]
|
|
45
|
+
: [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A written name's binding as a file states it, with no checker to ask, so nothing is
|
|
50
|
+
* proven the target's own (conventions.3): `declarationOf` names the declaration a name
|
|
51
|
+
* resolves to, and a property is read by how it is written alone.
|
|
52
|
+
*/
|
|
53
|
+
export function lexicalBinding(
|
|
54
|
+
declarationOf: (written: Identifier) => Node | undefined,
|
|
55
|
+
): BindingOf {
|
|
56
|
+
return (written) => {
|
|
57
|
+
const parent = written.parent;
|
|
58
|
+
if (isPropertyAccessExpression(parent) && parent.name === written) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const declaration = declarationOf(written);
|
|
62
|
+
return {
|
|
63
|
+
names: new Set([
|
|
64
|
+
written.text,
|
|
65
|
+
...(declaration === undefined ? [] : carriedNames(declaration)),
|
|
66
|
+
]),
|
|
67
|
+
own: declaration === undefined ? [] : [declaration],
|
|
68
|
+
provenOwn: false,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The name a callee or a constructor is written with, parentheses removed: an identifier or a property name. */
|
|
74
|
+
export function writtenName(expression: Node): Identifier | undefined {
|
|
75
|
+
const at = parenthesesRemoved(expression);
|
|
76
|
+
if (isIdentifier(at)) {
|
|
77
|
+
return at;
|
|
78
|
+
}
|
|
79
|
+
return isPropertyAccessExpression(at) && isIdentifier(at.name) ? at.name : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Whether a call or construction through `callee` is one of the platform's `name`: never
|
|
84
|
+
* where its binding is proven the target's own; otherwise where it is written or carried
|
|
85
|
+
* by that name, or resolves to a variable of the target whose initializer is such a
|
|
86
|
+
* callee or, for `require`, a call of a `createRequire`. A callee nothing binds is read by
|
|
87
|
+
* how it is written.
|
|
88
|
+
*/
|
|
89
|
+
export function callsPlatformBy(
|
|
90
|
+
name: PlatformName,
|
|
91
|
+
callee: Identifier,
|
|
92
|
+
bindingOf: BindingOf,
|
|
93
|
+
seen = new Set<Node>(),
|
|
94
|
+
): boolean {
|
|
95
|
+
const bound = bindingOf(callee);
|
|
96
|
+
if (bound?.provenOwn === true) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (callee.text === name || bound?.names.has(name) === true) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return (bound?.own ?? []).some((node) => {
|
|
103
|
+
if (seen.has(node) || !isVariableDeclaration(node) || node.initializer === undefined) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
seen.add(node);
|
|
107
|
+
const value = unwrapped(node.initializer);
|
|
108
|
+
if (isCallExpression(value)) {
|
|
109
|
+
const called = writtenName(value.expression);
|
|
110
|
+
return (
|
|
111
|
+
name === "require" &&
|
|
112
|
+
called !== undefined &&
|
|
113
|
+
callsPlatformBy("createRequire", called, bindingOf, seen)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const written = writtenName(value);
|
|
117
|
+
return written !== undefined && callsPlatformBy(name, written, bindingOf, seen);
|
|
118
|
+
});
|
|
119
|
+
}
|