@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/references.ts
CHANGED
|
@@ -32,26 +32,23 @@ import {
|
|
|
32
32
|
isSpreadAssignment,
|
|
33
33
|
isSpreadElement,
|
|
34
34
|
isStringLiteral,
|
|
35
|
+
isStringLiteralLikeNode,
|
|
35
36
|
isVoidExpression,
|
|
36
37
|
SyntaxKind,
|
|
38
|
+
type Identifier,
|
|
37
39
|
type Node,
|
|
38
40
|
type ParameterDeclaration,
|
|
39
41
|
type SourceFile,
|
|
42
|
+
type StringLiteralLikeNode,
|
|
40
43
|
} from "@typescript/native/ast";
|
|
41
44
|
import { parenthesesRemoved } from "./unwrap.ts";
|
|
42
45
|
import type { Symbol as TSSymbol } from "@typescript/native/sync";
|
|
43
46
|
import { aliasChains, type ChainTarget } from "./alias-chain.ts";
|
|
44
47
|
import { contextualUses, type FlowAt, type LiteralAt } from "./contextual-uses.ts";
|
|
45
48
|
import { destructuring, propertyReadsOf, type PropertyRead } from "./destructuring.ts";
|
|
46
|
-
import { specifierCandidates } from "./configuration-files.ts";
|
|
49
|
+
import { specifierCandidates } from "./entries/configuration-files.ts";
|
|
47
50
|
import { namespaceUses } from "./namespace-objects.ts";
|
|
48
|
-
import {
|
|
49
|
-
declarationsByName,
|
|
50
|
-
isParameterProperty,
|
|
51
|
-
nodeKey,
|
|
52
|
-
type Inventory,
|
|
53
|
-
type InventorySymbol,
|
|
54
|
-
} from "./inventory.ts";
|
|
51
|
+
import { isParameterProperty, nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
55
52
|
import { ambientGlobalInterfaces, augmentationReferences } from "./augmentations.ts";
|
|
56
53
|
import { elementAccessReferences } from "./element-access.ts";
|
|
57
54
|
import { indexedAccessReferences } from "./indexed-access.ts";
|
|
@@ -59,12 +56,23 @@ import { keyofComparisonReferences } from "./keyof-comparisons.ts";
|
|
|
59
56
|
import { unimplementedCalleeReads } from "./unimplemented-callees.ts";
|
|
60
57
|
import { moduleDeclarationReferences } from "./module-declarations.ts";
|
|
61
58
|
import { overrideReferences } from "./overrides.ts";
|
|
62
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
dirnamePath,
|
|
61
|
+
queriedModulePath,
|
|
62
|
+
relativePath,
|
|
63
|
+
relativeURLPath,
|
|
64
|
+
resolvePath,
|
|
65
|
+
} from "./paths.ts";
|
|
63
66
|
import { byPosition, isComponentFile, renderPosition, type Position } from "./position.ts";
|
|
64
67
|
import { DEFAULT_BATCH_CAP, propertyTables, UNANSWERED, type Answer } from "./query.ts";
|
|
65
68
|
import type { ProjectView } from "./session.ts";
|
|
69
|
+
import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
|
|
66
70
|
import { typeParameterReads } from "./type-parameter-reads.ts";
|
|
67
71
|
import { typeQueryAliases } from "./type-query-alias.ts";
|
|
72
|
+
import { callsPlatform, isPlatformGlobal, startsThread } from "./platform-symbols.ts";
|
|
73
|
+
import { writtenName } from "./platform-calls.ts";
|
|
74
|
+
import type { RunDirectories } from "./entries/run-directories.ts";
|
|
75
|
+
import { computedLoadsOf, isLoadCall, type LoadCall } from "./roots/unseen-loads.ts";
|
|
68
76
|
import {
|
|
69
77
|
computedKeyNames,
|
|
70
78
|
iterationReferences,
|
|
@@ -77,20 +85,12 @@ import { bytewise } from "./text/compare.ts";
|
|
|
77
85
|
import { globExpression } from "./text/glob.ts";
|
|
78
86
|
|
|
79
87
|
/**
|
|
80
|
-
* Every use one project's own files make of the project's own declarations
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* file's name nodes are gathered whole and handed over together.
|
|
87
|
-
*
|
|
88
|
-
* What the pass records and what it does not: a reference carries its use, its
|
|
89
|
-
* position, the declaration that encloses it and the classification of the file that
|
|
90
|
-
* made it, and every place that imports one of the project's own modules is an
|
|
91
|
-
* evaluation of that module's file. It carries no mode. Which of the references a run
|
|
92
|
-
* counts is the reading the sweep makes of this set, so the mode is one value the run
|
|
93
|
-
* decides once and this pass never sees it.
|
|
88
|
+
* Every use one project's own files make of the project's own declarations: one local walk
|
|
89
|
+
* and one batched resolution per file, never a reference query per symbol, whose answers
|
|
90
|
+
* would depend on the order the symbols are asked in. A reference carries its use, its
|
|
91
|
+
* position, the declaration enclosing it and whether a test file made it; every import of
|
|
92
|
+
* one of the project's modules is an evaluation of that module's file. It carries no mode:
|
|
93
|
+
* which references a run counts is the sweep's reading of this set.
|
|
94
94
|
*/
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -190,6 +190,12 @@ export interface ReferenceCost {
|
|
|
190
190
|
readonly shorthandLookups: number;
|
|
191
191
|
/** Per-symbol lookups that step from one alias to what it names, one per alias. */
|
|
192
192
|
readonly aliasSteps: number;
|
|
193
|
+
/**
|
|
194
|
+
* Per-node and per-symbol lookups that decide whether a load call is the platform's
|
|
195
|
+
* `require`, a construction its `URL`, or a `Worker` Node's, for a callee the pass's own
|
|
196
|
+
* chains leave open.
|
|
197
|
+
*/
|
|
198
|
+
readonly platformLookups: number;
|
|
193
199
|
/**
|
|
194
200
|
* Batched type lookups for the object patterns: per file, the patterns and the values
|
|
195
201
|
* assigned to destructuring targets, divided by the batch cap, rounded up.
|
|
@@ -225,6 +231,11 @@ export interface References {
|
|
|
225
231
|
* classification that marks the references the file makes.
|
|
226
232
|
*/
|
|
227
233
|
readonly testFilePaths: readonly string[];
|
|
234
|
+
/**
|
|
235
|
+
* Each load by a computed path a production file of the target writes whose files the
|
|
236
|
+
* analysis cannot name (confidence.26), in file then source order; none for a consumer.
|
|
237
|
+
*/
|
|
238
|
+
readonly unseenLoads: readonly Position[];
|
|
228
239
|
readonly cost: ReferenceCost;
|
|
229
240
|
}
|
|
230
241
|
|
|
@@ -252,6 +263,8 @@ export interface ReferenceOptions {
|
|
|
252
263
|
readonly consumer?: ConsumerSide;
|
|
253
264
|
/** The client's running request count, where the caller measures the client. */
|
|
254
265
|
readonly requestCount?: () => number;
|
|
266
|
+
/** The directories a thread's path and a glob's root are read against. */
|
|
267
|
+
readonly directories: RunDirectories;
|
|
255
268
|
}
|
|
256
269
|
|
|
257
270
|
/**
|
|
@@ -459,6 +472,10 @@ interface Evaluation {
|
|
|
459
472
|
/** The module specifier, whose symbol is the module evaluated. */
|
|
460
473
|
readonly node: Node;
|
|
461
474
|
readonly from: string;
|
|
475
|
+
/** The name a `require()` call is written with; {@link callsPlatform} owns which one loads. */
|
|
476
|
+
readonly require?: Identifier;
|
|
477
|
+
/** Whether the call's value is used, which reads the loaded file's export table. */
|
|
478
|
+
readonly whole?: boolean;
|
|
462
479
|
}
|
|
463
480
|
|
|
464
481
|
/**
|
|
@@ -468,11 +485,29 @@ interface Evaluation {
|
|
|
468
485
|
*/
|
|
469
486
|
interface ModuleURL {
|
|
470
487
|
readonly specifier: string;
|
|
471
|
-
/** The
|
|
472
|
-
readonly constructor:
|
|
488
|
+
/** The name the constructor is written with; {@link callsPlatform} owns which one is `URL`. */
|
|
489
|
+
readonly constructor: Identifier | undefined;
|
|
473
490
|
readonly at: Node;
|
|
474
491
|
}
|
|
475
492
|
|
|
493
|
+
/** One module URL, and the declaration whose code builds it. */
|
|
494
|
+
interface ModuleURLAt extends ModuleURL {
|
|
495
|
+
readonly from: string;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** One worker a call starts by a literal address (references-ts.2), and the declaration whose code starts it. */
|
|
499
|
+
interface WorkerSite {
|
|
500
|
+
readonly address: StringLiteralLikeNode;
|
|
501
|
+
/** The name the constructor or `navigator` is written with. */
|
|
502
|
+
readonly constructor: Identifier;
|
|
503
|
+
/** The namespace the constructor is read through, as `threads` in `threads.Worker`. */
|
|
504
|
+
readonly holder: Identifier | undefined;
|
|
505
|
+
/** Whether an options literal writes an `eval` other than `false`, which makes a thread's address code. */
|
|
506
|
+
readonly evaluated: boolean;
|
|
507
|
+
readonly at: Node;
|
|
508
|
+
readonly from: string;
|
|
509
|
+
}
|
|
510
|
+
|
|
476
511
|
/** One use of a module namespace object that may read every export of the module. */
|
|
477
512
|
interface WholeRead {
|
|
478
513
|
/** The module specifier, whose symbol is the module read. */
|
|
@@ -509,7 +544,9 @@ interface FileSites {
|
|
|
509
544
|
readonly links: readonly Link[];
|
|
510
545
|
readonly evaluations: readonly Evaluation[];
|
|
511
546
|
readonly wholes: readonly WholeRead[];
|
|
512
|
-
readonly urls: readonly
|
|
547
|
+
readonly urls: readonly ModuleURLAt[];
|
|
548
|
+
readonly workers: readonly WorkerSite[];
|
|
549
|
+
readonly loadCalls: readonly LoadCall[];
|
|
513
550
|
readonly decorated: readonly Decorated[];
|
|
514
551
|
readonly parameterWrites: readonly ParameterWrite[];
|
|
515
552
|
readonly keyed: readonly KeyedAccess[];
|
|
@@ -528,26 +565,24 @@ function isImportMetaURL(node: Node): boolean {
|
|
|
528
565
|
|
|
529
566
|
/** The module URL one node builds, where it builds one. */
|
|
530
567
|
function moduleURLOf(node: Node): ModuleURL | undefined {
|
|
531
|
-
const
|
|
568
|
+
const literal = (argument: Node | undefined): string | undefined => {
|
|
532
569
|
const at = argument === undefined ? undefined : parenthesesRemoved(argument);
|
|
533
|
-
return at
|
|
534
|
-
(isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
|
|
535
|
-
/^\.\.?\//u.test(at.text)
|
|
536
|
-
? at.text
|
|
537
|
-
: undefined;
|
|
570
|
+
return isLiteralText(at) ? at.text : undefined;
|
|
538
571
|
};
|
|
539
|
-
|
|
572
|
+
const constructor = isNewExpression(node) ? writtenName(node.expression) : undefined;
|
|
573
|
+
if (isNewExpression(node) && constructor !== undefined) {
|
|
540
574
|
const [reference, base] = node.arguments ?? [];
|
|
541
|
-
const specifier =
|
|
575
|
+
const specifier = literal(reference);
|
|
542
576
|
return specifier !== undefined && base !== undefined && isImportMetaURL(base)
|
|
543
|
-
? { specifier, constructor
|
|
577
|
+
? { specifier, constructor, at: node }
|
|
544
578
|
: undefined;
|
|
545
579
|
}
|
|
546
580
|
if (isCallExpression(node)) {
|
|
547
581
|
const callee = parenthesesRemoved(node.expression);
|
|
548
|
-
const specifier =
|
|
582
|
+
const specifier = literal(node.arguments[0]);
|
|
549
583
|
if (
|
|
550
584
|
specifier !== undefined &&
|
|
585
|
+
/^\.\.?\//u.test(specifier) &&
|
|
551
586
|
isPropertyAccessExpression(callee) &&
|
|
552
587
|
isMetaProperty(callee.expression) &&
|
|
553
588
|
callee.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
@@ -559,18 +594,125 @@ function moduleURLOf(node: Node): ModuleURL | undefined {
|
|
|
559
594
|
return undefined;
|
|
560
595
|
}
|
|
561
596
|
|
|
597
|
+
/** The constructors whose first argument addresses a worker. */
|
|
598
|
+
const WORKER_CONSTRUCTORS: ReadonlySet<string> = new Set(["Worker", "SharedWorker"]);
|
|
599
|
+
|
|
600
|
+
/** Whether an options argument is an object literal whose `eval` member is written as anything but `false`. */
|
|
601
|
+
function evaluatesCode(options: Node | undefined): boolean {
|
|
602
|
+
const at = options === undefined ? undefined : parenthesesRemoved(options);
|
|
603
|
+
return (
|
|
604
|
+
at !== undefined &&
|
|
605
|
+
isObjectLiteralExpression(at) &&
|
|
606
|
+
at.properties.some(
|
|
607
|
+
(property) =>
|
|
608
|
+
(isShorthandPropertyAssignment(property) &&
|
|
609
|
+
isIdentifier(property.name) &&
|
|
610
|
+
property.name.text === "eval") ||
|
|
611
|
+
(isPropertyAssignment(property) &&
|
|
612
|
+
(isIdentifier(property.name) || isStringLiteral(property.name)) &&
|
|
613
|
+
property.name.text === "eval" &&
|
|
614
|
+
parenthesesRemoved(property.initializer).kind !== SyntaxKind.FalseKeyword),
|
|
615
|
+
)
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/** The worker one node starts by a literal address, where it starts one. */
|
|
620
|
+
function workerOf(node: Node): Omit<WorkerSite, "from"> | undefined {
|
|
621
|
+
if (isNewExpression(node)) {
|
|
622
|
+
const constructor = writtenName(node.expression);
|
|
623
|
+
const [address, options] = node.arguments ?? [];
|
|
624
|
+
const literal = address === undefined ? undefined : parenthesesRemoved(address);
|
|
625
|
+
// A thread's constructor may be imported under another name, whose path is relative.
|
|
626
|
+
if (
|
|
627
|
+
constructor === undefined ||
|
|
628
|
+
!isLiteralText(literal) ||
|
|
629
|
+
(!WORKER_CONSTRUCTORS.has(constructor.text) && !/^\.\.?\//u.test(literal.text))
|
|
630
|
+
) {
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
const callee = parenthesesRemoved(node.expression);
|
|
634
|
+
const holder = isPropertyAccessExpression(callee)
|
|
635
|
+
? parenthesesRemoved(callee.expression)
|
|
636
|
+
: undefined;
|
|
637
|
+
return {
|
|
638
|
+
address: literal,
|
|
639
|
+
constructor,
|
|
640
|
+
holder: holder !== undefined && isIdentifier(holder) ? holder : undefined,
|
|
641
|
+
evaluated: evaluatesCode(options),
|
|
642
|
+
at: node,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
if (!isCallExpression(node)) {
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
const callee = parenthesesRemoved(node.expression);
|
|
649
|
+
const registry =
|
|
650
|
+
isPropertyAccessExpression(callee) && callee.name.text === "register"
|
|
651
|
+
? parenthesesRemoved(callee.expression)
|
|
652
|
+
: undefined;
|
|
653
|
+
const navigator =
|
|
654
|
+
registry !== undefined &&
|
|
655
|
+
isPropertyAccessExpression(registry) &&
|
|
656
|
+
registry.name.text === "serviceWorker"
|
|
657
|
+
? writtenName(registry.expression)
|
|
658
|
+
: undefined;
|
|
659
|
+
const [address] = node.arguments;
|
|
660
|
+
const literal = address === undefined ? undefined : parenthesesRemoved(address);
|
|
661
|
+
return navigator?.text === "navigator" && isLiteralText(literal)
|
|
662
|
+
? { address: literal, constructor: navigator, holder: undefined, evaluated: false, at: node }
|
|
663
|
+
: undefined;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/** The file of the target a relative specifier written in `file` names, by its source or output name. */
|
|
667
|
+
function fileNamed(
|
|
668
|
+
file: SourceFile,
|
|
669
|
+
specifier: string,
|
|
670
|
+
fileIdByName: ReadonlyMap<string, string>,
|
|
671
|
+
): string | undefined {
|
|
672
|
+
return specifierCandidates(dirnamePath(file.fileName), specifier)
|
|
673
|
+
.map((path) => fileIdByName.get(path))
|
|
674
|
+
.find((id) => id !== undefined);
|
|
675
|
+
}
|
|
676
|
+
|
|
562
677
|
/** Whether one node is a string literal or a template literal with no substitution. */
|
|
563
|
-
function isLiteralText(node: Node | undefined): node is
|
|
678
|
+
function isLiteralText(node: Node | undefined): node is StringLiteralLikeNode {
|
|
564
679
|
return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
|
|
565
680
|
}
|
|
566
681
|
|
|
567
682
|
/**
|
|
568
683
|
* The module specifier of the module one node evaluates, if it evaluates one: an import
|
|
569
684
|
* declaration, an import-equals declaration of an external module, an export
|
|
570
|
-
* declaration that names a module,
|
|
571
|
-
*
|
|
572
|
-
*
|
|
685
|
+
* declaration that names a module, an `import()` call whose argument is literal text, and
|
|
686
|
+
* a call that may be a `require()` of literal text: one written as the name `require`, or
|
|
687
|
+
* one of a relative specifier through another name. A type-only form evaluates nothing.
|
|
688
|
+
* An `import()` of a computed argument names no module this pass can know.
|
|
573
689
|
*/
|
|
690
|
+
function evaluationOf(node: Node): Omit<Evaluation, "from"> | undefined {
|
|
691
|
+
if (isCallExpression(node) && node.expression.kind !== SyntaxKind.ImportKeyword) {
|
|
692
|
+
const [argument] = node.arguments;
|
|
693
|
+
const require = writtenName(node.expression);
|
|
694
|
+
if (
|
|
695
|
+
require === undefined ||
|
|
696
|
+
!isLiteralText(argument) ||
|
|
697
|
+
(require.text !== "require" && !/^\.\.?\//u.test(argument.text))
|
|
698
|
+
) {
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
return { node: argument, require, whole: !discarded(node) };
|
|
702
|
+
}
|
|
703
|
+
const specifier = evaluatedBy(node);
|
|
704
|
+
return specifier === undefined ? undefined : { node: specifier };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/** Whether an expression's value is thrown away: it is a statement of its own, or `void`'s operand. */
|
|
708
|
+
function discarded(expression: Node): boolean {
|
|
709
|
+
let at = expression.parent;
|
|
710
|
+
while (isParenthesizedExpression(at)) {
|
|
711
|
+
at = at.parent;
|
|
712
|
+
}
|
|
713
|
+
return isExpressionStatement(at) || isVoidExpression(at);
|
|
714
|
+
}
|
|
715
|
+
|
|
574
716
|
function evaluatedBy(node: Node): Node | undefined {
|
|
575
717
|
if (isImportDeclaration(node)) {
|
|
576
718
|
return node.importClause?.phaseModifier === SyntaxKind.TypeKeyword
|
|
@@ -698,7 +840,9 @@ function sitesOf(
|
|
|
698
840
|
const links: Link[] = [];
|
|
699
841
|
const evaluations: Evaluation[] = [];
|
|
700
842
|
const wholes: WholeRead[] = [];
|
|
701
|
-
const urls:
|
|
843
|
+
const urls: ModuleURLAt[] = [];
|
|
844
|
+
const workers: WorkerSite[] = [];
|
|
845
|
+
const loadCalls: LoadCall[] = [];
|
|
702
846
|
const decorated: Decorated[] = [];
|
|
703
847
|
const parameterWrites: ParameterWrite[] = [];
|
|
704
848
|
const namespaces = namespaceUses(file);
|
|
@@ -775,13 +919,20 @@ function sitesOf(
|
|
|
775
919
|
reads.push({ ...read, from: enclosing });
|
|
776
920
|
}
|
|
777
921
|
links.push(...linksOf(file, node, declarations, enclosing));
|
|
778
|
-
const evaluated =
|
|
922
|
+
const evaluated = evaluationOf(node);
|
|
779
923
|
if (evaluated !== undefined) {
|
|
780
|
-
evaluations.push({
|
|
924
|
+
evaluations.push({ ...evaluated, from: enclosing });
|
|
781
925
|
}
|
|
782
926
|
const url = moduleURLOf(node);
|
|
783
927
|
if (url !== undefined) {
|
|
784
|
-
urls.push(url);
|
|
928
|
+
urls.push({ ...url, from: enclosing });
|
|
929
|
+
}
|
|
930
|
+
const worker = workerOf(node);
|
|
931
|
+
if (worker !== undefined) {
|
|
932
|
+
workers.push({ ...worker, from: enclosing });
|
|
933
|
+
}
|
|
934
|
+
if (isLoadCall(node)) {
|
|
935
|
+
loadCalls.push({ call: node, from: enclosing });
|
|
785
936
|
}
|
|
786
937
|
const held = declarations.get(nodeKey(file, node));
|
|
787
938
|
const outer = enclosing;
|
|
@@ -815,6 +966,8 @@ function sitesOf(
|
|
|
815
966
|
evaluations,
|
|
816
967
|
wholes,
|
|
817
968
|
urls,
|
|
969
|
+
workers,
|
|
970
|
+
loadCalls,
|
|
818
971
|
decorated,
|
|
819
972
|
parameterWrites,
|
|
820
973
|
keyed,
|
|
@@ -1005,20 +1158,12 @@ function aloneIn(
|
|
|
1005
1158
|
}
|
|
1006
1159
|
|
|
1007
1160
|
/**
|
|
1008
|
-
* Every reference one project's own files make to
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
* walks the consumer's files instead and renders against its root, as
|
|
1015
|
-
* {@link ReferenceOptions.consumer} states.
|
|
1016
|
-
*
|
|
1017
|
-
* The order is by the referencing position, then by the declaration named, the
|
|
1018
|
-
* declaration referencing it and the use. That is a total order because one name
|
|
1019
|
-
* references one declaration once from one declaration, while a chain of aliases reaches
|
|
1020
|
-
* several declarations at one position and a bare star re-export's specifier both reads
|
|
1021
|
-
* and evaluates the module it names.
|
|
1161
|
+
* Every reference one project's own files make to its own declarations, and the files the
|
|
1162
|
+
* test-file rules classified. `held` is the same project's inventory, where a resolved symbol
|
|
1163
|
+
* is looked up; positions render against `targetRoot`, or a consumer's root as
|
|
1164
|
+
* {@link ReferenceOptions.consumer} states. The order is by position, then the declaration
|
|
1165
|
+
* named, the referencing declaration and the use: an alias chain or a bare star re-export
|
|
1166
|
+
* names several at one position, and one name references one declaration once.
|
|
1022
1167
|
*/
|
|
1023
1168
|
export function references<Brand>(
|
|
1024
1169
|
project: ProjectView<Brand>,
|
|
@@ -1037,6 +1182,10 @@ export function references<Brand>(
|
|
|
1037
1182
|
let batched = 0;
|
|
1038
1183
|
let fileBatches = 0;
|
|
1039
1184
|
let residueFallbacks = 0;
|
|
1185
|
+
let platformLookups = 0;
|
|
1186
|
+
const asked = (): void => {
|
|
1187
|
+
platformLookups += 1;
|
|
1188
|
+
};
|
|
1040
1189
|
let shorthandLookups = 0;
|
|
1041
1190
|
|
|
1042
1191
|
const consumer = options.consumer;
|
|
@@ -1079,6 +1228,8 @@ export function references<Brand>(
|
|
|
1079
1228
|
return moduleBodies.get(moduleId) ?? [];
|
|
1080
1229
|
};
|
|
1081
1230
|
|
|
1231
|
+
let ownFiles: SourceFiles | undefined;
|
|
1232
|
+
const unseenLoads: Position[] = [];
|
|
1082
1233
|
const exportsCache = new Map<number, readonly ChainTarget[]>();
|
|
1083
1234
|
/**
|
|
1084
1235
|
* Every declaration one module exports, each re-export walked along its chain. A module
|
|
@@ -1132,7 +1283,7 @@ export function references<Brand>(
|
|
|
1132
1283
|
* live. A name a namespace import binds may be a use of any file.
|
|
1133
1284
|
*/
|
|
1134
1285
|
const named = (node: Node): readonly string[] => {
|
|
1135
|
-
const byName =
|
|
1286
|
+
const byName = held.byName;
|
|
1136
1287
|
const text = node.getText();
|
|
1137
1288
|
const imported = importedNames(node.getSourceFile()).get(text) ?? [];
|
|
1138
1289
|
return [
|
|
@@ -1161,7 +1312,7 @@ export function references<Brand>(
|
|
|
1161
1312
|
aliased.set(
|
|
1162
1313
|
id,
|
|
1163
1314
|
symbol === UNANSWERED
|
|
1164
|
-
? (
|
|
1315
|
+
? (held.byName.get(alias.name) ?? []).map((one) => ({
|
|
1165
1316
|
id: one,
|
|
1166
1317
|
stepped: true,
|
|
1167
1318
|
guessed: true,
|
|
@@ -1212,6 +1363,10 @@ export function references<Brand>(
|
|
|
1212
1363
|
const fileIdByName = new Map<string, string>(
|
|
1213
1364
|
walked.filter((one) => fileIds.has(one.fileId)).map((one) => [one.file.fileName, one.fileId]),
|
|
1214
1365
|
);
|
|
1366
|
+
const loadable = walked
|
|
1367
|
+
.filter((one) => fileIds.has(one.fileId))
|
|
1368
|
+
.map(({ file, fileId }) => ({ file, fileId }));
|
|
1369
|
+
const loadableById = new Map(loadable.map((one) => [one.fileId, one.file]));
|
|
1215
1370
|
const contextual = contextualUses(
|
|
1216
1371
|
project,
|
|
1217
1372
|
held,
|
|
@@ -1311,11 +1466,16 @@ export function references<Brand>(
|
|
|
1311
1466
|
evaluations,
|
|
1312
1467
|
wholes,
|
|
1313
1468
|
urls,
|
|
1469
|
+
workers,
|
|
1470
|
+
loadCalls,
|
|
1314
1471
|
decorated,
|
|
1315
1472
|
parameterWrites,
|
|
1316
1473
|
keyed,
|
|
1317
1474
|
} of walked) {
|
|
1318
1475
|
const test = testFile.get(file) === true;
|
|
1476
|
+
const loads = computedLoadsOf(file, loadCalls, loadable, (fileName) =>
|
|
1477
|
+
options.directories.bundlerRootOf(fileName),
|
|
1478
|
+
);
|
|
1319
1479
|
// A component file's markup may use any binding its blocks declare at the top level.
|
|
1320
1480
|
const markup = isComponentFile(file);
|
|
1321
1481
|
const bindings = markup ? importBindings(file) : [];
|
|
@@ -1355,10 +1515,24 @@ export function references<Brand>(
|
|
|
1355
1515
|
...new Set([
|
|
1356
1516
|
...uses.filter((site) => site.shorthand === undefined).map((site) => site.node),
|
|
1357
1517
|
...links.map((link) => link.node),
|
|
1358
|
-
...evaluations.
|
|
1518
|
+
...evaluations.flatMap((evaluation) =>
|
|
1519
|
+
evaluation.require === undefined
|
|
1520
|
+
? [evaluation.node]
|
|
1521
|
+
: [
|
|
1522
|
+
evaluation.require,
|
|
1523
|
+
...(evaluation.require.text === "require" ? [evaluation.node] : []),
|
|
1524
|
+
],
|
|
1525
|
+
),
|
|
1359
1526
|
...wholes.map((whole) => whole.specifier),
|
|
1360
1527
|
...urls.flatMap((url) => url.constructor ?? []),
|
|
1528
|
+
...workers.flatMap((worker) =>
|
|
1529
|
+
worker.holder === undefined ? [worker.constructor] : [worker.constructor, worker.holder],
|
|
1530
|
+
),
|
|
1361
1531
|
...bindings.map((binding) => binding.node),
|
|
1532
|
+
...loads.flatMap((load) => [
|
|
1533
|
+
...(load.require === undefined ? [] : [load.require]),
|
|
1534
|
+
...(load.asset ? [] : load.files.map((loaded) => loaded.file)),
|
|
1535
|
+
]),
|
|
1362
1536
|
]),
|
|
1363
1537
|
];
|
|
1364
1538
|
const answered = new Map<Node, Answer<TSSymbol>>();
|
|
@@ -1400,7 +1574,29 @@ export function references<Brand>(
|
|
|
1400
1574
|
}
|
|
1401
1575
|
}
|
|
1402
1576
|
|
|
1403
|
-
|
|
1577
|
+
const loading = evaluations.filter(
|
|
1578
|
+
(one) =>
|
|
1579
|
+
one.require === undefined ||
|
|
1580
|
+
callsPlatform(project, "require", one.require, answered.get(one.require), asked),
|
|
1581
|
+
);
|
|
1582
|
+
// A script declares no module, so the checker names nothing for a specifier of one;
|
|
1583
|
+
// the compiler's module resolution still names its file.
|
|
1584
|
+
const unnamed = loading.flatMap(({ node }) =>
|
|
1585
|
+
answered.get(node) === undefined &&
|
|
1586
|
+
isStringLiteralLikeNode(node) &&
|
|
1587
|
+
queriedFile(file, node, fileIdByName) === undefined
|
|
1588
|
+
? [node]
|
|
1589
|
+
: [],
|
|
1590
|
+
);
|
|
1591
|
+
const resolved = project.resolvedFileNames(unnamed);
|
|
1592
|
+
const scripts = new Map<Node, string | undefined>(
|
|
1593
|
+
unnamed.map((node, at) => {
|
|
1594
|
+
const name = resolved[at];
|
|
1595
|
+
return [node, name === undefined ? undefined : fileIdByName.get(name)];
|
|
1596
|
+
}),
|
|
1597
|
+
);
|
|
1598
|
+
const wholeReads: { readonly evaluation: Evaluation; readonly fileId: string }[] = [];
|
|
1599
|
+
for (const evaluation of loading) {
|
|
1404
1600
|
const queried = queriedFile(file, evaluation.node, fileIdByName);
|
|
1405
1601
|
if (queried !== undefined) {
|
|
1406
1602
|
found.push({
|
|
@@ -1413,6 +1609,29 @@ export function references<Brand>(
|
|
|
1413
1609
|
});
|
|
1414
1610
|
}
|
|
1415
1611
|
const module = answered.get(evaluation.node);
|
|
1612
|
+
// Node's require resolves a relative specifier the program records no resolution for,
|
|
1613
|
+
// as in a TypeScript file, by its own extension search.
|
|
1614
|
+
const script =
|
|
1615
|
+
scripts.get(evaluation.node) ??
|
|
1616
|
+
(evaluation.require !== undefined &&
|
|
1617
|
+
module === undefined &&
|
|
1618
|
+
isLiteralText(evaluation.node) &&
|
|
1619
|
+
/^\.\.?\//u.test(evaluation.node.text)
|
|
1620
|
+
? fileNamed(file, evaluation.node.text, fileIdByName)
|
|
1621
|
+
: undefined);
|
|
1622
|
+
if (script !== undefined && evaluation.whole === true && module === undefined) {
|
|
1623
|
+
wholeReads.push({ evaluation, fileId: script });
|
|
1624
|
+
}
|
|
1625
|
+
if (script !== undefined) {
|
|
1626
|
+
found.push({
|
|
1627
|
+
from: evaluation.from,
|
|
1628
|
+
to: script,
|
|
1629
|
+
position: renderPosition(file, root, evaluation.node.getStart()),
|
|
1630
|
+
use: "evaluation",
|
|
1631
|
+
resolution: "syntax",
|
|
1632
|
+
test,
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1416
1635
|
if (module === undefined) {
|
|
1417
1636
|
continue;
|
|
1418
1637
|
}
|
|
@@ -1433,23 +1652,43 @@ export function references<Brand>(
|
|
|
1433
1652
|
}
|
|
1434
1653
|
}
|
|
1435
1654
|
|
|
1655
|
+
// A required file the checker names no module for is read whole where the call's value
|
|
1656
|
+
// is used: the value is the file's exports object, which this pass does not follow.
|
|
1657
|
+
const required = wholeReads.flatMap((one) => {
|
|
1658
|
+
const loaded = loadableById.get(one.fileId);
|
|
1659
|
+
return loaded === undefined ? [] : [{ ...one, loaded }];
|
|
1660
|
+
});
|
|
1661
|
+
const modules = project.symbolsAt(
|
|
1662
|
+
required.map((one) => project.handle(one.loaded)),
|
|
1663
|
+
cap,
|
|
1664
|
+
);
|
|
1665
|
+
required.forEach(({ evaluation }, at) => {
|
|
1666
|
+
const module = modules[at];
|
|
1667
|
+
const position = renderPosition(file, root, evaluation.node.getStart());
|
|
1668
|
+
for (const target of module === undefined ? [] : exportsRead(module)) {
|
|
1669
|
+
found.push({
|
|
1670
|
+
from: evaluation.from,
|
|
1671
|
+
to: target.id,
|
|
1672
|
+
position,
|
|
1673
|
+
use: "read",
|
|
1674
|
+
resolution: target.guessed ? "by-name" : "batch",
|
|
1675
|
+
test,
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
|
|
1436
1680
|
for (const url of urls) {
|
|
1437
|
-
const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
|
|
1438
|
-
// An augmentation of the global `URL` merges into it, which keeps the
|
|
1439
|
-
// library's declaration; a `URL` the target declares has no other.
|
|
1440
1681
|
if (
|
|
1441
|
-
|
|
1442
|
-
constructor.
|
|
1443
|
-
constructor.declarations.every((handle) => ownPaths.has(handle.path))
|
|
1682
|
+
url.constructor !== undefined &&
|
|
1683
|
+
!callsPlatform(project, "URL", url.constructor, answered.get(url.constructor), asked)
|
|
1444
1684
|
) {
|
|
1445
1685
|
continue;
|
|
1446
1686
|
}
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
.find((id) => id !== undefined);
|
|
1687
|
+
const path = relativeURLPath(dirnamePath(file.fileName), url.specifier);
|
|
1688
|
+
const loaded = path === undefined ? undefined : fileNamed(file, path, fileIdByName);
|
|
1450
1689
|
if (loaded !== undefined) {
|
|
1451
1690
|
found.push({
|
|
1452
|
-
from:
|
|
1691
|
+
from: url.from,
|
|
1453
1692
|
to: loaded,
|
|
1454
1693
|
position: renderPosition(file, root, url.at.getStart()),
|
|
1455
1694
|
use: "evaluation",
|
|
@@ -1459,6 +1698,75 @@ export function references<Brand>(
|
|
|
1459
1698
|
}
|
|
1460
1699
|
}
|
|
1461
1700
|
|
|
1701
|
+
for (const worker of workers) {
|
|
1702
|
+
const symbol = answered.get(worker.constructor);
|
|
1703
|
+
const text = worker.address.text;
|
|
1704
|
+
let path: string | undefined;
|
|
1705
|
+
if (startsThread(project, symbol, worker.holder && answered.get(worker.holder), asked)) {
|
|
1706
|
+
path =
|
|
1707
|
+
worker.evaluated || !/^\.\.?\//u.test(text)
|
|
1708
|
+
? undefined
|
|
1709
|
+
: resolvePath(options.directories.workingDirectoryOf(file.fileName), text);
|
|
1710
|
+
} else if (
|
|
1711
|
+
(WORKER_CONSTRUCTORS.has(worker.constructor.text) ||
|
|
1712
|
+
worker.constructor.text === "navigator") &&
|
|
1713
|
+
isPlatformGlobal(project, symbol)
|
|
1714
|
+
) {
|
|
1715
|
+
path = relativeURLPath(dirnamePath(file.fileName), text);
|
|
1716
|
+
}
|
|
1717
|
+
ownFiles ??= sourceFilesOf(project, targetRoot);
|
|
1718
|
+
for (const started of path === undefined ? [] : ownFiles.named(path)) {
|
|
1719
|
+
const to = declaredAt(started);
|
|
1720
|
+
if (to !== undefined && fileIds.has(to)) {
|
|
1721
|
+
found.push({
|
|
1722
|
+
from: worker.from,
|
|
1723
|
+
to,
|
|
1724
|
+
position: renderPosition(file, root, worker.at.getStart()),
|
|
1725
|
+
use: "evaluation",
|
|
1726
|
+
resolution: "syntax",
|
|
1727
|
+
test,
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
// A computed load imports each file it loads where the call is written, reading what
|
|
1734
|
+
// the file exports unless it reads the file as text or a URL; callsPlatform owns which
|
|
1735
|
+
// call is a `require`.
|
|
1736
|
+
for (const load of loads) {
|
|
1737
|
+
if (
|
|
1738
|
+
load.require !== undefined &&
|
|
1739
|
+
!callsPlatform(project, "require", load.require, answered.get(load.require), asked)
|
|
1740
|
+
) {
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
const position = renderPosition(file, root, load.call.getStart());
|
|
1744
|
+
if (!load.resolved && !test && consumer === undefined && !file.isDeclarationFile) {
|
|
1745
|
+
unseenLoads.push(position);
|
|
1746
|
+
}
|
|
1747
|
+
for (const loaded of load.files) {
|
|
1748
|
+
found.push({
|
|
1749
|
+
from: load.eager ? fileId : load.from,
|
|
1750
|
+
to: loaded.fileId,
|
|
1751
|
+
position,
|
|
1752
|
+
use: "evaluation",
|
|
1753
|
+
resolution: "syntax",
|
|
1754
|
+
test,
|
|
1755
|
+
});
|
|
1756
|
+
const module = load.asset ? undefined : answered.get(loaded.file);
|
|
1757
|
+
for (const target of module === undefined ? [] : exportsRead(module)) {
|
|
1758
|
+
found.push({
|
|
1759
|
+
from: load.from,
|
|
1760
|
+
to: target.id,
|
|
1761
|
+
position,
|
|
1762
|
+
use: "read",
|
|
1763
|
+
resolution: target.guessed ? "by-name" : "batch",
|
|
1764
|
+
test,
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1462
1770
|
for (const whole of wholes) {
|
|
1463
1771
|
const module = answered.get(whole.specifier);
|
|
1464
1772
|
if (module === undefined) {
|
|
@@ -1529,7 +1837,7 @@ export function references<Brand>(
|
|
|
1529
1837
|
const position = renderPosition(file, root, read.key.getStart());
|
|
1530
1838
|
const targets =
|
|
1531
1839
|
symbol === UNANSWERED
|
|
1532
|
-
? (
|
|
1840
|
+
? (held.byName.get(read.name) ?? []).map((id) => ({
|
|
1533
1841
|
id,
|
|
1534
1842
|
stepped: false,
|
|
1535
1843
|
guessed: true,
|
|
@@ -1673,12 +1981,14 @@ export function references<Brand>(
|
|
|
1673
1981
|
? found
|
|
1674
1982
|
: found.map((reference) => ({ ...reference, consumer: consumer.id })),
|
|
1675
1983
|
testFilePaths: tests.sort(bytewise),
|
|
1984
|
+
unseenLoads,
|
|
1676
1985
|
cost: {
|
|
1677
1986
|
batched,
|
|
1678
1987
|
fileBatches,
|
|
1679
1988
|
residueFallbacks,
|
|
1680
1989
|
shorthandLookups,
|
|
1681
1990
|
aliasSteps: chains.steps,
|
|
1991
|
+
platformLookups,
|
|
1682
1992
|
patternBatches: destructured.cost.patternBatches,
|
|
1683
1993
|
patternLookups: destructured.cost.patternLookups,
|
|
1684
1994
|
contextualBatches: contextual.batches,
|