@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/dist/src/dependencies.js
CHANGED
|
@@ -8,13 +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 {
|
|
12
|
-
import { nodeKey
|
|
11
|
+
import { declaredIn as declaredBeside, dependenciesNamed, moduleStrings, outsideChain, } from "./entries/configuration-files.js";
|
|
12
|
+
import { nodeKey } from "./inventory.js";
|
|
13
|
+
import { packageScope } from "./load/package-scope.js";
|
|
13
14
|
import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
|
|
14
15
|
import { UNANSWERED } from "./query.js";
|
|
15
16
|
import { sourceFilesOf } from "./source-files.js";
|
|
16
|
-
import { manifestCommands, nodemonTexts } from "./manifest.js";
|
|
17
|
-
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";
|
|
18
21
|
import { pathsNamed, preloadedPackages, readCommands, } from "./commands/read.js";
|
|
19
22
|
import { heldNames } from "./text/named-files.js";
|
|
20
23
|
import { bytewise } from "./text/compare.js";
|
|
@@ -201,10 +204,11 @@ function bindingsOf(node) {
|
|
|
201
204
|
* Every package one project needs, and which declaration each use is written in.
|
|
202
205
|
* One local walk and one batched resolution per file: a use of an import binding is
|
|
203
206
|
* an identifier resolving to the binding's own symbol, so a local declaration that
|
|
204
|
-
* shadows the name is not one. A string of a configuration file
|
|
205
|
-
* 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.
|
|
206
210
|
*/
|
|
207
|
-
export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
211
|
+
export function projectNeeds(project, held, host, targetRoot, parse, configuration, workflows) {
|
|
208
212
|
const programPackages = new Set();
|
|
209
213
|
for (const name of project.program.getSourceFileNames()) {
|
|
210
214
|
const named = packageOfPath(name);
|
|
@@ -255,7 +259,6 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
255
259
|
return names;
|
|
256
260
|
};
|
|
257
261
|
const files = sourceFilesOf(project, targetRoot);
|
|
258
|
-
const configuration = configurationFiles(host, targetRoot, files, parse);
|
|
259
262
|
for (const { file, manifestDir } of configuration.modules) {
|
|
260
263
|
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
261
264
|
for (const { node, text } of moduleStrings(file)) {
|
|
@@ -270,7 +273,7 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
270
273
|
}
|
|
271
274
|
}
|
|
272
275
|
}
|
|
273
|
-
const ranFiles = outsideChain(host, targetRoot, files, parse, scriptRunFiles(host, targetRoot, files));
|
|
276
|
+
const ranFiles = outsideChain(host, targetRoot, files, parse, scriptRunFiles(host, targetRoot, files, workflows));
|
|
274
277
|
for (const { file, manifestDirs } of [...configuration.outside, ...ranFiles]) {
|
|
275
278
|
for (const manifestDir of manifestDirs) {
|
|
276
279
|
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
@@ -287,7 +290,7 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
287
290
|
* workflow step or git hook names, with the target root's: a path `pathsNamed` reads, below the
|
|
288
291
|
* target root and in no `node_modules`.
|
|
289
292
|
*/
|
|
290
|
-
function scriptRunFiles(host, targetRoot, files) {
|
|
293
|
+
function scriptRunFiles(host, targetRoot, files, workflows) {
|
|
291
294
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
292
295
|
const dirs = new Set([root]);
|
|
293
296
|
for (const name of files.byName.keys()) {
|
|
@@ -299,12 +302,7 @@ function scriptRunFiles(host, targetRoot, files) {
|
|
|
299
302
|
}
|
|
300
303
|
}
|
|
301
304
|
const named = new Map([
|
|
302
|
-
[
|
|
303
|
-
root,
|
|
304
|
-
workflowReadings(host, root)
|
|
305
|
-
.flatMap(pathsNamed)
|
|
306
|
-
.map((one) => one.path),
|
|
307
|
-
],
|
|
305
|
+
[root, workflows.readings.flatMap(pathsNamed).map((one) => one.path)],
|
|
308
306
|
]);
|
|
309
307
|
for (const dir of dirs) {
|
|
310
308
|
const read = readDocument(host, joinPath(dir, MANIFEST));
|
|
@@ -340,19 +338,9 @@ function manifestUses(host, dir, manifest) {
|
|
|
340
338
|
return { ...uses, held: [uses.held, ...texts].join("\n") };
|
|
341
339
|
}
|
|
342
340
|
/** What the target root's workflow steps, git hooks and recipes use, held with their files' whole text. */
|
|
343
|
-
function workflowUses(
|
|
344
|
-
const uses = usesOf(
|
|
345
|
-
return { ...uses, held: [uses.held, ...
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* What the target root's workflow steps, git hooks, hook managers and recipes run, each held
|
|
349
|
-
* with the directory the file writes for it.
|
|
350
|
-
*/
|
|
351
|
-
function workflowReadings(host, targetRoot) {
|
|
352
|
-
return workflowCommands(host, targetRoot).map(({ text, dir, writtenDirectory }) => {
|
|
353
|
-
const reading = readCommands(text, dir);
|
|
354
|
-
return { ...reading, held: [reading.held, writtenDirectory ?? ""].join("\n") };
|
|
355
|
-
});
|
|
341
|
+
function workflowUses(workflows) {
|
|
342
|
+
const uses = usesOf(workflows.readings);
|
|
343
|
+
return { ...uses, held: [uses.held, ...workflows.texts].join("\n") };
|
|
356
344
|
}
|
|
357
345
|
function usesOf(readings) {
|
|
358
346
|
return {
|
|
@@ -438,7 +426,15 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
|
|
|
438
426
|
if (names.size > 0 || automatic.globals.size > 0) {
|
|
439
427
|
visit(file);
|
|
440
428
|
}
|
|
441
|
-
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
|
+
];
|
|
442
438
|
const handles = asked.map((node) => project.handle(node));
|
|
443
439
|
const resolved = project.symbolsAt(handles);
|
|
444
440
|
const packagesOf = new Map();
|
|
@@ -509,33 +505,69 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
|
|
|
509
505
|
for (const reference of file.typeReferenceDirectives) {
|
|
510
506
|
use(ownersOf(file, held, file), typePackages(reference.fileName, programPackages));
|
|
511
507
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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))) {
|
|
515
533
|
use(ownersOf(file, held, specifier), [named]);
|
|
516
534
|
}
|
|
535
|
+
else if (through.some((one) => unansweredCallees.has(one))) {
|
|
536
|
+
use([PROJECT_OWNER], [named]);
|
|
537
|
+
}
|
|
517
538
|
}
|
|
518
539
|
return resolved.slice(0, specifiers.length).includes(UNANSWERED);
|
|
519
540
|
}
|
|
520
541
|
/**
|
|
521
|
-
* The
|
|
522
|
-
*
|
|
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.
|
|
523
547
|
*/
|
|
524
|
-
function
|
|
548
|
+
function runtimeLoads(file) {
|
|
525
549
|
const found = [];
|
|
526
|
-
if (!file.text.includes(".resolve(")) {
|
|
527
|
-
return found;
|
|
528
|
-
}
|
|
529
550
|
const visit = (node) => {
|
|
530
|
-
if (isCallExpression(node)
|
|
531
|
-
const callee = node.expression;
|
|
551
|
+
if (isCallExpression(node)) {
|
|
532
552
|
const [argument] = node.arguments;
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
+
}
|
|
539
571
|
}
|
|
540
572
|
}
|
|
541
573
|
node.forEachChild(visit);
|
|
@@ -726,7 +758,7 @@ function addNeededPeers(declared, peers, needed, ran) {
|
|
|
726
758
|
* manifest declares nothing, nor does a nested package's uninstalled dependency, because
|
|
727
759
|
* no run requires a nested package to be installed.
|
|
728
760
|
*/
|
|
729
|
-
export function dependenciesOf(host, targetRoot, perProject, candidates, members = [], unbuiltMembers = [], nested = []) {
|
|
761
|
+
export function dependenciesOf(host, targetRoot, workflows, perProject, candidates, members = [], unbuiltMembers = [], nested = []) {
|
|
730
762
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
731
763
|
const scopeOf = packageScope(host, targetRoot);
|
|
732
764
|
const manifests = new Map([[root, MANIFEST]]);
|
|
@@ -743,7 +775,7 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
743
775
|
unbuilt.add(path);
|
|
744
776
|
}
|
|
745
777
|
}
|
|
746
|
-
const workflow = workflowUses(
|
|
778
|
+
const workflow = workflowUses(workflows);
|
|
747
779
|
const declared = [];
|
|
748
780
|
const ran = new Set();
|
|
749
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. */
|