monkeypatchlist 0.0.0-stage → 0.1.0
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/LICENSE +202 -0
- package/NOTICE +61 -0
- package/README.md +183 -2
- package/catalog/catalog.json +552211 -0
- package/catalog/catalog.schema.json +787 -0
- package/catalog/changelog.json +3 -0
- package/dist/bin/monkeypatchlist.d.ts +2 -0
- package/dist/bin/monkeypatchlist.js +3 -0
- package/dist/catalog.d.ts +561 -0
- package/dist/catalog.js +58 -0
- package/dist/cli.d.ts +84 -0
- package/dist/cli.js +470 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lockfile-input.d.ts +9 -0
- package/dist/lockfile-input.js +91 -0
- package/dist/packages/analysis/binding.d.ts +2 -0
- package/dist/packages/analysis/binding.js +5 -0
- package/dist/packages/analysis/index.d.ts +10 -0
- package/dist/packages/analysis/index.js +13 -0
- package/dist/packages/analysis/job.d.ts +33 -0
- package/dist/packages/analysis/job.js +85 -0
- package/dist/packages/analysis/lib/absence-tags.d.ts +34 -0
- package/dist/packages/analysis/lib/absence-tags.js +340 -0
- package/dist/packages/analysis/lib/ast.d.ts +28 -0
- package/dist/packages/analysis/lib/ast.js +64 -0
- package/dist/packages/analysis/lib/binding.d.ts +36 -0
- package/dist/packages/analysis/lib/binding.js +66 -0
- package/dist/packages/analysis/lib/budget.d.ts +80 -0
- package/dist/packages/analysis/lib/budget.js +31 -0
- package/dist/packages/analysis/lib/catch-arms.d.ts +19 -0
- package/dist/packages/analysis/lib/catch-arms.js +351 -0
- package/dist/packages/analysis/lib/chunk-loads.d.ts +160 -0
- package/dist/packages/analysis/lib/chunk-loads.js +882 -0
- package/dist/packages/analysis/lib/chunk-registry.d.ts +45 -0
- package/dist/packages/analysis/lib/chunk-registry.js +334 -0
- package/dist/packages/analysis/lib/code-routes.d.ts +52 -0
- package/dist/packages/analysis/lib/code-routes.js +921 -0
- package/dist/packages/analysis/lib/collisions.d.ts +22 -0
- package/dist/packages/analysis/lib/collisions.js +103 -0
- package/dist/packages/analysis/lib/definitions.d.ts +76 -0
- package/dist/packages/analysis/lib/definitions.js +782 -0
- package/dist/packages/analysis/lib/early-exits.d.ts +39 -0
- package/dist/packages/analysis/lib/early-exits.js +161 -0
- package/dist/packages/analysis/lib/effect.d.ts +138 -0
- package/dist/packages/analysis/lib/effect.js +800 -0
- package/dist/packages/analysis/lib/global-object.d.ts +581 -0
- package/dist/packages/analysis/lib/global-object.js +8240 -0
- package/dist/packages/analysis/lib/keys.d.ts +33 -0
- package/dist/packages/analysis/lib/keys.js +82 -0
- package/dist/packages/analysis/lib/line-starts.d.ts +41 -0
- package/dist/packages/analysis/lib/line-starts.js +79 -0
- package/dist/packages/analysis/lib/link-group.d.ts +51 -0
- package/dist/packages/analysis/lib/link-group.js +487 -0
- package/dist/packages/analysis/lib/module-links.d.ts +117 -0
- package/dist/packages/analysis/lib/module-links.js +1060 -0
- package/dist/packages/analysis/lib/nested-code.d.ts +38 -0
- package/dist/packages/analysis/lib/nested-code.js +200 -0
- package/dist/packages/analysis/lib/numeric.d.ts +16 -0
- package/dist/packages/analysis/lib/numeric.js +216 -0
- package/dist/packages/analysis/lib/once-flags.d.ts +12 -0
- package/dist/packages/analysis/lib/once-flags.js +190 -0
- package/dist/packages/analysis/lib/parameter-arguments.d.ts +12 -0
- package/dist/packages/analysis/lib/parameter-arguments.js +34 -0
- package/dist/packages/analysis/lib/parse.d.ts +10 -0
- package/dist/packages/analysis/lib/parse.js +39 -0
- package/dist/packages/analysis/lib/partial.d.ts +60 -0
- package/dist/packages/analysis/lib/partial.js +518 -0
- package/dist/packages/analysis/lib/print.d.ts +3 -0
- package/dist/packages/analysis/lib/print.js +150 -0
- package/dist/packages/analysis/lib/reachability.d.ts +137 -0
- package/dist/packages/analysis/lib/reachability.js +1295 -0
- package/dist/packages/analysis/lib/resolver.d.ts +13 -0
- package/dist/packages/analysis/lib/resolver.js +2886 -0
- package/dist/packages/analysis/lib/restores.d.ts +42 -0
- package/dist/packages/analysis/lib/restores.js +482 -0
- package/dist/packages/analysis/lib/rewritten-deletes.d.ts +6 -0
- package/dist/packages/analysis/lib/rewritten-deletes.js +142 -0
- package/dist/packages/analysis/lib/scope.d.ts +110 -0
- package/dist/packages/analysis/lib/scope.js +443 -0
- package/dist/packages/analysis/lib/script-loads.d.ts +56 -0
- package/dist/packages/analysis/lib/script-loads.js +359 -0
- package/dist/packages/analysis/lib/summaries.d.ts +177 -0
- package/dist/packages/analysis/lib/summaries.js +498 -0
- package/dist/packages/analysis/lib/types.d.ts +632 -0
- package/dist/packages/analysis/lib/types.js +128 -0
- package/dist/packages/analysis/lib/wrap.d.ts +34 -0
- package/dist/packages/analysis/lib/wrap.js +407 -0
- package/dist/packages/analysis/lib/writes.d.ts +358 -0
- package/dist/packages/analysis/lib/writes.js +1612 -0
- package/dist/packages/analysis/visit-order.d.ts +3 -0
- package/dist/packages/analysis/visit-order.js +23 -0
- package/dist/packages/attribution/index.d.ts +233 -0
- package/dist/packages/attribution/index.js +444 -0
- package/dist/packages/attribution/lib/containing-package.d.ts +76 -0
- package/dist/packages/attribution/lib/containing-package.js +304 -0
- package/dist/packages/attribution/lib/first-party.d.ts +37 -0
- package/dist/packages/attribution/lib/first-party.js +153 -0
- package/dist/packages/attribution/lib/lockfile-graph.d.ts +99 -0
- package/dist/packages/attribution/lib/lockfile-graph.js +158 -0
- package/dist/packages/attribution/lib/lockfile.d.ts +33 -0
- package/dist/packages/attribution/lib/lockfile.js +330 -0
- package/dist/packages/attribution/lib/path-rules.d.ts +29 -0
- package/dist/packages/attribution/lib/path-rules.js +84 -0
- package/dist/packages/attribution/lib/pnpm.d.ts +4 -0
- package/dist/packages/attribution/lib/pnpm.js +133 -0
- package/dist/packages/attribution/lib/remote-url.d.ts +7 -0
- package/dist/packages/attribution/lib/remote-url.js +24 -0
- package/dist/packages/attribution/lib/source-map.d.ts +51 -0
- package/dist/packages/attribution/lib/source-map.js +125 -0
- package/dist/packages/attribution/lib/yaml.d.ts +9 -0
- package/dist/packages/attribution/lib/yaml.js +277 -0
- package/dist/packages/attribution/lib/yarn.d.ts +7 -0
- package/dist/packages/attribution/lib/yarn.js +227 -0
- package/dist/packages/baseline/index.d.ts +45 -0
- package/dist/packages/baseline/index.js +41 -0
- package/dist/packages/baseline/lib/error.d.ts +15 -0
- package/dist/packages/baseline/lib/error.js +25 -0
- package/dist/packages/baseline/lib/format.d.ts +49 -0
- package/dist/packages/baseline/lib/format.js +22 -0
- package/dist/packages/baseline/lib/migrations.d.ts +19 -0
- package/dist/packages/baseline/lib/migrations.js +7 -0
- package/dist/packages/baseline/lib/previous.d.ts +23 -0
- package/dist/packages/baseline/lib/previous.js +64 -0
- package/dist/packages/baseline/lib/read.d.ts +39 -0
- package/dist/packages/baseline/lib/read.js +219 -0
- package/dist/packages/baseline/lib/report.d.ts +25 -0
- package/dist/packages/baseline/lib/report.js +104 -0
- package/dist/packages/baseline/lib/skew.d.ts +51 -0
- package/dist/packages/baseline/lib/skew.js +75 -0
- package/dist/packages/compare/index.d.ts +120 -0
- package/dist/packages/compare/index.js +274 -0
- package/dist/packages/compare/lib/catalog-notes.d.ts +73 -0
- package/dist/packages/compare/lib/catalog-notes.js +224 -0
- package/dist/packages/completeness/index.d.ts +246 -0
- package/dist/packages/completeness/index.js +249 -0
- package/dist/packages/confidence/index.d.ts +54 -0
- package/dist/packages/confidence/index.js +71 -0
- package/dist/packages/config/index.d.ts +107 -0
- package/dist/packages/config/index.js +95 -0
- package/dist/packages/config/lib/command-line.d.ts +23 -0
- package/dist/packages/config/lib/command-line.js +239 -0
- package/dist/packages/config/lib/config-file.d.ts +11 -0
- package/dist/packages/config/lib/config-file.js +119 -0
- package/dist/packages/config/lib/scan-config-id.d.ts +28 -0
- package/dist/packages/config/lib/scan-config-id.js +54 -0
- package/dist/packages/config/lib/schema.d.ts +2 -0
- package/dist/packages/config/lib/schema.js +31 -0
- package/dist/packages/config/lib/types.d.ts +64 -0
- package/dist/packages/config/lib/types.js +6 -0
- package/dist/packages/config/lib/urls.d.ts +6 -0
- package/dist/packages/config/lib/urls.js +9 -0
- package/dist/packages/discovery/index.d.ts +277 -0
- package/dist/packages/discovery/index.js +1950 -0
- package/dist/packages/discovery/lib/import-map.d.ts +68 -0
- package/dist/packages/discovery/lib/import-map.js +180 -0
- package/dist/packages/discovery/lib/requests.d.ts +58 -0
- package/dist/packages/discovery/lib/requests.js +591 -0
- package/dist/packages/discovery/lib/scopes.d.ts +6 -0
- package/dist/packages/discovery/lib/scopes.js +286 -0
- package/dist/packages/discovery/lib/source-maps.d.ts +56 -0
- package/dist/packages/discovery/lib/source-maps.js +209 -0
- package/dist/packages/discovery/lib/swept-code.d.ts +27 -0
- package/dist/packages/discovery/lib/swept-code.js +107 -0
- package/dist/packages/discovery/lib/syntax.d.ts +17 -0
- package/dist/packages/discovery/lib/syntax.js +39 -0
- package/dist/packages/findings/index.d.ts +106 -0
- package/dist/packages/findings/index.js +254 -0
- package/dist/packages/findings/lib/scope.d.ts +38 -0
- package/dist/packages/findings/lib/scope.js +110 -0
- package/dist/packages/parallel/index.d.ts +2 -0
- package/dist/packages/parallel/index.js +5 -0
- package/dist/packages/parallel/lib/protocol.d.ts +15 -0
- package/dist/packages/parallel/lib/protocol.js +2 -0
- package/dist/packages/parallel/lib/runner.d.ts +36 -0
- package/dist/packages/parallel/lib/runner.js +96 -0
- package/dist/packages/parallel/lib/schedule.d.ts +16 -0
- package/dist/packages/parallel/lib/schedule.js +52 -0
- package/dist/packages/parallel/lib/worker.d.ts +1 -0
- package/dist/packages/parallel/lib/worker.js +18 -0
- package/dist/packages/policy/index.d.ts +36 -0
- package/dist/packages/policy/index.js +22 -0
- package/dist/packages/report/explain.d.ts +26 -0
- package/dist/packages/report/explain.js +99 -0
- package/dist/packages/report/index.d.ts +210 -0
- package/dist/packages/report/index.js +388 -0
- package/dist/packages/report/terminal.d.ts +11 -0
- package/dist/packages/report/terminal.js +331 -0
- package/dist/packages/source-store/fixture.d.ts +22 -0
- package/dist/packages/source-store/fixture.js +58 -0
- package/dist/packages/source-store/index.d.ts +278 -0
- package/dist/packages/source-store/index.js +322 -0
- package/dist/packages/source-store/lib/addresses.d.ts +6 -0
- package/dist/packages/source-store/lib/addresses.js +91 -0
- package/dist/packages/source-store/lib/connection.d.ts +15 -0
- package/dist/packages/source-store/lib/connection.js +135 -0
- package/dist/packages/source-store/lib/content-types.d.ts +16 -0
- package/dist/packages/source-store/lib/content-types.js +75 -0
- package/dist/packages/source-store/lib/data-urls.d.ts +10 -0
- package/dist/packages/source-store/lib/data-urls.js +85 -0
- package/dist/packages/source-store/lib/deadline.d.ts +18 -0
- package/dist/packages/source-store/lib/deadline.js +44 -0
- package/dist/packages/source-store/lib/exchange.d.ts +29 -0
- package/dist/packages/source-store/lib/exchange.js +112 -0
- package/dist/packages/source-store/lib/integrity.d.ts +19 -0
- package/dist/packages/source-store/lib/integrity.js +86 -0
- package/dist/packages/source-store/lib/outcomes.d.ts +12 -0
- package/dist/packages/source-store/lib/outcomes.js +22 -0
- package/dist/packages/source-store/lib/proxy.d.ts +11 -0
- package/dist/packages/source-store/lib/proxy.js +53 -0
- package/dist/packages/source-store/lib/session.d.ts +12 -0
- package/dist/packages/source-store/lib/session.js +139 -0
- package/dist/packages/source-store/lib/urls.d.ts +6 -0
- package/dist/packages/source-store/lib/urls.js +15 -0
- package/dist/packages/source-store/resolve.d.ts +59 -0
- package/dist/packages/source-store/resolve.js +45 -0
- package/dist/scan.d.ts +65 -0
- package/dist/scan.js +592 -0
- package/package.json +95 -4
- package/schema/config.schema.json +133 -0
- package/schema/report.schema.json +1905 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type AstNode } from './ast.ts';
|
|
2
|
+
/** A push into a registry: its call, the chunk ids it pushes, and its factories by module id. */
|
|
3
|
+
export interface RegistryPush {
|
|
4
|
+
registry: string;
|
|
5
|
+
call: AstNode;
|
|
6
|
+
chunks: string[];
|
|
7
|
+
/** The factories it pushes, each with its module id, in document order. */
|
|
8
|
+
factories: {
|
|
9
|
+
id: string;
|
|
10
|
+
factory: AstNode;
|
|
11
|
+
}[];
|
|
12
|
+
/** The callback it pushes, which the loader calls with its require. */
|
|
13
|
+
callback?: AstNode;
|
|
14
|
+
}
|
|
15
|
+
/** A loader's read of a registry: the registry's name, the read, and the ids its id table maps ('any' for every id). */
|
|
16
|
+
export interface RegistryLoad {
|
|
17
|
+
registry: string;
|
|
18
|
+
read: AstNode;
|
|
19
|
+
ids: ReadonlySet<string> | 'any';
|
|
20
|
+
}
|
|
21
|
+
export interface Registries {
|
|
22
|
+
pushes: RegistryPush[];
|
|
23
|
+
loads: RegistryLoad[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The registry an ESM chunk's export of its module table counts as a push into (above). A global's name can't be it: a
|
|
27
|
+
* registry read never names it.
|
|
28
|
+
*/
|
|
29
|
+
export declare const esmRegistry = "\0webpack-esm";
|
|
30
|
+
/** The chunk registries a program pushes into and reads (above). */
|
|
31
|
+
export declare function registriesOf(program: AstNode): Registries;
|
|
32
|
+
/**
|
|
33
|
+
* The chunk registries an artifact's code pushes into and reads, as data: each push's registry and chunk ids, and each
|
|
34
|
+
* loader's registry and the ids its id table maps (`null` for every id). None when the code doesn't parse.
|
|
35
|
+
*/
|
|
36
|
+
export declare function chunkRegistries(text: string, sourceType: 'script' | 'module'): {
|
|
37
|
+
pushes: {
|
|
38
|
+
registry: string;
|
|
39
|
+
chunks: string[];
|
|
40
|
+
}[];
|
|
41
|
+
loads: {
|
|
42
|
+
registry: string;
|
|
43
|
+
ids: string[] | null;
|
|
44
|
+
}[];
|
|
45
|
+
};
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
// Chunk registries (§6 Module-linking idiom 6, Globals are not links): the global array a chunk loader's chunks push
|
|
2
|
+
// into, recognized from structure alone.
|
|
3
|
+
//
|
|
4
|
+
// - A **registry** is a global object's member with a constant name, read as `G.R = G.R || []` or `G.R ||= []` (and
|
|
5
|
+
// `G["R"]`), where `G` is `self`, `window`, `globalThis` or `this`.
|
|
6
|
+
// - A **push** is `registry.push([[ids…], modules, callback?])`, where `ids` are constant chunk ids and `modules` an
|
|
7
|
+
// object literal of factories keyed by constant module id, or an array literal of factories indexed by it.
|
|
8
|
+
// - A **loader** reads a registry into a variable whose `push` it overrides (`l.push = …`), so that it reads every push.
|
|
9
|
+
// Its **id table** is the ids its URL function maps: a function of one parameter that builds a script URL by string
|
|
10
|
+
// concatenation (a string literal naming a `.js`, `.mjs` or `.cjs` file), such as `n.u = e => ({581: "recorder"}[e] +
|
|
11
|
+
// "." + {581: "27e3"}[e] + ".js")`. Each object literal it indexes with the parameter maps only its constant keys, so
|
|
12
|
+
// the function maps the ids every such literal has. A literal with a fallback (`{…}[e] || e`) and the parameter itself
|
|
13
|
+
// (`e => e + ".js"`) map every id. When no function builds a script URL, every concatenating function counts, and one
|
|
14
|
+
// with a table outweighs one that maps every id: an unrelated `e => "\\" + e` never widens a loader's table.
|
|
15
|
+
//
|
|
16
|
+
// - An **ESM chunk** (webpack's ESM chunk format) doesn't push: the module exports its chunk ids and its module
|
|
17
|
+
// table, `export const __webpack_esm_ids__ = [ids…]` and `export const __webpack_esm_modules__ = {factories}`. It
|
|
18
|
+
// counts as a push into the registry `esmRegistry`, which no global names. Its **loader** loads it with `import()`
|
|
19
|
+
// and destructures the module's `__webpack_esm_modules__` (`let {__webpack_esm_modules__: o} = e`) to install them,
|
|
20
|
+
// which counts as a read of that registry, with the id table of its URL function.
|
|
21
|
+
//
|
|
22
|
+
// Registry names and chunk ids are build-local: they decide links, never identity (§6 Reporting).
|
|
23
|
+
import { walkTree } from "./ast.js";
|
|
24
|
+
import { literalKey } from "./keys.js";
|
|
25
|
+
import { parse } from "./parse.js";
|
|
26
|
+
/**
|
|
27
|
+
* The registry an ESM chunk's export of its module table counts as a push into (above). A global's name can't be it: a
|
|
28
|
+
* registry read never names it.
|
|
29
|
+
*/
|
|
30
|
+
export const esmRegistry = '\u0000webpack-esm';
|
|
31
|
+
/** The names an ESM chunk exports its chunk ids and its module table under. */
|
|
32
|
+
const esmIds = '__webpack_esm_ids__';
|
|
33
|
+
const esmModules = '__webpack_esm_modules__';
|
|
34
|
+
/** The chunk registries a program pushes into and reads (above). */
|
|
35
|
+
export function registriesOf(program) {
|
|
36
|
+
const pushes = [];
|
|
37
|
+
// The ESM chunk exports of each statement list, by the node that holds it.
|
|
38
|
+
const exported = new Map();
|
|
39
|
+
const esmReads = [];
|
|
40
|
+
// The variables registry reads are stored in, by name, with the registry and the read: `l = self.R = self.R || []`.
|
|
41
|
+
const stored = [];
|
|
42
|
+
const hooked = new Set();
|
|
43
|
+
const tables = [];
|
|
44
|
+
walkTree(program, {
|
|
45
|
+
enter(node, parent) {
|
|
46
|
+
if (node.type === 'CallExpression') {
|
|
47
|
+
const push = pushOf(node);
|
|
48
|
+
if (push !== undefined)
|
|
49
|
+
pushes.push(push);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (node.type === 'VariableDeclarator') {
|
|
53
|
+
const id = node['id'];
|
|
54
|
+
const init = node['init'];
|
|
55
|
+
const registry = init === null ? undefined : registryRead(init);
|
|
56
|
+
if (registry !== undefined && id.type === 'Identifier')
|
|
57
|
+
stored.push({ name: id['name'], registry, read: init });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (node.type === 'AssignmentExpression' && node['operator'] === '=') {
|
|
61
|
+
const left = node['left'];
|
|
62
|
+
const right = node['right'];
|
|
63
|
+
const registry = registryRead(right);
|
|
64
|
+
if (registry !== undefined && left.type === 'Identifier')
|
|
65
|
+
stored.push({ name: left['name'], registry, read: right });
|
|
66
|
+
// `l.push = …`: the loader reads every push.
|
|
67
|
+
const object = left.type === 'MemberExpression' ? left['object'] : undefined;
|
|
68
|
+
if (object?.type === 'Identifier' && memberName(left) === 'push')
|
|
69
|
+
hooked.add(object['name']);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (node.type === 'ExportNamedDeclaration' && parent !== null) {
|
|
73
|
+
const declaration = node['declaration'];
|
|
74
|
+
if (declaration?.type !== 'VariableDeclaration')
|
|
75
|
+
return;
|
|
76
|
+
for (const declarator of declaration['declarations']) {
|
|
77
|
+
const id = declarator['id'];
|
|
78
|
+
const init = declarator['init'];
|
|
79
|
+
if (id.type !== 'Identifier' || init === null)
|
|
80
|
+
continue;
|
|
81
|
+
const entry = exported.get(parent) ?? {};
|
|
82
|
+
if (id['name'] === esmIds && init.type === 'ArrayExpression') {
|
|
83
|
+
const ids = init['elements'].map((element) => (element === null ? undefined : literalKey(element)));
|
|
84
|
+
if (ids.every((each) => each !== undefined))
|
|
85
|
+
entry.ids = ids;
|
|
86
|
+
}
|
|
87
|
+
else if (id['name'] === esmModules && init.type === 'ObjectExpression') {
|
|
88
|
+
entry.modules = init;
|
|
89
|
+
entry.declaration = node;
|
|
90
|
+
}
|
|
91
|
+
exported.set(parent, entry);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (node.type === 'ObjectPattern') {
|
|
96
|
+
const reads = node['properties'].some((property) => property.type === 'Property' && property['computed'] !== true && keyName(property['key']) === esmModules);
|
|
97
|
+
if (reads)
|
|
98
|
+
esmReads.push(node);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (isFunction(node)) {
|
|
102
|
+
const table = idTable(node);
|
|
103
|
+
if (table !== undefined)
|
|
104
|
+
tables.push(table);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
for (const { ids, modules, declaration } of exported.values()) {
|
|
109
|
+
if (ids === undefined || modules === undefined || declaration === undefined)
|
|
110
|
+
continue;
|
|
111
|
+
const factories = objectFactories(modules);
|
|
112
|
+
if (factories !== undefined)
|
|
113
|
+
pushes.push({ registry: esmRegistry, call: declaration, chunks: ids, factories });
|
|
114
|
+
}
|
|
115
|
+
const ids = mappedIds(tables);
|
|
116
|
+
const loads = [];
|
|
117
|
+
for (const { name, registry, read } of stored) {
|
|
118
|
+
if (!hooked.has(name) || loads.some((load) => load.read === read))
|
|
119
|
+
continue;
|
|
120
|
+
loads.push({ registry, read, ids });
|
|
121
|
+
}
|
|
122
|
+
for (const read of esmReads)
|
|
123
|
+
loads.push({ registry: esmRegistry, read, ids });
|
|
124
|
+
return { pushes, loads };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The chunk registries an artifact's code pushes into and reads, as data: each push's registry and chunk ids, and each
|
|
128
|
+
* loader's registry and the ids its id table maps (`null` for every id). None when the code doesn't parse.
|
|
129
|
+
*/
|
|
130
|
+
export function chunkRegistries(text, sourceType) {
|
|
131
|
+
// A cheap test first: code that never names `push` neither pushes nor hooks one, unless it is an ESM chunk or loader.
|
|
132
|
+
if (!text.includes('push') && !text.includes(esmModules))
|
|
133
|
+
return { pushes: [], loads: [] };
|
|
134
|
+
const parsed = parse(text, sourceType);
|
|
135
|
+
if ('error' in parsed)
|
|
136
|
+
return { pushes: [], loads: [] };
|
|
137
|
+
const { pushes, loads } = registriesOf(parsed.program);
|
|
138
|
+
return {
|
|
139
|
+
pushes: pushes.map(({ registry, chunks }) => ({ registry, chunks })),
|
|
140
|
+
loads: loads.map(({ registry, ids }) => ({ registry, ids: ids === 'any' ? null : [...ids].sort() })),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const globalNames = new Set(['self', 'window', 'globalThis']);
|
|
144
|
+
/** The registry an expression reads, `G.R = G.R || []` or `G.R ||= []`, by its name. */
|
|
145
|
+
function registryRead(node) {
|
|
146
|
+
if (node.type !== 'AssignmentExpression')
|
|
147
|
+
return undefined;
|
|
148
|
+
const left = node['left'];
|
|
149
|
+
const right = node['right'];
|
|
150
|
+
const name = globalMember(left);
|
|
151
|
+
if (name === undefined || name === esmRegistry)
|
|
152
|
+
return undefined;
|
|
153
|
+
if (node['operator'] === '||=' || node['operator'] === '??=')
|
|
154
|
+
return emptyArray(right) ? name : undefined;
|
|
155
|
+
if (node['operator'] !== '=' || right.type !== 'LogicalExpression' || right['operator'] !== '||')
|
|
156
|
+
return undefined;
|
|
157
|
+
return globalMember(right['left']) === name && emptyArray(right['right']) ? name : undefined;
|
|
158
|
+
}
|
|
159
|
+
/** `G.R` or `G["R"]` with `G` a name of the global object, or `this`: the member's name. */
|
|
160
|
+
function globalMember(node) {
|
|
161
|
+
if (node.type !== 'MemberExpression')
|
|
162
|
+
return undefined;
|
|
163
|
+
const object = node['object'];
|
|
164
|
+
const global = object.type === 'ThisExpression' || (object.type === 'Identifier' && globalNames.has(object['name']));
|
|
165
|
+
return global ? memberName(node) : undefined;
|
|
166
|
+
}
|
|
167
|
+
/** A member expression's constant name: `o.x` or `o["x"]`. */
|
|
168
|
+
function memberName(node) {
|
|
169
|
+
if (node.type !== 'MemberExpression')
|
|
170
|
+
return undefined;
|
|
171
|
+
const property = node['property'];
|
|
172
|
+
if (node['computed'] !== true)
|
|
173
|
+
return property.type === 'Identifier' ? property['name'] : undefined;
|
|
174
|
+
return property.type === 'Literal' && typeof property['value'] === 'string' ? property['value'] : undefined;
|
|
175
|
+
}
|
|
176
|
+
const emptyArray = (node) => node.type === 'ArrayExpression' && node['elements'].length === 0;
|
|
177
|
+
/** A push into a registry (above), or undefined for any other call. */
|
|
178
|
+
function pushOf(call) {
|
|
179
|
+
const callee = call['callee'];
|
|
180
|
+
if (callee.type !== 'MemberExpression' || memberName(callee) !== 'push')
|
|
181
|
+
return undefined;
|
|
182
|
+
const object = callee['object'];
|
|
183
|
+
const registry = registryRead(object) ?? globalMember(object);
|
|
184
|
+
if (registry === undefined)
|
|
185
|
+
return undefined;
|
|
186
|
+
const [only, ...rest] = call['arguments'];
|
|
187
|
+
if (only?.type !== 'ArrayExpression' || rest.length > 0)
|
|
188
|
+
return undefined;
|
|
189
|
+
const [list, modules, callback] = only['elements'];
|
|
190
|
+
if (list?.type !== 'ArrayExpression' || modules === null || modules === undefined)
|
|
191
|
+
return undefined;
|
|
192
|
+
const chunks = [];
|
|
193
|
+
for (const element of list['elements']) {
|
|
194
|
+
const id = element === null ? undefined : literalKey(element);
|
|
195
|
+
if (id === undefined)
|
|
196
|
+
return undefined;
|
|
197
|
+
chunks.push(id);
|
|
198
|
+
}
|
|
199
|
+
let factories = [];
|
|
200
|
+
if (modules.type === 'ObjectExpression') {
|
|
201
|
+
const found = objectFactories(modules);
|
|
202
|
+
if (found === undefined)
|
|
203
|
+
return undefined;
|
|
204
|
+
factories = found;
|
|
205
|
+
}
|
|
206
|
+
else if (modules.type === 'ArrayExpression') {
|
|
207
|
+
for (const [index, element] of modules['elements'].entries()) {
|
|
208
|
+
if (element === null)
|
|
209
|
+
continue;
|
|
210
|
+
if (!isFunction(element))
|
|
211
|
+
return undefined;
|
|
212
|
+
factories.push({ id: String(index), factory: element });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
return undefined;
|
|
217
|
+
const push = { registry, call, chunks, factories };
|
|
218
|
+
if (callback !== null && callback !== undefined && isFunction(callback))
|
|
219
|
+
push.callback = callback;
|
|
220
|
+
return push;
|
|
221
|
+
}
|
|
222
|
+
/** An object literal of factories keyed by constant module id, or undefined when it is anything else. */
|
|
223
|
+
function objectFactories(modules) {
|
|
224
|
+
const factories = [];
|
|
225
|
+
for (const property of modules['properties']) {
|
|
226
|
+
if (property.type !== 'Property' || property['kind'] !== 'init' || property['computed'] === true)
|
|
227
|
+
return undefined;
|
|
228
|
+
const id = keyName(property['key']);
|
|
229
|
+
const value = property['value'];
|
|
230
|
+
if (id === undefined || !isFunction(value))
|
|
231
|
+
return undefined;
|
|
232
|
+
factories.push({ id, factory: value });
|
|
233
|
+
}
|
|
234
|
+
return factories;
|
|
235
|
+
}
|
|
236
|
+
/** A non-computed property key's name. */
|
|
237
|
+
const keyName = (key) => (key.type === 'Identifier' ? key['name'] : literalKey(key));
|
|
238
|
+
const functionTypes = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
|
|
239
|
+
const isFunction = (node) => functionTypes.has(node.type);
|
|
240
|
+
/**
|
|
241
|
+
* The ids a program's URL functions map (above): those of the functions that build a script URL when there are any,
|
|
242
|
+
* else of every concatenating function, a table outweighing 'any'.
|
|
243
|
+
*/
|
|
244
|
+
function mappedIds(tables) {
|
|
245
|
+
const scripts = tables.filter((table) => table.script);
|
|
246
|
+
const counted = scripts.length > 0 ? scripts : tables.filter((table) => table.ids !== 'any');
|
|
247
|
+
if (counted.some((table) => table.ids === 'any'))
|
|
248
|
+
return 'any';
|
|
249
|
+
if (counted.length === 0)
|
|
250
|
+
return tables.length > 0 ? 'any' : new Set();
|
|
251
|
+
return new Set(counted.flatMap((table) => [...table.ids]));
|
|
252
|
+
}
|
|
253
|
+
const scriptUrl = /\.[cm]?js(?:$|[?#])/i;
|
|
254
|
+
/**
|
|
255
|
+
* A function's id table (above): the ids it maps, 'any' when it maps every id, and whether it builds a script URL, or
|
|
256
|
+
* undefined when it concatenates no use of its one parameter.
|
|
257
|
+
*/
|
|
258
|
+
function idTable(fn) {
|
|
259
|
+
const params = fn['params'];
|
|
260
|
+
if (params.length !== 1 || params[0].type !== 'Identifier')
|
|
261
|
+
return undefined;
|
|
262
|
+
const param = params[0]['name'];
|
|
263
|
+
const isParam = (node) => node.type === 'Identifier' && node['name'] === param;
|
|
264
|
+
// The key sets of the tables it indexes with no fallback: an id must be in each.
|
|
265
|
+
const constraints = [];
|
|
266
|
+
let used = false;
|
|
267
|
+
let script = false;
|
|
268
|
+
walkTree(fn['body'], {
|
|
269
|
+
enter(node) {
|
|
270
|
+
if (node !== fn['body'] && isFunction(node))
|
|
271
|
+
return false;
|
|
272
|
+
if (node.type !== 'BinaryExpression' || node['operator'] !== '+')
|
|
273
|
+
return undefined;
|
|
274
|
+
if (!concatenation(node))
|
|
275
|
+
return undefined;
|
|
276
|
+
for (const side of [node['left'], node['right']]) {
|
|
277
|
+
if (side.type === 'Literal' && typeof side['value'] === 'string' && scriptUrl.test(side['value']))
|
|
278
|
+
script = true;
|
|
279
|
+
if (isParam(side))
|
|
280
|
+
used = true;
|
|
281
|
+
// `{…}[e] || e` keeps a table's id, or the id itself: every id.
|
|
282
|
+
if (side.type === 'LogicalExpression') {
|
|
283
|
+
const operands = [side['left'], side['right']];
|
|
284
|
+
if (operands.some((operand) => isParam(operand) || tableKeys(operand, isParam) !== undefined))
|
|
285
|
+
used = true;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const keys = tableKeys(side, isParam);
|
|
289
|
+
if (keys !== undefined) {
|
|
290
|
+
used = true;
|
|
291
|
+
constraints.push(keys);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return undefined;
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
if (!used)
|
|
298
|
+
return undefined;
|
|
299
|
+
if (constraints.length === 0)
|
|
300
|
+
return { ids: 'any', script };
|
|
301
|
+
const [first, ...rest] = constraints;
|
|
302
|
+
return { ids: new Set(first.filter((id) => rest.every((keys) => keys.includes(id)))), script };
|
|
303
|
+
}
|
|
304
|
+
/** Whether a `+` builds a string: one of its operands, down its left spine, is a string literal. */
|
|
305
|
+
function concatenation(node) {
|
|
306
|
+
for (let at = node; at.type === 'BinaryExpression' && at['operator'] === '+'; at = at['left']) {
|
|
307
|
+
const right = at['right'];
|
|
308
|
+
const left = at['left'];
|
|
309
|
+
if (right.type === 'Literal' && typeof right['value'] === 'string')
|
|
310
|
+
return true;
|
|
311
|
+
if (left.type === 'Literal' && typeof left['value'] === 'string')
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
/** `{k: v, …}[p]`: the literal's constant keys. */
|
|
317
|
+
function tableKeys(node, isParam) {
|
|
318
|
+
if (node.type !== 'MemberExpression' || node['computed'] !== true || !isParam(node['property']))
|
|
319
|
+
return undefined;
|
|
320
|
+
const object = node['object'];
|
|
321
|
+
if (object.type !== 'ObjectExpression')
|
|
322
|
+
return undefined;
|
|
323
|
+
const keys = [];
|
|
324
|
+
for (const property of object['properties']) {
|
|
325
|
+
if (property.type !== 'Property' || property['computed'] === true)
|
|
326
|
+
return undefined;
|
|
327
|
+
const key = property['key'];
|
|
328
|
+
const id = key.type === 'Identifier' ? key['name'] : literalKey(key);
|
|
329
|
+
if (id === undefined)
|
|
330
|
+
return undefined;
|
|
331
|
+
keys.push(id);
|
|
332
|
+
}
|
|
333
|
+
return keys;
|
|
334
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Realm, SerializedCatalog } from '../../../catalog.ts';
|
|
2
|
+
import { type AstNode } from './ast.ts';
|
|
3
|
+
import { type GlobalResolution, type Position } from './global-object.ts';
|
|
4
|
+
import type { ScopeRules } from './scope.ts';
|
|
5
|
+
/** The natives that compile code from strings (§2). */
|
|
6
|
+
export type Compiler = 'eval' | 'setTimeout' | 'setInterval' | 'Function' | '%AsyncFunction%' | '%GeneratorFunction%' | '%AsyncGeneratorFunction%';
|
|
7
|
+
/** The functions the global object holds that compile code from strings, which are no holders. */
|
|
8
|
+
export declare const isGlobalCompiler: (name: unknown) => name is "eval" | "setTimeout" | "setInterval";
|
|
9
|
+
/**
|
|
10
|
+
* The arguments a call passes: those the analyzer can list, in order (`null` for a hole, which is `undefined`), then
|
|
11
|
+
* the expression whose value gives any further ones, which it can't list (a spread, or an array that is no literal).
|
|
12
|
+
*/
|
|
13
|
+
export interface Args {
|
|
14
|
+
items: (AstNode | null)[];
|
|
15
|
+
rest?: AstNode;
|
|
16
|
+
}
|
|
17
|
+
/** A call that reaches a native that compiles code from strings, the arguments it passes it, and the route it took. */
|
|
18
|
+
export interface IndirectCall {
|
|
19
|
+
compiler: Compiler;
|
|
20
|
+
args: Args;
|
|
21
|
+
/**
|
|
22
|
+
* Each step of the route that made the call indirect, once: those that reached the native (`a computed key`,
|
|
23
|
+
* `.constructor`, `.bind`), then those that called it (`.call`, `Reflect.apply`).
|
|
24
|
+
*/
|
|
25
|
+
via: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface CodeRoutes {
|
|
28
|
+
/**
|
|
29
|
+
* The natives that compile code a call, `new` expression or tagged template reaches, one for each native, and how.
|
|
30
|
+
* The direct forms are not told apart here: the resolver tries them first.
|
|
31
|
+
*/
|
|
32
|
+
reached(node: AstNode, at: Position): IndirectCall[];
|
|
33
|
+
/**
|
|
34
|
+
* The expressions a callee may be (Principle 3): each branch of a conditional or logical expression that its test
|
|
35
|
+
* doesn't rule out, in source order, or the callee itself.
|
|
36
|
+
*/
|
|
37
|
+
alternatives(expression: AstNode, at: Position): AstNode[];
|
|
38
|
+
}
|
|
39
|
+
export declare function codeRoutes(context: {
|
|
40
|
+
catalog: SerializedCatalog;
|
|
41
|
+
realm: Realm;
|
|
42
|
+
/** The realm's global holder, e.g. `Window`. */
|
|
43
|
+
global: string;
|
|
44
|
+
program: AstNode;
|
|
45
|
+
scope: ScopeRules;
|
|
46
|
+
resolution: GlobalResolution;
|
|
47
|
+
}): CodeRoutes;
|
|
48
|
+
/**
|
|
49
|
+
* The type of the node that stands for a tagged template's strings array: an array, which a string conversion joins
|
|
50
|
+
* into the template's text, located at the template.
|
|
51
|
+
*/
|
|
52
|
+
export declare const templateStrings = "TemplateStrings";
|