@cplieger/deadset-ts 8.0.0-dev.15 → 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/analysis.js +41 -37
- package/dist/src/commands/read.js +6 -7
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +3 -1
- package/dist/src/contract/config.generated.js +1 -1
- package/dist/src/contract/contract.generated.js +119 -2
- package/dist/src/dependencies.js +79 -48
- 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/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 -4
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/inventory/commonjs.js +62 -17
- package/dist/src/inventory/enumerate.js +2 -1
- 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 +15 -7
- 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/references.js +202 -32
- 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/source-files.js +5 -9
- 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 +2 -2
- package/src/analysis.ts +75 -52
- 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 +4 -0
- package/src/contract/config.generated.ts +1 -1
- package/src/contract/contract.generated.ts +138 -2
- package/src/dependencies.ts +96 -49
- 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 +1 -1
- 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 -5
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/inventory/commonjs.ts +102 -17
- package/src/inventory/enumerate.ts +2 -1
- 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 +27 -11
- 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/references.ts +281 -34
- 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/source-files.ts +5 -10
- 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 -444
- package/dist/src/own-body.js +0 -28
- package/dist/src/roots.js +0 -865
- package/dist/src/unseen-loads.js +0 -189
- package/src/build-scripts.ts +0 -536
- package/src/entry-points.ts +0 -584
- package/src/own-body.ts +0 -42
- package/src/unseen-loads.ts +0 -282
package/dist/src/dependencies.js
CHANGED
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { isArrayLiteralExpression, isCallExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNamespaceExport, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isStringLiteral, isNoSubstitutionTemplateLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { automaticTypes } from "./automatic-types.js";
|
|
11
|
-
import {
|
|
11
|
+
import { declaredIn as declaredBeside, dependenciesNamed, moduleStrings, outsideChain, } from "./entries/configuration-files.js";
|
|
12
12
|
import { nodeKey } from "./inventory.js";
|
|
13
13
|
import { packageScope } from "./load/package-scope.js";
|
|
14
14
|
import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
|
|
15
15
|
import { UNANSWERED } from "./query.js";
|
|
16
16
|
import { sourceFilesOf } from "./source-files.js";
|
|
17
|
-
import { manifestCommands, nodemonTexts } from "./manifest.js";
|
|
18
|
-
import {
|
|
17
|
+
import { manifestCommands, nodemonTexts } from "./roots/manifest.js";
|
|
18
|
+
import { writtenName } from "./platform-calls.js";
|
|
19
|
+
import { callsPlatform } from "./platform-symbols.js";
|
|
20
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
19
21
|
import { pathsNamed, preloadedPackages, readCommands, } from "./commands/read.js";
|
|
20
22
|
import { heldNames } from "./text/named-files.js";
|
|
21
23
|
import { bytewise } from "./text/compare.js";
|
|
@@ -202,10 +204,11 @@ function bindingsOf(node) {
|
|
|
202
204
|
* Every package one project needs, and which declaration each use is written in.
|
|
203
205
|
* One local walk and one batched resolution per file: a use of an import binding is
|
|
204
206
|
* an identifier resolving to the binding's own symbol, so a local declaration that
|
|
205
|
-
* shadows the name is not one. A string of a configuration file
|
|
206
|
-
* the manifest beside the file
|
|
207
|
+
* shadows the name is not one. A string of a configuration file, as the entry sources
|
|
208
|
+
* read them in `configuration`, uses each dependency the manifest beside the file
|
|
209
|
+
* declares that it spells.
|
|
207
210
|
*/
|
|
208
|
-
export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
211
|
+
export function projectNeeds(project, held, host, targetRoot, parse, configuration, workflows) {
|
|
209
212
|
const programPackages = new Set();
|
|
210
213
|
for (const name of project.program.getSourceFileNames()) {
|
|
211
214
|
const named = packageOfPath(name);
|
|
@@ -256,7 +259,6 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
256
259
|
return names;
|
|
257
260
|
};
|
|
258
261
|
const files = sourceFilesOf(project, targetRoot);
|
|
259
|
-
const configuration = configurationFiles(host, targetRoot, files, parse);
|
|
260
262
|
for (const { file, manifestDir } of configuration.modules) {
|
|
261
263
|
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
262
264
|
for (const { node, text } of moduleStrings(file)) {
|
|
@@ -271,7 +273,7 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
271
273
|
}
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
|
-
const ranFiles = outsideChain(host, targetRoot, files, parse, scriptRunFiles(host, targetRoot, files));
|
|
276
|
+
const ranFiles = outsideChain(host, targetRoot, files, parse, scriptRunFiles(host, targetRoot, files, workflows));
|
|
275
277
|
for (const { file, manifestDirs } of [...configuration.outside, ...ranFiles]) {
|
|
276
278
|
for (const manifestDir of manifestDirs) {
|
|
277
279
|
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
@@ -288,7 +290,7 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
288
290
|
* workflow step or git hook names, with the target root's: a path `pathsNamed` reads, below the
|
|
289
291
|
* target root and in no `node_modules`.
|
|
290
292
|
*/
|
|
291
|
-
function scriptRunFiles(host, targetRoot, files) {
|
|
293
|
+
function scriptRunFiles(host, targetRoot, files, workflows) {
|
|
292
294
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
293
295
|
const dirs = new Set([root]);
|
|
294
296
|
for (const name of files.byName.keys()) {
|
|
@@ -300,12 +302,7 @@ function scriptRunFiles(host, targetRoot, files) {
|
|
|
300
302
|
}
|
|
301
303
|
}
|
|
302
304
|
const named = new Map([
|
|
303
|
-
[
|
|
304
|
-
root,
|
|
305
|
-
workflowReadings(host, root)
|
|
306
|
-
.flatMap(pathsNamed)
|
|
307
|
-
.map((one) => one.path),
|
|
308
|
-
],
|
|
305
|
+
[root, workflows.readings.flatMap(pathsNamed).map((one) => one.path)],
|
|
309
306
|
]);
|
|
310
307
|
for (const dir of dirs) {
|
|
311
308
|
const read = readDocument(host, joinPath(dir, MANIFEST));
|
|
@@ -341,19 +338,9 @@ function manifestUses(host, dir, manifest) {
|
|
|
341
338
|
return { ...uses, held: [uses.held, ...texts].join("\n") };
|
|
342
339
|
}
|
|
343
340
|
/** What the target root's workflow steps, git hooks and recipes use, held with their files' whole text. */
|
|
344
|
-
function workflowUses(
|
|
345
|
-
const uses = usesOf(
|
|
346
|
-
return { ...uses, held: [uses.held, ...
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* What the target root's workflow steps, git hooks, hook managers and recipes run, each held
|
|
350
|
-
* with the directory the file writes for it.
|
|
351
|
-
*/
|
|
352
|
-
function workflowReadings(host, targetRoot) {
|
|
353
|
-
return workflowCommands(host, targetRoot).map(({ text, dir, writtenDirectory }) => {
|
|
354
|
-
const reading = readCommands(text, dir);
|
|
355
|
-
return { ...reading, held: [reading.held, writtenDirectory ?? ""].join("\n") };
|
|
356
|
-
});
|
|
341
|
+
function workflowUses(workflows) {
|
|
342
|
+
const uses = usesOf(workflows.readings);
|
|
343
|
+
return { ...uses, held: [uses.held, ...workflows.texts].join("\n") };
|
|
357
344
|
}
|
|
358
345
|
function usesOf(readings) {
|
|
359
346
|
return {
|
|
@@ -439,7 +426,15 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
|
|
|
439
426
|
if (names.size > 0 || automatic.globals.size > 0) {
|
|
440
427
|
visit(file);
|
|
441
428
|
}
|
|
442
|
-
const
|
|
429
|
+
const loads = runtimeLoads(file);
|
|
430
|
+
const callees = loads.flatMap((load) => load.callees);
|
|
431
|
+
const asked = [
|
|
432
|
+
...specifiers,
|
|
433
|
+
...bindings.map(({ node }) => node),
|
|
434
|
+
...candidates,
|
|
435
|
+
...globals,
|
|
436
|
+
...callees,
|
|
437
|
+
];
|
|
443
438
|
const handles = asked.map((node) => project.handle(node));
|
|
444
439
|
const resolved = project.symbolsAt(handles);
|
|
445
440
|
const packagesOf = new Map();
|
|
@@ -510,33 +505,69 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
|
|
|
510
505
|
for (const reference of file.typeReferenceDirectives) {
|
|
511
506
|
use(ownersOf(file, held, file), typePackages(reference.fileName, programPackages));
|
|
512
507
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
508
|
+
// A callee the checker leaves unanswered may be a require, so its call holds the
|
|
509
|
+
// dependency it names by live code, as program.33 holds one.
|
|
510
|
+
const calleesAt = unresolvedAt + globals.length;
|
|
511
|
+
const decided = new Map();
|
|
512
|
+
const requires = new Set();
|
|
513
|
+
const unansweredCallees = new Set();
|
|
514
|
+
callees.forEach((callee, index) => {
|
|
515
|
+
const symbol = resolved[calleesAt + index];
|
|
516
|
+
if (symbol === UNANSWERED) {
|
|
517
|
+
unansweredCallees.add(callee);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
// The decision reads the callee's symbol and its written name, and nothing else of it.
|
|
521
|
+
const key = symbol === undefined ? undefined : `${symbol.id}\u0000${callee.text}`;
|
|
522
|
+
const answer = (key === undefined ? undefined : decided.get(key)) ??
|
|
523
|
+
callsPlatform(project, "require", callee, symbol, () => undefined);
|
|
524
|
+
if (key !== undefined) {
|
|
525
|
+
decided.set(key, answer);
|
|
526
|
+
}
|
|
527
|
+
if (answer) {
|
|
528
|
+
requires.add(callee);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
for (const { specifier, named, callees: through } of loads) {
|
|
532
|
+
if (through.length === 0 || through.some((one) => requires.has(one))) {
|
|
516
533
|
use(ownersOf(file, held, specifier), [named]);
|
|
517
534
|
}
|
|
535
|
+
else if (through.some((one) => unansweredCallees.has(one))) {
|
|
536
|
+
use([PROJECT_OWNER], [named]);
|
|
537
|
+
}
|
|
518
538
|
}
|
|
519
539
|
return resolved.slice(0, specifiers.length).includes(UNANSWERED);
|
|
520
540
|
}
|
|
521
541
|
/**
|
|
522
|
-
* The
|
|
523
|
-
*
|
|
542
|
+
* The calls one file may load or resolve a package with at run time, each of whose first
|
|
543
|
+
* argument is literal text naming a package: a `require()` call, which the compiler lists among
|
|
544
|
+
* a file's imports only in a JavaScript file and only through the bare name; a `.resolve` of
|
|
545
|
+
* another name, which needs it where that name is a `require`; and `import.meta.resolve` or
|
|
546
|
+
* a `.resolve` of the name `require`, whatever it names, a use where it stands.
|
|
524
547
|
*/
|
|
525
|
-
function
|
|
548
|
+
function runtimeLoads(file) {
|
|
526
549
|
const found = [];
|
|
527
|
-
if (!file.text.includes(".resolve(")) {
|
|
528
|
-
return found;
|
|
529
|
-
}
|
|
530
550
|
const visit = (node) => {
|
|
531
|
-
if (isCallExpression(node)
|
|
532
|
-
const callee = node.expression;
|
|
551
|
+
if (isCallExpression(node)) {
|
|
533
552
|
const [argument] = node.arguments;
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
553
|
+
if (argument !== undefined && isLiteralText(argument)) {
|
|
554
|
+
const named = packageOfSpecifier(argument.text);
|
|
555
|
+
const callee = parenthesesRemoved(node.expression);
|
|
556
|
+
const receiver = isPropertyAccessExpression(callee) && callee.name.text === "resolve"
|
|
557
|
+
? parenthesesRemoved(callee.expression)
|
|
558
|
+
: undefined;
|
|
559
|
+
const stands = receiver !== undefined &&
|
|
560
|
+
((isMetaProperty(receiver) && receiver.name.text === "meta") ||
|
|
561
|
+
(isIdentifier(receiver) && receiver.text === "require"));
|
|
562
|
+
const callees = stands
|
|
563
|
+
? []
|
|
564
|
+
: [
|
|
565
|
+
writtenName(callee),
|
|
566
|
+
receiver === undefined ? undefined : writtenName(receiver),
|
|
567
|
+
].filter((one) => one !== undefined);
|
|
568
|
+
if (named !== undefined && (stands || callees.length > 0)) {
|
|
569
|
+
found.push({ specifier: argument, named, callees });
|
|
570
|
+
}
|
|
540
571
|
}
|
|
541
572
|
}
|
|
542
573
|
node.forEachChild(visit);
|
|
@@ -727,7 +758,7 @@ function addNeededPeers(declared, peers, needed, ran) {
|
|
|
727
758
|
* manifest declares nothing, nor does a nested package's uninstalled dependency, because
|
|
728
759
|
* no run requires a nested package to be installed.
|
|
729
760
|
*/
|
|
730
|
-
export function dependenciesOf(host, targetRoot, perProject, candidates, members = [], unbuiltMembers = [], nested = []) {
|
|
761
|
+
export function dependenciesOf(host, targetRoot, workflows, perProject, candidates, members = [], unbuiltMembers = [], nested = []) {
|
|
731
762
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
732
763
|
const scopeOf = packageScope(host, targetRoot);
|
|
733
764
|
const manifests = new Map([[root, MANIFEST]]);
|
|
@@ -744,7 +775,7 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
744
775
|
unbuilt.add(path);
|
|
745
776
|
}
|
|
746
777
|
}
|
|
747
|
-
const workflow = workflowUses(
|
|
778
|
+
const workflow = workflowUses(workflows);
|
|
748
779
|
const declared = [];
|
|
749
780
|
const ran = new Set();
|
|
750
781
|
const peers = new Map();
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files a bundler's JavaScript API builds from, read from the build scripts a
|
|
3
|
+
* manifest's scripts run: the strings an options object passes as the bundler's entries.
|
|
4
|
+
* A script is read without running it, so a value that is not written out, a computed
|
|
5
|
+
* path or an options object built at run time among them, names nothing.
|
|
6
|
+
*/
|
|
7
|
+
import { isArrowFunction, isCallExpression, isConstructorDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isNewExpression, isSetAccessorDeclaration, isVariableDeclaration, } from "@typescript/native/ast";
|
|
8
|
+
import { declarationOf, membersNamed, staticModule } from "./static-module.js";
|
|
9
|
+
import { filesNamedBy } from "./configuration-files.js";
|
|
10
|
+
import { ancestors, dirnamePath, relativePath, resolvePath } from "../paths.js";
|
|
11
|
+
import { bytewise } from "../text/compare.js";
|
|
12
|
+
import { unwrapped } from "../unwrap.js";
|
|
13
|
+
import { holdsManifest, inInstalledPackages, manifestAt } from "../fs/manifest.js";
|
|
14
|
+
import { globExpression } from "../text/glob.js";
|
|
15
|
+
import { pathsNamed, readCommands } from "../commands/read.js";
|
|
16
|
+
import { manifestCommands } from "../roots/manifest.js";
|
|
17
|
+
import { extensionOf, MODULE_EXTENSIONS } from "../extensions.js";
|
|
18
|
+
/**
|
|
19
|
+
* The bundlers whose build call names its entries: esbuild resolves `entryPoints` against
|
|
20
|
+
* `absWorkingDir` (https://esbuild.github.io/api/#working-directory), webpack `entry`
|
|
21
|
+
* against `context` (https://webpack.js.org/configuration/entry-context/), and Rollup
|
|
22
|
+
* `input` against the working directory, each the manifest's directory when its script runs.
|
|
23
|
+
*/
|
|
24
|
+
const BUNDLERS = [
|
|
25
|
+
{
|
|
26
|
+
module: "esbuild",
|
|
27
|
+
calls: ["build", "buildSync", "context"],
|
|
28
|
+
option: "entryPoints",
|
|
29
|
+
base: "absWorkingDir",
|
|
30
|
+
paths: true,
|
|
31
|
+
},
|
|
32
|
+
{ module: "webpack", calls: ["default"], option: "entry", base: "context", paths: false },
|
|
33
|
+
{ module: "rollup", calls: ["rollup", "watch"], option: "input", base: "", paths: true },
|
|
34
|
+
];
|
|
35
|
+
/** The one value an object writes last for a key, undefined where it writes none or may hide it. */
|
|
36
|
+
function lastMember(object, key) {
|
|
37
|
+
return membersNamed(object, key)?.at(-1);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The path one entry value writes, read against `dir`: a literal, or a join of the
|
|
41
|
+
* script's own directory and literals, read against that directory.
|
|
42
|
+
*/
|
|
43
|
+
function pathOf(value, dir, scriptDir) {
|
|
44
|
+
if (value.kind === "text") {
|
|
45
|
+
const { text } = value;
|
|
46
|
+
const request = !text.startsWith("./") && !text.startsWith("../") && !text.startsWith("/");
|
|
47
|
+
return { path: resolvePath(dir, text), text, request };
|
|
48
|
+
}
|
|
49
|
+
if (value.kind === "directory") {
|
|
50
|
+
const joined = value.segments.join("/");
|
|
51
|
+
return { path: resolvePath(scriptDir, joined), text: joined, request: false };
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** The entry values one entries option writes: a value, an array of them, an object of them. */
|
|
56
|
+
function entryValues(value) {
|
|
57
|
+
if (value.kind === "array") {
|
|
58
|
+
return value.elements.flatMap(entryValues);
|
|
59
|
+
}
|
|
60
|
+
if (value.kind === "object") {
|
|
61
|
+
const imported = lastMember(value, "import") ?? lastMember(value, "in");
|
|
62
|
+
if (imported !== undefined) {
|
|
63
|
+
return entryValues(imported);
|
|
64
|
+
}
|
|
65
|
+
return value.members.flatMap((member) => entryValues(member.value));
|
|
66
|
+
}
|
|
67
|
+
return [value];
|
|
68
|
+
}
|
|
69
|
+
/** The own files one entry string names, read as `api` reads it. */
|
|
70
|
+
function entriesNamed(files, api, entry) {
|
|
71
|
+
if (!api.paths && entry.request) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
if (api.paths && /[*?[{]/u.test(entry.text)) {
|
|
75
|
+
const expression = globExpression(entry.path);
|
|
76
|
+
return [...files.byName.values()].filter((file) => expression.test(file.fileName));
|
|
77
|
+
}
|
|
78
|
+
const dir = dirnamePath(entry.path);
|
|
79
|
+
return filesNamedBy(files, dir, entry.path.slice(dir.length + 1));
|
|
80
|
+
}
|
|
81
|
+
/** Whether one node is a function, whose body runs only where something calls it. */
|
|
82
|
+
function isFunction(node) {
|
|
83
|
+
return (isFunctionDeclaration(node) ||
|
|
84
|
+
isFunctionExpression(node) ||
|
|
85
|
+
isArrowFunction(node) ||
|
|
86
|
+
isMethodDeclaration(node) ||
|
|
87
|
+
isConstructorDeclaration(node) ||
|
|
88
|
+
isGetAccessorDeclaration(node) ||
|
|
89
|
+
isSetAccessorDeclaration(node));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The calls one build script runs: those of its top level, and those of each function a
|
|
93
|
+
* call it runs calls or passes on, as a callee, an argument or a function called where it
|
|
94
|
+
* is written, a name read through to the function it declares.
|
|
95
|
+
*/
|
|
96
|
+
function runCalls(file, each) {
|
|
97
|
+
const ran = new Set();
|
|
98
|
+
const run = (fn) => {
|
|
99
|
+
if (!ran.has(fn)) {
|
|
100
|
+
ran.add(fn);
|
|
101
|
+
fn.forEachChild(visit);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const runs = (node) => {
|
|
105
|
+
const at = unwrapped(node);
|
|
106
|
+
const declared = isIdentifier(at) ? declarationOf(at) : undefined;
|
|
107
|
+
const fn = declared !== undefined && isVariableDeclaration(declared) && declared.initializer
|
|
108
|
+
? unwrapped(declared.initializer)
|
|
109
|
+
: (declared ?? at);
|
|
110
|
+
if (isFunction(fn)) {
|
|
111
|
+
run(fn);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const visit = (node) => {
|
|
115
|
+
if (isFunction(node)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (isCallExpression(node) || isNewExpression(node)) {
|
|
119
|
+
if (isCallExpression(node)) {
|
|
120
|
+
each(node);
|
|
121
|
+
}
|
|
122
|
+
runs(node.expression);
|
|
123
|
+
for (const argument of node.arguments ?? []) {
|
|
124
|
+
runs(argument);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
node.forEachChild(visit);
|
|
128
|
+
};
|
|
129
|
+
file.forEachChild(visit);
|
|
130
|
+
}
|
|
131
|
+
/** The entries every bundler call one build script runs names, read against `dir`. */
|
|
132
|
+
function scriptEntries(file, dir, files) {
|
|
133
|
+
const module = staticModule(file);
|
|
134
|
+
const building = new Map();
|
|
135
|
+
for (const api of BUNDLERS) {
|
|
136
|
+
for (const one of module.calls(api.module)) {
|
|
137
|
+
if (api.calls.includes(one.export)) {
|
|
138
|
+
building.set(one.call, api);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const scriptDir = dirnamePath(file.fileName);
|
|
143
|
+
const found = [];
|
|
144
|
+
runCalls(file, (call) => {
|
|
145
|
+
const api = building.get(call);
|
|
146
|
+
if (api === undefined) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
for (const options of module.argument(call)) {
|
|
150
|
+
const entries = lastMember(options, api.option);
|
|
151
|
+
const base = api.base === "" ? undefined : lastMember(options, api.base);
|
|
152
|
+
const against = base === undefined ? { path: dir } : pathOf(base, dir, scriptDir);
|
|
153
|
+
if (entries === undefined || against === undefined) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
for (const value of entryValues(entries)) {
|
|
157
|
+
const named = pathOf(value, against.path, scriptDir);
|
|
158
|
+
if (named === undefined) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
for (const entry of entriesNamed(files, api, named)) {
|
|
162
|
+
found.push({ file: entry, source: named.text });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return found;
|
|
168
|
+
}
|
|
169
|
+
/** The modules one manifest's command strings run or name, by absolute path, each below the target root. */
|
|
170
|
+
function scriptModules(host, root, dir) {
|
|
171
|
+
const manifest = manifestAt(host, dir);
|
|
172
|
+
const found = new Set();
|
|
173
|
+
for (const { command } of manifest === undefined ? [] : manifestCommands(host, dir, manifest)) {
|
|
174
|
+
for (const { path } of pathsNamed(readCommands(command, dir))) {
|
|
175
|
+
if (extensionOf(path, MODULE_EXTENSIONS) !== undefined &&
|
|
176
|
+
relativePath(root, path) !== undefined &&
|
|
177
|
+
!inInstalledPackages(path) &&
|
|
178
|
+
host.kindOf(path) === "file") {
|
|
179
|
+
found.add(path);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return [...found].sort(bytewise);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Every own file of one project a build script names as a bundler's entry: a module a
|
|
187
|
+
* script of a manifest at or above an own file runs, below the target root, read from
|
|
188
|
+
* the program where it holds the module and parsed alone otherwise, where a parser is given.
|
|
189
|
+
*/
|
|
190
|
+
export function buildScriptEntries(host, targetRoot, files, parse) {
|
|
191
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
192
|
+
const dirs = new Set([root]);
|
|
193
|
+
for (const file of files.byName.values()) {
|
|
194
|
+
for (const dir of ancestors(dirnamePath(file.fileName), root)) {
|
|
195
|
+
if (dirs.has(dir)) {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
dirs.add(dir);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const found = [];
|
|
202
|
+
for (const dir of [...dirs].sort(bytewise)) {
|
|
203
|
+
if (!holdsManifest(host, dir)) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
for (const path of scriptModules(host, root, dir)) {
|
|
207
|
+
let script = files.byName.get(path);
|
|
208
|
+
if (script === undefined && parse !== undefined) {
|
|
209
|
+
try {
|
|
210
|
+
script = parse(path, host.readFile(path));
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (script !== undefined) {
|
|
217
|
+
found.push(...scriptEntries(script, dir, files));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return found;
|
|
222
|
+
}
|
|
@@ -6,19 +6,26 @@
|
|
|
6
6
|
* every string either holds as it likes, so each one that spells a declared dependency
|
|
7
7
|
* uses it and each relative one that names a source file of the program roots the file.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
9
|
+
import { isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/ast";
|
|
10
|
+
import { relativeSpecifiers } from "./static-module.js";
|
|
11
|
+
import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "../paths.js";
|
|
12
|
+
import { bytewise } from "../text/compare.js";
|
|
13
|
+
import { isRecord } from "../json-document.js";
|
|
14
|
+
import { filesIn } from "../fs/tree.js";
|
|
15
|
+
import { DEPENDENCY_SECTIONS, holdsManifest, inInstalledPackages, manifestAt, readJSON, } from "../fs/manifest.js";
|
|
16
|
+
import { readCommands } from "../commands/read.js";
|
|
17
|
+
import { manifestCommands } from "../roots/manifest.js";
|
|
18
|
+
import { APPENDED_EXTENSIONS, extensionOf, MODULE_EXTENSIONS } from "../extensions.js";
|
|
19
|
+
/** The extensions of a configuration module, as an expression's alternatives. */
|
|
20
|
+
const MODULE_NAME = MODULE_EXTENSIONS.map((one) => one.slice(1)).join("|");
|
|
17
21
|
/** A configuration module's name, each form with the spelling a root reports it by. */
|
|
18
22
|
const MODULE_FORMS = [
|
|
19
|
-
{ name: /^[\w-]+\.config\.(?:js|mjs|cjs|ts|mts|cts)$/u, form: "<stem>.config.<ext>" },
|
|
20
23
|
{
|
|
21
|
-
name:
|
|
24
|
+
name: new RegExp(String.raw `^[\w-]+\.config\.(?:${MODULE_NAME})$`, "u"),
|
|
25
|
+
form: "<stem>.config.<ext>",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: new RegExp(String.raw `^[\w-]+\.[\w-]+\.config\.(?:${MODULE_NAME})$`, "u"),
|
|
22
29
|
form: "<stem>.<qualifier>.config.<ext>",
|
|
23
30
|
},
|
|
24
31
|
{ name: /^\.[\w-]+rc\.(?:js|mjs|cjs)$/u, form: ".<stem>rc.<ext>" },
|
|
@@ -34,8 +41,6 @@ const COUNTERPARTS = [
|
|
|
34
41
|
[".mjs", [".mts"]],
|
|
35
42
|
[".cjs", [".cts"]],
|
|
36
43
|
];
|
|
37
|
-
/** The extensions appended to a relative specifier from a configuration file, in order. */
|
|
38
|
-
const APPENDED = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
39
44
|
/** The extensions the program's module resolution appends to a specifier written without one. */
|
|
40
45
|
export const RESOLVED_EXTENSIONS = [
|
|
41
46
|
".ts",
|
|
@@ -65,8 +70,6 @@ export function configurationModuleForm(name) {
|
|
|
65
70
|
export function isConfigurationDocumentName(name) {
|
|
66
71
|
return DOCUMENT_NAME.test(name);
|
|
67
72
|
}
|
|
68
|
-
/** The extensions of a module a configuration flag may name. */
|
|
69
|
-
const MODULE_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"];
|
|
70
73
|
/**
|
|
71
74
|
* The files the command strings of the manifest in one directory name after a configuration
|
|
72
75
|
* flag, as `commands/read.ts` reads them against the directory each command runs in, each a
|
|
@@ -200,47 +203,6 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
200
203
|
: outsideModules(host, root, [...dirs], files, manifestHeld, parse, named),
|
|
201
204
|
};
|
|
202
205
|
}
|
|
203
|
-
/** The literal module specifier one `require(...)` or `import(...)` call names, if any. */
|
|
204
|
-
function calledSpecifier(node) {
|
|
205
|
-
if (!isCallExpression(node) || node.arguments.length !== 1) {
|
|
206
|
-
return undefined;
|
|
207
|
-
}
|
|
208
|
-
const callee = node.expression;
|
|
209
|
-
const loads = callee.kind === SyntaxKind.ImportKeyword || (isIdentifier(callee) && callee.text === "require");
|
|
210
|
-
return loads ? node.arguments[0] : undefined;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* The relative specifiers one file's imports and re-exports name, and those its
|
|
214
|
-
* `require(...)` and `import(...)` calls name by a literal.
|
|
215
|
-
*/
|
|
216
|
-
export function relativeSpecifiers(file) {
|
|
217
|
-
const known = specifiersOf.get(file);
|
|
218
|
-
if (known !== undefined) {
|
|
219
|
-
return known;
|
|
220
|
-
}
|
|
221
|
-
const found = [];
|
|
222
|
-
const named = (specifier) => {
|
|
223
|
-
if (specifier !== undefined &&
|
|
224
|
-
(isStringLiteral(specifier) || isNoSubstitutionTemplateLiteral(specifier)) &&
|
|
225
|
-
RELATIVE_PREFIXES.some((prefix) => specifier.text.startsWith(prefix))) {
|
|
226
|
-
found.push(specifier.text);
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
const visit = (node) => {
|
|
230
|
-
named(calledSpecifier(node));
|
|
231
|
-
node.forEachChild(visit);
|
|
232
|
-
};
|
|
233
|
-
for (const statement of file.statements) {
|
|
234
|
-
named(isImportDeclaration(statement) || isExportDeclaration(statement)
|
|
235
|
-
? statement.moduleSpecifier
|
|
236
|
-
: undefined);
|
|
237
|
-
visit(statement);
|
|
238
|
-
}
|
|
239
|
-
specifiersOf.set(file, found);
|
|
240
|
-
return found;
|
|
241
|
-
}
|
|
242
|
-
/** What {@link relativeSpecifiers} found in each file, which every project of a run reads alike. */
|
|
243
|
-
const specifiersOf = new WeakMap();
|
|
244
206
|
/**
|
|
245
207
|
* The files no program holds that a chain from `seeds` reads: each seed, and each file
|
|
246
208
|
* below the target root one imports, re-exports or loads by a relative specifier that no
|
|
@@ -261,7 +223,7 @@ export function outsideChain(host, targetRoot, files, parse, seeds) {
|
|
|
261
223
|
export function specifierCandidates(dir, specifier) {
|
|
262
224
|
const path = resolvePath(dir, specifier);
|
|
263
225
|
const candidates = [];
|
|
264
|
-
if (
|
|
226
|
+
if (extensionOf(path, APPENDED_EXTENSIONS) !== undefined) {
|
|
265
227
|
candidates.push(path);
|
|
266
228
|
}
|
|
267
229
|
for (const [extension, counterparts] of COUNTERPARTS) {
|
|
@@ -270,8 +232,8 @@ export function specifierCandidates(dir, specifier) {
|
|
|
270
232
|
candidates.push(...counterparts.map((one) => stem + one));
|
|
271
233
|
}
|
|
272
234
|
}
|
|
273
|
-
candidates.push(...
|
|
274
|
-
candidates.push(...
|
|
235
|
+
candidates.push(...APPENDED_EXTENSIONS.map((extension) => path + extension));
|
|
236
|
+
candidates.push(...APPENDED_EXTENSIONS.map((extension) => joinPath(path, `index${extension}`)));
|
|
275
237
|
return candidates;
|
|
276
238
|
}
|
|
277
239
|
/** The file on disk one relative specifier from a configuration file reaches, or undefined. */
|