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.
Files changed (221) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +61 -0
  3. package/README.md +183 -2
  4. package/catalog/catalog.json +552211 -0
  5. package/catalog/catalog.schema.json +787 -0
  6. package/catalog/changelog.json +3 -0
  7. package/dist/bin/monkeypatchlist.d.ts +2 -0
  8. package/dist/bin/monkeypatchlist.js +3 -0
  9. package/dist/catalog.d.ts +561 -0
  10. package/dist/catalog.js +58 -0
  11. package/dist/cli.d.ts +84 -0
  12. package/dist/cli.js +470 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +1 -0
  15. package/dist/lockfile-input.d.ts +9 -0
  16. package/dist/lockfile-input.js +91 -0
  17. package/dist/packages/analysis/binding.d.ts +2 -0
  18. package/dist/packages/analysis/binding.js +5 -0
  19. package/dist/packages/analysis/index.d.ts +10 -0
  20. package/dist/packages/analysis/index.js +13 -0
  21. package/dist/packages/analysis/job.d.ts +33 -0
  22. package/dist/packages/analysis/job.js +85 -0
  23. package/dist/packages/analysis/lib/absence-tags.d.ts +34 -0
  24. package/dist/packages/analysis/lib/absence-tags.js +340 -0
  25. package/dist/packages/analysis/lib/ast.d.ts +28 -0
  26. package/dist/packages/analysis/lib/ast.js +64 -0
  27. package/dist/packages/analysis/lib/binding.d.ts +36 -0
  28. package/dist/packages/analysis/lib/binding.js +66 -0
  29. package/dist/packages/analysis/lib/budget.d.ts +80 -0
  30. package/dist/packages/analysis/lib/budget.js +31 -0
  31. package/dist/packages/analysis/lib/catch-arms.d.ts +19 -0
  32. package/dist/packages/analysis/lib/catch-arms.js +351 -0
  33. package/dist/packages/analysis/lib/chunk-loads.d.ts +160 -0
  34. package/dist/packages/analysis/lib/chunk-loads.js +882 -0
  35. package/dist/packages/analysis/lib/chunk-registry.d.ts +45 -0
  36. package/dist/packages/analysis/lib/chunk-registry.js +334 -0
  37. package/dist/packages/analysis/lib/code-routes.d.ts +52 -0
  38. package/dist/packages/analysis/lib/code-routes.js +921 -0
  39. package/dist/packages/analysis/lib/collisions.d.ts +22 -0
  40. package/dist/packages/analysis/lib/collisions.js +103 -0
  41. package/dist/packages/analysis/lib/definitions.d.ts +76 -0
  42. package/dist/packages/analysis/lib/definitions.js +782 -0
  43. package/dist/packages/analysis/lib/early-exits.d.ts +39 -0
  44. package/dist/packages/analysis/lib/early-exits.js +161 -0
  45. package/dist/packages/analysis/lib/effect.d.ts +138 -0
  46. package/dist/packages/analysis/lib/effect.js +800 -0
  47. package/dist/packages/analysis/lib/global-object.d.ts +581 -0
  48. package/dist/packages/analysis/lib/global-object.js +8240 -0
  49. package/dist/packages/analysis/lib/keys.d.ts +33 -0
  50. package/dist/packages/analysis/lib/keys.js +82 -0
  51. package/dist/packages/analysis/lib/line-starts.d.ts +41 -0
  52. package/dist/packages/analysis/lib/line-starts.js +79 -0
  53. package/dist/packages/analysis/lib/link-group.d.ts +51 -0
  54. package/dist/packages/analysis/lib/link-group.js +487 -0
  55. package/dist/packages/analysis/lib/module-links.d.ts +117 -0
  56. package/dist/packages/analysis/lib/module-links.js +1060 -0
  57. package/dist/packages/analysis/lib/nested-code.d.ts +38 -0
  58. package/dist/packages/analysis/lib/nested-code.js +200 -0
  59. package/dist/packages/analysis/lib/numeric.d.ts +16 -0
  60. package/dist/packages/analysis/lib/numeric.js +216 -0
  61. package/dist/packages/analysis/lib/once-flags.d.ts +12 -0
  62. package/dist/packages/analysis/lib/once-flags.js +190 -0
  63. package/dist/packages/analysis/lib/parameter-arguments.d.ts +12 -0
  64. package/dist/packages/analysis/lib/parameter-arguments.js +34 -0
  65. package/dist/packages/analysis/lib/parse.d.ts +10 -0
  66. package/dist/packages/analysis/lib/parse.js +39 -0
  67. package/dist/packages/analysis/lib/partial.d.ts +60 -0
  68. package/dist/packages/analysis/lib/partial.js +518 -0
  69. package/dist/packages/analysis/lib/print.d.ts +3 -0
  70. package/dist/packages/analysis/lib/print.js +150 -0
  71. package/dist/packages/analysis/lib/reachability.d.ts +137 -0
  72. package/dist/packages/analysis/lib/reachability.js +1295 -0
  73. package/dist/packages/analysis/lib/resolver.d.ts +13 -0
  74. package/dist/packages/analysis/lib/resolver.js +2886 -0
  75. package/dist/packages/analysis/lib/restores.d.ts +42 -0
  76. package/dist/packages/analysis/lib/restores.js +482 -0
  77. package/dist/packages/analysis/lib/rewritten-deletes.d.ts +6 -0
  78. package/dist/packages/analysis/lib/rewritten-deletes.js +142 -0
  79. package/dist/packages/analysis/lib/scope.d.ts +110 -0
  80. package/dist/packages/analysis/lib/scope.js +443 -0
  81. package/dist/packages/analysis/lib/script-loads.d.ts +56 -0
  82. package/dist/packages/analysis/lib/script-loads.js +359 -0
  83. package/dist/packages/analysis/lib/summaries.d.ts +177 -0
  84. package/dist/packages/analysis/lib/summaries.js +498 -0
  85. package/dist/packages/analysis/lib/types.d.ts +632 -0
  86. package/dist/packages/analysis/lib/types.js +128 -0
  87. package/dist/packages/analysis/lib/wrap.d.ts +34 -0
  88. package/dist/packages/analysis/lib/wrap.js +407 -0
  89. package/dist/packages/analysis/lib/writes.d.ts +358 -0
  90. package/dist/packages/analysis/lib/writes.js +1612 -0
  91. package/dist/packages/analysis/visit-order.d.ts +3 -0
  92. package/dist/packages/analysis/visit-order.js +23 -0
  93. package/dist/packages/attribution/index.d.ts +233 -0
  94. package/dist/packages/attribution/index.js +444 -0
  95. package/dist/packages/attribution/lib/containing-package.d.ts +76 -0
  96. package/dist/packages/attribution/lib/containing-package.js +304 -0
  97. package/dist/packages/attribution/lib/first-party.d.ts +37 -0
  98. package/dist/packages/attribution/lib/first-party.js +153 -0
  99. package/dist/packages/attribution/lib/lockfile-graph.d.ts +99 -0
  100. package/dist/packages/attribution/lib/lockfile-graph.js +158 -0
  101. package/dist/packages/attribution/lib/lockfile.d.ts +33 -0
  102. package/dist/packages/attribution/lib/lockfile.js +330 -0
  103. package/dist/packages/attribution/lib/path-rules.d.ts +29 -0
  104. package/dist/packages/attribution/lib/path-rules.js +84 -0
  105. package/dist/packages/attribution/lib/pnpm.d.ts +4 -0
  106. package/dist/packages/attribution/lib/pnpm.js +133 -0
  107. package/dist/packages/attribution/lib/remote-url.d.ts +7 -0
  108. package/dist/packages/attribution/lib/remote-url.js +24 -0
  109. package/dist/packages/attribution/lib/source-map.d.ts +51 -0
  110. package/dist/packages/attribution/lib/source-map.js +125 -0
  111. package/dist/packages/attribution/lib/yaml.d.ts +9 -0
  112. package/dist/packages/attribution/lib/yaml.js +277 -0
  113. package/dist/packages/attribution/lib/yarn.d.ts +7 -0
  114. package/dist/packages/attribution/lib/yarn.js +227 -0
  115. package/dist/packages/baseline/index.d.ts +45 -0
  116. package/dist/packages/baseline/index.js +41 -0
  117. package/dist/packages/baseline/lib/error.d.ts +15 -0
  118. package/dist/packages/baseline/lib/error.js +25 -0
  119. package/dist/packages/baseline/lib/format.d.ts +49 -0
  120. package/dist/packages/baseline/lib/format.js +22 -0
  121. package/dist/packages/baseline/lib/migrations.d.ts +19 -0
  122. package/dist/packages/baseline/lib/migrations.js +7 -0
  123. package/dist/packages/baseline/lib/previous.d.ts +23 -0
  124. package/dist/packages/baseline/lib/previous.js +64 -0
  125. package/dist/packages/baseline/lib/read.d.ts +39 -0
  126. package/dist/packages/baseline/lib/read.js +219 -0
  127. package/dist/packages/baseline/lib/report.d.ts +25 -0
  128. package/dist/packages/baseline/lib/report.js +104 -0
  129. package/dist/packages/baseline/lib/skew.d.ts +51 -0
  130. package/dist/packages/baseline/lib/skew.js +75 -0
  131. package/dist/packages/compare/index.d.ts +120 -0
  132. package/dist/packages/compare/index.js +274 -0
  133. package/dist/packages/compare/lib/catalog-notes.d.ts +73 -0
  134. package/dist/packages/compare/lib/catalog-notes.js +224 -0
  135. package/dist/packages/completeness/index.d.ts +246 -0
  136. package/dist/packages/completeness/index.js +249 -0
  137. package/dist/packages/confidence/index.d.ts +54 -0
  138. package/dist/packages/confidence/index.js +71 -0
  139. package/dist/packages/config/index.d.ts +107 -0
  140. package/dist/packages/config/index.js +95 -0
  141. package/dist/packages/config/lib/command-line.d.ts +23 -0
  142. package/dist/packages/config/lib/command-line.js +239 -0
  143. package/dist/packages/config/lib/config-file.d.ts +11 -0
  144. package/dist/packages/config/lib/config-file.js +119 -0
  145. package/dist/packages/config/lib/scan-config-id.d.ts +28 -0
  146. package/dist/packages/config/lib/scan-config-id.js +54 -0
  147. package/dist/packages/config/lib/schema.d.ts +2 -0
  148. package/dist/packages/config/lib/schema.js +31 -0
  149. package/dist/packages/config/lib/types.d.ts +64 -0
  150. package/dist/packages/config/lib/types.js +6 -0
  151. package/dist/packages/config/lib/urls.d.ts +6 -0
  152. package/dist/packages/config/lib/urls.js +9 -0
  153. package/dist/packages/discovery/index.d.ts +277 -0
  154. package/dist/packages/discovery/index.js +1950 -0
  155. package/dist/packages/discovery/lib/import-map.d.ts +68 -0
  156. package/dist/packages/discovery/lib/import-map.js +180 -0
  157. package/dist/packages/discovery/lib/requests.d.ts +58 -0
  158. package/dist/packages/discovery/lib/requests.js +591 -0
  159. package/dist/packages/discovery/lib/scopes.d.ts +6 -0
  160. package/dist/packages/discovery/lib/scopes.js +286 -0
  161. package/dist/packages/discovery/lib/source-maps.d.ts +56 -0
  162. package/dist/packages/discovery/lib/source-maps.js +209 -0
  163. package/dist/packages/discovery/lib/swept-code.d.ts +27 -0
  164. package/dist/packages/discovery/lib/swept-code.js +107 -0
  165. package/dist/packages/discovery/lib/syntax.d.ts +17 -0
  166. package/dist/packages/discovery/lib/syntax.js +39 -0
  167. package/dist/packages/findings/index.d.ts +106 -0
  168. package/dist/packages/findings/index.js +254 -0
  169. package/dist/packages/findings/lib/scope.d.ts +38 -0
  170. package/dist/packages/findings/lib/scope.js +110 -0
  171. package/dist/packages/parallel/index.d.ts +2 -0
  172. package/dist/packages/parallel/index.js +5 -0
  173. package/dist/packages/parallel/lib/protocol.d.ts +15 -0
  174. package/dist/packages/parallel/lib/protocol.js +2 -0
  175. package/dist/packages/parallel/lib/runner.d.ts +36 -0
  176. package/dist/packages/parallel/lib/runner.js +96 -0
  177. package/dist/packages/parallel/lib/schedule.d.ts +16 -0
  178. package/dist/packages/parallel/lib/schedule.js +52 -0
  179. package/dist/packages/parallel/lib/worker.d.ts +1 -0
  180. package/dist/packages/parallel/lib/worker.js +18 -0
  181. package/dist/packages/policy/index.d.ts +36 -0
  182. package/dist/packages/policy/index.js +22 -0
  183. package/dist/packages/report/explain.d.ts +26 -0
  184. package/dist/packages/report/explain.js +99 -0
  185. package/dist/packages/report/index.d.ts +210 -0
  186. package/dist/packages/report/index.js +388 -0
  187. package/dist/packages/report/terminal.d.ts +11 -0
  188. package/dist/packages/report/terminal.js +331 -0
  189. package/dist/packages/source-store/fixture.d.ts +22 -0
  190. package/dist/packages/source-store/fixture.js +58 -0
  191. package/dist/packages/source-store/index.d.ts +278 -0
  192. package/dist/packages/source-store/index.js +322 -0
  193. package/dist/packages/source-store/lib/addresses.d.ts +6 -0
  194. package/dist/packages/source-store/lib/addresses.js +91 -0
  195. package/dist/packages/source-store/lib/connection.d.ts +15 -0
  196. package/dist/packages/source-store/lib/connection.js +135 -0
  197. package/dist/packages/source-store/lib/content-types.d.ts +16 -0
  198. package/dist/packages/source-store/lib/content-types.js +75 -0
  199. package/dist/packages/source-store/lib/data-urls.d.ts +10 -0
  200. package/dist/packages/source-store/lib/data-urls.js +85 -0
  201. package/dist/packages/source-store/lib/deadline.d.ts +18 -0
  202. package/dist/packages/source-store/lib/deadline.js +44 -0
  203. package/dist/packages/source-store/lib/exchange.d.ts +29 -0
  204. package/dist/packages/source-store/lib/exchange.js +112 -0
  205. package/dist/packages/source-store/lib/integrity.d.ts +19 -0
  206. package/dist/packages/source-store/lib/integrity.js +86 -0
  207. package/dist/packages/source-store/lib/outcomes.d.ts +12 -0
  208. package/dist/packages/source-store/lib/outcomes.js +22 -0
  209. package/dist/packages/source-store/lib/proxy.d.ts +11 -0
  210. package/dist/packages/source-store/lib/proxy.js +53 -0
  211. package/dist/packages/source-store/lib/session.d.ts +12 -0
  212. package/dist/packages/source-store/lib/session.js +139 -0
  213. package/dist/packages/source-store/lib/urls.d.ts +6 -0
  214. package/dist/packages/source-store/lib/urls.js +15 -0
  215. package/dist/packages/source-store/resolve.d.ts +59 -0
  216. package/dist/packages/source-store/resolve.js +45 -0
  217. package/dist/scan.d.ts +65 -0
  218. package/dist/scan.js +592 -0
  219. package/package.json +95 -4
  220. package/schema/config.schema.json +133 -0
  221. package/schema/report.schema.json +1905 -0
@@ -0,0 +1,782 @@
1
+ // Definitions and reaching definitions (§5 Resolver capabilities 2). A variable's value is the union of its
2
+ // definitions across its whole scope. Within one function, the walk orders a read after the definitions that reach it
3
+ // through statement lists, `if` arms, `?:`, `&&`/`||`/`??` and comma sequences, so a later definition kills an earlier
4
+ // one. Details:
5
+ //
6
+ // - A variable declared in the function the read is in starts with its start value (the `undefined` of a `var` or
7
+ // `let`, the native value of a global name), which counts only where the walk shows it reaches the read.
8
+ // - A read the walk can't order sees only the values the code assigns: a read in a nested function of a variable
9
+ // declared outside it, a read inside a loop, `try`, `switch` or other statement the walk doesn't follow, and a read
10
+ // past the walk's reach. Only a variable nothing assigns keeps its start value there.
11
+ // - A definition in another function (a closure, or the function around the read's) joins the read only where a call,
12
+ // `new`, tagged template, `await` or `yield` lies between the reaching definition and the read.
13
+ // - A statement the walk doesn't follow (a loop, `try`, `switch`, a labelled statement) adds the definitions in it to
14
+ // those that reach past it, and a closure's definitions when a call lies in it.
15
+ // - A class's static blocks and static field initializers run when the class is evaluated (ECMA-262
16
+ // ClassDefinitionEvaluation), so the walk follows them there, after the class's heritage and computed keys. Methods
17
+ // and instance field initializers are functions of their own.
18
+ //
19
+ // A variable is a declared name, keyed by the declaration it resolves to after the scope post-passes, or a name bound
20
+ // on the global object (§5 Global resolution): a free name or a classic script's top-level `var`, keyed by its name.
21
+ import { walkTree } from "./ast.js";
22
+ import { alwaysExits } from "./early-exits.js";
23
+ import { boundNames } from "./scope.js";
24
+ /** Whether a primitive passes a narrowing's test. */
25
+ export function passes(narrowing, value) {
26
+ switch (narrowing) {
27
+ case 'truthy':
28
+ return Boolean(value);
29
+ case 'falsy':
30
+ return !value;
31
+ case 'nullish':
32
+ return value === null || value === undefined;
33
+ case 'notNullish':
34
+ return value !== null && value !== undefined;
35
+ case 'null':
36
+ return value === null;
37
+ case 'notNull':
38
+ return value !== null;
39
+ case 'undefined':
40
+ return value === undefined;
41
+ default:
42
+ return value !== undefined;
43
+ }
44
+ }
45
+ /** The narrowing that holds where a test fails, from the one that holds where it passes. */
46
+ const opposite = {
47
+ truthy: 'falsy',
48
+ falsy: 'truthy',
49
+ nullish: 'notNullish',
50
+ notNullish: 'nullish',
51
+ null: 'notNull',
52
+ notNull: 'null',
53
+ undefined: 'notUndefined',
54
+ notUndefined: 'undefined',
55
+ };
56
+ /** Whether an assignment operator is a compound one that reads its target and folds (`+=`, `**=`), not `=` or a logical one. */
57
+ export const compoundOperator = (operator) => operator !== '=' && operator !== '||=' && operator !== '&&=' && operator !== '??=';
58
+ const compoundOperations = new WeakMap();
59
+ /**
60
+ * The binary expression a compound assignment of an identifier, `x op= e`, evaluates (ECMA-262 13.15.2): `x op e`,
61
+ * spanning the assignment, with its own operand nodes. One per assignment, so it is a stable node to memoize and trace
62
+ * by. Undefined for any other node.
63
+ */
64
+ export function compoundOf(site) {
65
+ if (site.type !== 'AssignmentExpression' || !compoundOperator(site['operator']))
66
+ return undefined;
67
+ const left = site['left'];
68
+ if (left.type !== 'Identifier')
69
+ return undefined;
70
+ let found = compoundOperations.get(site);
71
+ if (found === undefined) {
72
+ const operator = site['operator'].slice(0, -1);
73
+ found = { type: 'BinaryExpression', operator, left, right: site['right'], start: site.start, end: site.end };
74
+ compoundOperations.set(site, found);
75
+ }
76
+ return found;
77
+ }
78
+ /**
79
+ * The function declarations whose own name a parameter (or `arguments`) shadows, by their `id`: `function n(t, n){…}`.
80
+ * The seam gives both of its bindings `n` the declaration, so the name, read outside the function, is keyed by the
81
+ * declaration's `id` instead.
82
+ */
83
+ const shadowedNames = new WeakMap();
84
+ /** The function declaration a variable is the name of, when its key is the `id` of one a parameter shadows. */
85
+ export const shadowedNameOf = (scope) => scope === 'global' ? undefined : shadowedNames.get(scope);
86
+ /** Whether a function binds `name` in itself, as a parameter or `arguments`. */
87
+ export const bindsOwn = (fn, name) => name === 'arguments' || fn['params'].flatMap(boundNames).includes(name);
88
+ /** Functions, which run as code of their own. */
89
+ const functionTypes = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
90
+ /** Nodes whose evaluation can run code the read's function doesn't show: a call, `new`, `await` or `yield`. */
91
+ const runsCode = new Set([
92
+ 'CallExpression',
93
+ 'NewExpression',
94
+ 'TaggedTemplateExpression',
95
+ 'AwaitExpression',
96
+ 'YieldExpression',
97
+ ]);
98
+ /** The statements the walk follows in order. Any other statement is walked as a whole (`opaque`). */
99
+ const followed = new Set([
100
+ 'ExpressionStatement',
101
+ 'VariableDeclaration',
102
+ 'IfStatement',
103
+ 'BlockStatement',
104
+ 'ReturnStatement',
105
+ 'ThrowStatement',
106
+ 'EmptyStatement',
107
+ 'DebuggerStatement',
108
+ 'FunctionDeclaration',
109
+ 'ClassDeclaration',
110
+ 'ExportNamedDeclaration',
111
+ 'ExportDefaultDeclaration',
112
+ ]);
113
+ export function definitionsOf(program, scope) {
114
+ let index;
115
+ // Each identifier's key, found once: the scope rules don't change while the program is analyzed (§18 step 1).
116
+ const keys = new WeakMap();
117
+ const keyOf = (id) => {
118
+ let key = keys.get(id);
119
+ if (key === undefined)
120
+ keys.set(id, (key = keyFound(id) ?? null));
121
+ return key ?? undefined;
122
+ };
123
+ const keyFound = (id) => {
124
+ let name = id['name'];
125
+ let binding;
126
+ try {
127
+ binding = scope.resolve(id);
128
+ }
129
+ catch {
130
+ // A name the seam recorded no reference for.
131
+ return undefined;
132
+ }
133
+ if (binding !== 'local')
134
+ return { scope: 'global', name };
135
+ const declaration = scope.declaration(id);
136
+ if (declaration === undefined)
137
+ return undefined;
138
+ name = scope.nameOf(id);
139
+ // Outside a function declaration, its name is the function, even where a parameter of it shares the name.
140
+ if (declaration.type === 'FunctionDeclaration' && (id.start < declaration.start || id.start >= declaration.end)) {
141
+ const own = declaration['id'];
142
+ if (own !== null && bindsOwn(declaration, name)) {
143
+ shadowedNames.set(own, declaration);
144
+ return { scope: own, name };
145
+ }
146
+ }
147
+ return { scope: declaration, name };
148
+ };
149
+ function built() {
150
+ if (index !== undefined)
151
+ return index;
152
+ const byKey = new Map();
153
+ const owners = [];
154
+ const open = [];
155
+ /** The code whose `this` is open: the program, functions, static blocks and field initializers. */
156
+ const thisCode = [];
157
+ const define = (id, definition) => {
158
+ const key = keyOf(id);
159
+ if (key === undefined)
160
+ return;
161
+ const owner = open.at(-1);
162
+ let names = byKey.get(key.scope);
163
+ if (names === undefined)
164
+ byKey.set(key.scope, (names = new Map()));
165
+ let list = names.get(key.name);
166
+ if (list === undefined)
167
+ names.set(key.name, (list = []));
168
+ const topLevel = thisCode.slice(1).every((code) => code.type === 'ArrowFunctionExpression');
169
+ list.push({ ...definition, name: key.name, owner: owner.node, topLevel });
170
+ };
171
+ walkTree(program, {
172
+ enter(node, parent) {
173
+ const field = parent !== null &&
174
+ (parent.type === 'PropertyDefinition' || parent.type === 'AccessorProperty') &&
175
+ parent['value'] === node;
176
+ if (node.type === 'Program' || functionTypes.has(node.type) || node.type === 'StaticBlock' || field) {
177
+ thisCode.push(node);
178
+ }
179
+ // A static block or static field initializer runs when its class is evaluated: its code is its function's.
180
+ if (node.type === 'Program' || functionTypes.has(node.type) || (field && parent['static'] !== true)) {
181
+ const owner = {
182
+ node,
183
+ start: node.start,
184
+ end: node.end,
185
+ parent: open.at(-1) ?? null,
186
+ effects: [],
187
+ names: new Map(),
188
+ walks: new Map(),
189
+ };
190
+ owners.push(owner);
191
+ open.push(owner);
192
+ }
193
+ if (runsCode.has(node.type))
194
+ open.at(-1).effects.push(node.start);
195
+ if (node.type === 'Identifier') {
196
+ const { names } = open.at(-1);
197
+ const name = node['name'];
198
+ const starts = names.get(name);
199
+ if (starts === undefined)
200
+ names.set(name, [node.start]);
201
+ else
202
+ starts.push(node.start);
203
+ }
204
+ switch (node.type) {
205
+ case 'VariableDeclarator': {
206
+ const init = node['init'];
207
+ if (init === null)
208
+ break;
209
+ const pattern = node['id'];
210
+ for (const id of identifiersOf(pattern))
211
+ define(id, { site: node, pattern, source: init, logical: false });
212
+ break;
213
+ }
214
+ case 'AssignmentExpression': {
215
+ const left = node['left'];
216
+ const operator = node['operator'];
217
+ const right = node['right'];
218
+ const logical = operator === '||=' || operator === '&&=' || operator === '??=';
219
+ const source = operator === '=' || logical ? { source: right } : {};
220
+ const operation = compoundOf(node);
221
+ const compound = operation === undefined ? {} : { compound: operation };
222
+ if (left.type === 'Identifier')
223
+ define(left, { site: node, pattern: left, ...source, ...compound, logical });
224
+ else if (left.type === 'ObjectPattern' || left.type === 'ArrayPattern') {
225
+ for (const id of identifiersOf(left))
226
+ define(id, { site: node, pattern: left, source: right, logical: false });
227
+ }
228
+ break;
229
+ }
230
+ case 'UpdateExpression': {
231
+ const argument = node['argument'];
232
+ if (argument.type === 'Identifier')
233
+ define(argument, { site: node, pattern: argument, logical: false });
234
+ break;
235
+ }
236
+ case 'ForInStatement':
237
+ case 'ForOfStatement': {
238
+ const left = node['left'];
239
+ const patterns = left.type === 'VariableDeclaration'
240
+ ? left['declarations'].map((declarator) => declarator['id'])
241
+ : [left];
242
+ for (const pattern of patterns) {
243
+ for (const id of identifiersOf(pattern))
244
+ define(id, { site: node, pattern: id, logical: false });
245
+ }
246
+ break;
247
+ }
248
+ }
249
+ },
250
+ leave(node) {
251
+ if (open.at(-1)?.node === node)
252
+ open.pop();
253
+ if (thisCode.at(-1) === node)
254
+ thisCode.pop();
255
+ },
256
+ });
257
+ return (index = { byKey, owners });
258
+ }
259
+ const of = (key) => built().byKey.get(key.scope)?.get(key.name) ?? [];
260
+ /** The innermost owner whose own code holds `node`, other than `node` itself. */
261
+ function ownerOf(node) {
262
+ const { owners } = built();
263
+ let low = 0;
264
+ let high = owners.length - 1;
265
+ while (low < high) {
266
+ const middle = (low + high + 1) >> 1;
267
+ if (owners[middle].start <= node.start)
268
+ low = middle;
269
+ else
270
+ high = middle - 1;
271
+ }
272
+ let owner = owners[low];
273
+ while (owner !== null && !(owner.node !== node && owner.start <= node.start && node.end <= owner.end)) {
274
+ owner = owner.parent;
275
+ }
276
+ return owner ?? owners[0];
277
+ }
278
+ /** Whether a variable is declared in the own scope of `owner`, so it starts with its start value there. */
279
+ function declaredIn(key, owner) {
280
+ if (key.scope === 'global')
281
+ return owner.parent === null;
282
+ // A shadowed function name (`shadowedNameOf`) is declared around its function.
283
+ const named = shadowedNameOf(key.scope);
284
+ if (named !== undefined)
285
+ return ownerOf(named).node === owner.node;
286
+ const declaration = key.scope;
287
+ if (functionTypes.has(declaration.type)) {
288
+ // A function declares its parameters and `arguments` in itself, and its name around it (or in itself, for a
289
+ // function expression's name).
290
+ const params = declaration['params'].flatMap(boundNames);
291
+ const own = params.includes(key.name) || key.name === 'arguments' || declaration.type === 'FunctionExpression';
292
+ if (own)
293
+ return declaration === owner.node;
294
+ }
295
+ return ownerOf(declaration).node === owner.node;
296
+ }
297
+ function reaching(id, key) {
298
+ const owner = ownerOf(id);
299
+ let byName = owner.walks.get(key.scope);
300
+ if (byName === undefined)
301
+ owner.walks.set(key.scope, (byName = new Map()));
302
+ let reads = byName.get(key.name);
303
+ if (reads === undefined)
304
+ byName.set(key.name, (reads = walked(owner, key)));
305
+ return reads.get(id) ?? unorderedOf(of(key), isParameter(key));
306
+ }
307
+ /**
308
+ * What a read the walk can't order sees: the values the code assigns, or the start value when it assigns none. A
309
+ * parameter's start value is its argument, which it holds until the code assigns it, so that is seen too: a once-flag
310
+ * over a factory, `e => () => { if (e) { var n = e; e = 0; n() } }` (webpack's `.cw`, §6), reads its argument.
311
+ */
312
+ const unorderedOf = (all, parameter = false) => all.length === 0 || parameter ? { start: true, definitions: all, ordered: false } : { start: false, definitions: all, ordered: false };
313
+ /** Whether a variable is a function's parameter. */
314
+ const isParameter = (key) => key.scope !== 'global' &&
315
+ functionTypes.has(key.scope.type) &&
316
+ key.scope['params'].flatMap(boundNames).includes(key.name);
317
+ /**
318
+ * Walks the own code of `owner` once for the variable `key`, and gives the definitions that reach each read of it the
319
+ * walk orders.
320
+ */
321
+ function walked(owner, key) {
322
+ const readsOrdered = new Map();
323
+ const all = of(key);
324
+ const unordered = unorderedOf(all, isParameter(key));
325
+ const own = all.filter((definition) => definition.owner === owner.node);
326
+ const foreign = all.filter((definition) => definition.owner !== owner.node);
327
+ const ownAt = new Map(own.map((definition) => [definition.site, definition]));
328
+ const entry = new Map((declaredIn(key, owner) || (unordered.start && all.length === 0) ? ['start'] : unordered.start ? ['start', ...all] : all).map((item) => [item, []]));
329
+ const candidates = owner.names.get(key.name) ?? [];
330
+ /** Whether any of `list` (sorted starts) lies in `node`. */
331
+ const within = (list, node) => {
332
+ let low = 0;
333
+ let high = list.length;
334
+ while (low < high) {
335
+ const middle = (low + high) >> 1;
336
+ if (list[middle] < node.start)
337
+ low = middle + 1;
338
+ else
339
+ high = middle;
340
+ }
341
+ return low < list.length && list[low] < node.end;
342
+ };
343
+ /** Where the walk must look: the reads it may meet and the own definitions, in order. */
344
+ const interesting = [...candidates, ...own.map((definition) => definition.site.start)].sort((a, b) => a - b);
345
+ const effects = foreign.length === 0 ? [] : owner.effects;
346
+ /** Whether walking `node` can change what reaches a read other than through a call, or holds a read. */
347
+ const matters = (node) => within(interesting, node);
348
+ /** Whether a call, `new`, `await` or `yield` lies in `start`..`end`, where a closure's definitions join. */
349
+ const runsBetween = (start, end) => within(effects, { start, end });
350
+ /** Passes over code that holds no read and no own definition: only a closure's definitions can join there. */
351
+ const skip = (start, end) => {
352
+ if (effects.length > 0 && runsBetween(start, end))
353
+ add(foreign);
354
+ };
355
+ /**
356
+ * Walks a list of statements or expressions in order, visiting only those that matter, found by binary search, so
357
+ * that one walk costs what its reads and definitions need rather than the length of the list.
358
+ */
359
+ const inOrder = (nodes, visit) => {
360
+ let next = 0;
361
+ while (next < nodes.length) {
362
+ const from = nodes[next];
363
+ const at = firstFrom(interesting, from.start);
364
+ const last = nodes.at(-1);
365
+ if (at === undefined || at >= last.end) {
366
+ skip(from.start, last.end);
367
+ return;
368
+ }
369
+ // The node the interesting position is in: the last one that starts at or before it.
370
+ let low = next;
371
+ let high = nodes.length - 1;
372
+ while (low < high) {
373
+ const middle = (low + high + 1) >> 1;
374
+ if (nodes[middle].start <= at)
375
+ low = middle;
376
+ else
377
+ high = middle - 1;
378
+ }
379
+ const node = nodes[low];
380
+ if (low > next)
381
+ skip(from.start, node.start);
382
+ if (at < node.end)
383
+ visit(node);
384
+ else
385
+ skip(node.start, node.end);
386
+ next = low + 1;
387
+ }
388
+ };
389
+ let state = entry;
390
+ const add = (definitions) => {
391
+ if (state === null)
392
+ return;
393
+ for (const definition of definitions)
394
+ state.set(definition, []);
395
+ };
396
+ /** Where either path may come from: an item narrowed on both keeps only the narrowings both paths passed. */
397
+ const join = (a, b) => {
398
+ if (a === null)
399
+ return b;
400
+ if (b === null)
401
+ return a;
402
+ const out = new Map(a);
403
+ for (const [item, narrowings] of b) {
404
+ const other = out.get(item);
405
+ out.set(item, other === undefined ? narrowings : other.filter((each) => narrowings.includes(each)));
406
+ }
407
+ return out;
408
+ };
409
+ const copy = (a) => (a === null ? null : new Map(a));
410
+ /** `a` with every item narrowed by `narrowing` too. */
411
+ const narrow = (a, narrowing) => {
412
+ if (a === null || narrowing === undefined)
413
+ return a;
414
+ return new Map([...a].map(([item, narrowings]) => [item, [...narrowings, narrowing]]));
415
+ };
416
+ /** Whether every path through `node` that reaches its end assigns the variable in it: no item from before it is left. */
417
+ const assignsIn = (after, node) => after !== null &&
418
+ [...after.keys()].every((item) => item !== 'start' && node.start <= item.site.start && item.site.end <= node.end);
419
+ const defineAt = (site) => {
420
+ const definition = ownAt.get(site);
421
+ if (definition === undefined || state === null)
422
+ return;
423
+ if (definition.logical)
424
+ state.set(definition, []);
425
+ else
426
+ state = new Map([[definition, []]]);
427
+ };
428
+ /**
429
+ * What a test of the variable shows where it passes (§5 Resolver capabilities 6): `k`, `!k`, `k == null`,
430
+ * `k === undefined`, `typeof k === "undefined"` and their negations. Any other test shows nothing.
431
+ */
432
+ const tested = (test) => {
433
+ if (test.type === 'Identifier')
434
+ return reads(test) ? 'truthy' : undefined;
435
+ if (test.type === 'UnaryExpression' && test['operator'] === '!') {
436
+ const inner = tested(test['argument']);
437
+ return inner === undefined ? undefined : opposite[inner];
438
+ }
439
+ if (test.type !== 'BinaryExpression')
440
+ return undefined;
441
+ const operator = test['operator'];
442
+ if (!['==', '!=', '===', '!=='].includes(operator))
443
+ return undefined;
444
+ const left = test['left'];
445
+ const right = test['right'];
446
+ const narrowing = comparedTo(left, right, operator.length === 3) ?? comparedTo(right, left, operator.length === 3);
447
+ if (narrowing === undefined)
448
+ return undefined;
449
+ return operator.startsWith('!') ? opposite[narrowing] : narrowing;
450
+ };
451
+ /** What `side == other` (or `===`, when `strict`) shows about the variable where it holds. */
452
+ const comparedTo = (side, other, strict) => {
453
+ const typeOf = side.type === 'UnaryExpression' && side['operator'] === 'typeof';
454
+ const read = typeOf ? side['argument'] : side;
455
+ if (read.type !== 'Identifier' || !reads(read))
456
+ return undefined;
457
+ if (typeOf)
458
+ return other.type === 'Literal' && other['value'] === 'undefined' ? 'undefined' : undefined;
459
+ const isNull = other.type === 'Literal' && other['value'] === null && other['raw'] === 'null';
460
+ const isUndefined = (other.type === 'Identifier' && other['name'] === 'undefined' && keyOf(other)?.scope === 'global') ||
461
+ (other.type === 'UnaryExpression' && other['operator'] === 'void' && other['argument'].type === 'Literal');
462
+ if (!strict)
463
+ return isNull || isUndefined ? 'nullish' : undefined;
464
+ return isNull ? 'null' : isUndefined ? 'undefined' : undefined;
465
+ };
466
+ /**
467
+ * A lazy init by an `if` or `?:` (§5 Resolver capabilities 6): when one arm assigns the variable on every path that
468
+ * reaches its end, what reaches past the other arm from before the test passed the test that arm is taken on.
469
+ */
470
+ const lazily = (test, consequent, alternate, taken, other, set) => {
471
+ const shown = tested(test);
472
+ if (shown === undefined)
473
+ return;
474
+ if (assignsIn(taken, consequent))
475
+ set(taken, narrow(other, opposite[shown]));
476
+ else if (alternate !== null && assignsIn(other, alternate))
477
+ set(narrow(taken, shown), other);
478
+ };
479
+ /** Whether an identifier reads the variable this walk is for. */
480
+ const reads = (id) => {
481
+ const read = keyOf(id);
482
+ return read !== undefined && read.scope === key.scope && read.name === key.name;
483
+ };
484
+ function statement(node) {
485
+ if (!matters(node)) {
486
+ skip(node.start, node.end);
487
+ // A `return` or `throw` that holds no read still ends the path it is on.
488
+ if (alwaysExits(node))
489
+ state = null;
490
+ return;
491
+ }
492
+ if (!followed.has(node.type)) {
493
+ // A loop, `try`, `switch` or labelled statement: a read in it falls back to the union, and past it the
494
+ // definitions in it may reach.
495
+ add(own.filter((definition) => node.start <= definition.site.start && definition.site.end <= node.end));
496
+ skip(node.start, node.end);
497
+ return;
498
+ }
499
+ switch (node.type) {
500
+ case 'ExpressionStatement':
501
+ expression(node['expression']);
502
+ return;
503
+ case 'VariableDeclaration':
504
+ inOrder(node['declarations'], (declarator) => {
505
+ const init = declarator['init'];
506
+ if (init !== null)
507
+ expression(init);
508
+ expression(declarator['id']);
509
+ defineAt(declarator);
510
+ });
511
+ return;
512
+ case 'IfStatement': {
513
+ const test = node['test'];
514
+ const [whenTrue, whenFalse] = branches(test);
515
+ state = whenTrue;
516
+ const consequent = node['consequent'];
517
+ statement(consequent);
518
+ let taken = state;
519
+ state = whenFalse;
520
+ const alternate = node['alternate'];
521
+ if (alternate !== null)
522
+ statement(alternate);
523
+ lazily(test, consequent, alternate, taken, state, (a, b) => ((taken = a), (state = b)));
524
+ state = join(taken, state);
525
+ return;
526
+ }
527
+ case 'BlockStatement':
528
+ inOrder(node['body'], statement);
529
+ return;
530
+ case 'ReturnStatement':
531
+ case 'ThrowStatement': {
532
+ const argument = node['argument'];
533
+ if (argument !== null)
534
+ expression(argument);
535
+ state = null;
536
+ return;
537
+ }
538
+ case 'ExportNamedDeclaration':
539
+ case 'ExportDefaultDeclaration': {
540
+ const declaration = node['declaration'];
541
+ if (declaration === null)
542
+ return;
543
+ if (followed.has(declaration.type))
544
+ statement(declaration);
545
+ else
546
+ expression(declaration);
547
+ return;
548
+ }
549
+ case 'ClassDeclaration':
550
+ classEvaluation(node);
551
+ return;
552
+ default:
553
+ // A function declaration runs no code of its own here.
554
+ return;
555
+ }
556
+ }
557
+ /**
558
+ * A class's evaluation (ECMA-262 ClassDefinitionEvaluation): its heritage, its computed keys in order, then its
559
+ * static field initializers and static blocks in order.
560
+ */
561
+ function classEvaluation(node) {
562
+ const heritage = node['superClass'];
563
+ if (heritage !== null)
564
+ expression(heritage);
565
+ const elements = node['body']['body'];
566
+ for (const element of elements) {
567
+ if (element['computed'] === true)
568
+ expression(element['key']);
569
+ }
570
+ for (const element of elements) {
571
+ if (element.type === 'StaticBlock')
572
+ inOrder(element['body'], statement);
573
+ else if (element['static'] === true && element.type !== 'MethodDefinition') {
574
+ const value = element['value'];
575
+ if (value !== null)
576
+ expression(value);
577
+ }
578
+ }
579
+ }
580
+ function expression(node) {
581
+ if (!matters(node)) {
582
+ skip(node.start, node.end);
583
+ return;
584
+ }
585
+ if (node.type === 'Identifier') {
586
+ const read = keyOf(node);
587
+ if (state !== null && read !== undefined && read.scope === key.scope && read.name === key.name) {
588
+ const definitions = [...state.keys()].filter((each) => each !== 'start');
589
+ const narrowed = new Map([...state].filter(([, narrowings]) => narrowings.length > 0));
590
+ readsOrdered.set(node, {
591
+ start: state.has('start'),
592
+ definitions,
593
+ ordered: true,
594
+ ...(narrowed.size === 0 ? {} : { narrowed }),
595
+ });
596
+ }
597
+ return;
598
+ }
599
+ switch (node.type) {
600
+ case 'FunctionExpression':
601
+ case 'ArrowFunctionExpression':
602
+ return;
603
+ case 'ClassExpression':
604
+ classEvaluation(node);
605
+ return;
606
+ case 'SequenceExpression':
607
+ inOrder(node['expressions'], expression);
608
+ return;
609
+ case 'AssignmentExpression': {
610
+ const left = node['left'];
611
+ if (left.type === 'Identifier') {
612
+ // A compound assignment reads its variable before its right side (ECMA-262 13.15.2).
613
+ if (compoundOperator(node['operator']))
614
+ expression(left);
615
+ expression(node['right']);
616
+ }
617
+ else if (left.type === 'ObjectPattern' || left.type === 'ArrayPattern') {
618
+ expression(node['right']);
619
+ expression(left);
620
+ }
621
+ else {
622
+ expression(left);
623
+ expression(node['right']);
624
+ }
625
+ const operator = node['operator'];
626
+ if (operator === '||=' || operator === '&&=' || operator === '??=') {
627
+ const before = copy(state);
628
+ // `k ||= …` is a lazy init of `k`: where it assigns, `k` holds what it assigns, and elsewhere only the entry
629
+ // values that skip the assignment.
630
+ const definition = left.type === 'Identifier' && reads(left) ? ownAt.get(node) : undefined;
631
+ if (definition === undefined || state === null) {
632
+ defineAt(node);
633
+ state = join(before, state);
634
+ }
635
+ else {
636
+ const skipped = operator === '||=' ? 'truthy' : operator === '&&=' ? 'falsy' : 'notNullish';
637
+ state = join(narrow(before, skipped), new Map([[definition, []]]));
638
+ }
639
+ }
640
+ else
641
+ defineAt(node);
642
+ return;
643
+ }
644
+ case 'UpdateExpression':
645
+ defineAt(node);
646
+ return;
647
+ case 'ConditionalExpression': {
648
+ const test = node['test'];
649
+ const [whenTrue, whenFalse] = branches(test);
650
+ state = whenTrue;
651
+ const consequent = node['consequent'];
652
+ expression(consequent);
653
+ let taken = state;
654
+ state = whenFalse;
655
+ const alternate = node['alternate'];
656
+ expression(alternate);
657
+ lazily(test, consequent, alternate, taken, state, (a, b) => ((taken = a), (state = b)));
658
+ state = join(taken, state);
659
+ return;
660
+ }
661
+ case 'LogicalExpression': {
662
+ const left = node['left'];
663
+ expression(left);
664
+ let before = copy(state);
665
+ const right = node['right'];
666
+ expression(right);
667
+ // A lazy init (`k || (k = …)`): past it, the entry values that skipped the assignment passed the test.
668
+ if (assignsIn(state, right)) {
669
+ const operator = node['operator'];
670
+ const shown = tested(left);
671
+ const skipped = operator === '||'
672
+ ? shown
673
+ : operator === '&&'
674
+ ? shown === undefined
675
+ ? undefined
676
+ : opposite[shown]
677
+ : left.type === 'Identifier' && reads(left)
678
+ ? 'notNullish'
679
+ : undefined;
680
+ before = narrow(before, skipped);
681
+ }
682
+ state = join(before, state);
683
+ return;
684
+ }
685
+ default:
686
+ children(node);
687
+ if (runsCode.has(node.type) && foreign.length > 0)
688
+ add(foreign);
689
+ }
690
+ }
691
+ /**
692
+ * Walks a test, and gives what reaches the code it lets through when it is true and when it is false (ECMA-262
693
+ * 13.13.1): `a && b` is true only where `b` ran and was true, so a definition in every operand reaches the arm it
694
+ * guards, and `a || b` is false only where `b` ran and was false. `!` swaps the two. Any other test gives what
695
+ * reaches past it for both.
696
+ */
697
+ function branches(test) {
698
+ if (matters(test) && test.type === 'UnaryExpression' && test['operator'] === '!') {
699
+ const [whenTrue, whenFalse] = branches(test['argument']);
700
+ return [whenFalse, whenTrue];
701
+ }
702
+ const operator = test['operator'];
703
+ if (!matters(test) || test.type !== 'LogicalExpression' || (operator !== '&&' && operator !== '||')) {
704
+ expression(test);
705
+ return [state, copy(state)];
706
+ }
707
+ const left = test['left'];
708
+ const right = test['right'];
709
+ const [leftTrue, leftFalse] = branches(left);
710
+ const and = operator === '&&';
711
+ // The right side runs only where the left lets it through; elsewhere the left decides the test.
712
+ let decided = and ? leftFalse : leftTrue;
713
+ state = and ? leftTrue : leftFalse;
714
+ const [rightTrue, rightFalse] = branches(right);
715
+ // A lazy init in the right side (`k || (k = …)`): where the left decided, the entry values passed its test.
716
+ if (assignsIn(join(copy(rightTrue), rightFalse), right)) {
717
+ const shown = tested(left);
718
+ decided = narrow(decided, and ? (shown === undefined ? undefined : opposite[shown]) : shown);
719
+ }
720
+ return and ? [rightTrue, join(decided, rightFalse)] : [join(decided, rightTrue), rightFalse];
721
+ }
722
+ /** A node's children, in the order of its visitor keys, which is the order they are evaluated in. */
723
+ function children(node) {
724
+ const nodes = [];
725
+ walkTree(node, {
726
+ enter(child) {
727
+ if (child === node)
728
+ return;
729
+ nodes.push(child);
730
+ return false;
731
+ },
732
+ });
733
+ for (const child of nodes)
734
+ expression(child);
735
+ }
736
+ const body = owner.node['body'];
737
+ if (owner.node.type === 'Program') {
738
+ inOrder(body, statement);
739
+ }
740
+ else if (functionTypes.has(owner.node.type)) {
741
+ const block = body;
742
+ if (block.type === 'BlockStatement')
743
+ statement(block);
744
+ else
745
+ expression(block);
746
+ }
747
+ else
748
+ expression(owner.node);
749
+ return readsOrdered;
750
+ }
751
+ return { keyOf, of, reaching };
752
+ }
753
+ /** The first of `list` (sorted) at or after `start`. */
754
+ function firstFrom(list, start) {
755
+ let low = 0;
756
+ let high = list.length;
757
+ while (low < high) {
758
+ const middle = (low + high) >> 1;
759
+ if (list[middle] < start)
760
+ low = middle + 1;
761
+ else
762
+ high = middle;
763
+ }
764
+ return list[low];
765
+ }
766
+ /** The identifiers a binding or assignment pattern binds, in source order. */
767
+ export function identifiersOf(pattern) {
768
+ switch (pattern.type) {
769
+ case 'Identifier':
770
+ return [pattern];
771
+ case 'ObjectPattern':
772
+ return pattern['properties'].flatMap((property) => identifiersOf((property.type === 'RestElement' ? property['argument'] : property['value'])));
773
+ case 'ArrayPattern':
774
+ return pattern['elements'].flatMap((element) => (element === null ? [] : identifiersOf(element)));
775
+ case 'AssignmentPattern':
776
+ return identifiersOf(pattern['left']);
777
+ case 'RestElement':
778
+ return identifiersOf(pattern['argument']);
779
+ default:
780
+ return [];
781
+ }
782
+ }