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,1060 @@
1
+ // Module links inside one artifact (§6, ADR 0003): the webpack shapes of the module-linking idioms, and, in
2
+ // `hoistedLinks` below, the shapes esbuild, Rollup and Rolldown emit (memoized getters, lazy module thunks, their getter
3
+ // export definers and interop wrappers).
4
+ //
5
+ // - **Factory table + require**: an object or array literal of factories keyed by constant id (webpack 5 object
6
+ // tables, webpack 4-style arrays), read by a require function, a function whose own code calls `T[id](…)` or
7
+ // `T[id].call(…)` with its first parameter as the key and passes itself on. The require is that function, and the
8
+ // parameter each factory takes it as (its third).
9
+ // - **Getter export definer**: `R.d(exports, { x: () => l })`, its array form `R.d(exports, ["x", () => l, "y", 0, v])`
10
+ // (a `0` marks a value), and the older `R.d(exports, "x", () => l)`, where `R` is the require.
11
+ // - **Interop wrappers**: `R.n(m)` and `R.cw(factory)`. `R.cw`'s thunk is also a lazy module thunk, which `hoistedLinks`
12
+ // recognizes by its shape (below), so each `var x = R.cw(factory)` is a module.
13
+ //
14
+ // Links are recovered from structure, never from knowing which bundler produced the code. The require and the table are
15
+ // followed by the evaluator's general call and heap semantics: a require call runs its factory through the table's
16
+ // computed read. The definer is modeled here: each of its calls defines its exports' fields, getters or values, and
17
+ // the defines the definer's own body makes are left to the model. What comes through a definer's export carries a
18
+ // `module-link` fold (§14), with its module's id and the export's name. Module ids are build-local, so they appear only
19
+ // in the trace, never in identity (§6).
20
+ //
21
+ // A require call whose id isn't a constant, or names no factory in the table, is a broken link (§6 Degradation). A call
22
+ // through what a broken link returns (`R(id)(…)`, `R(id).x(…)`, `m.x(…)` with `m = R(id)`) is an unlinked module call.
23
+ // An unrecognized runtime looks like ordinary code, and gives nothing here.
24
+ import { calleeExpression, walkTree } from "./ast.js";
25
+ import { shadowedNameOf } from "./definitions.js";
26
+ import { registriesOf } from "./chunk-registry.js";
27
+ import { literalKey } from "./keys.js";
28
+ const none = {
29
+ idioms: new Set(),
30
+ tables: false,
31
+ modules: 0,
32
+ defines: [],
33
+ definers: new Set(),
34
+ links: new Map(),
35
+ firsts: new Map(),
36
+ factories: new Map(),
37
+ broken: [],
38
+ unlinkedCalls: new Map(),
39
+ moduleCalls: new Map(),
40
+ namespaces: new Map(),
41
+ factoryCalls: new Map(),
42
+ makes: new Map(),
43
+ chunkFactories: new Map(),
44
+ pushCallbacks: new Map(),
45
+ starts: new Map(),
46
+ registries: new Set(),
47
+ pushed: 0,
48
+ loader: false,
49
+ };
50
+ const functionTypes = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
51
+ const isFunction = (node) => functionTypes.has(node.type);
52
+ const sameKey = (a, b) => a !== undefined && b !== undefined && a.scope === b.scope && a.name === b.name;
53
+ /** The module-linking idioms of a program (above). */
54
+ export function moduleLinks(program, definitions) {
55
+ const tables = tableLinks(program, definitions);
56
+ const hoisted = hoistedLinks(program, definitions);
57
+ if (hoisted.idioms.size === 0)
58
+ return tables;
59
+ return {
60
+ ...tables,
61
+ idioms: new Set([...tables.idioms, ...hoisted.idioms]),
62
+ modules: tables.modules + hoisted.modules,
63
+ defines: [...tables.defines, ...hoisted.defines].sort((a, b) => a.site.start - b.site.start),
64
+ definers: new Set([...tables.definers, ...hoisted.definers]),
65
+ moduleCalls: hoisted.moduleCalls,
66
+ namespaces: hoisted.namespaces,
67
+ factoryCalls: hoisted.factoryCalls,
68
+ makes: hoisted.makes,
69
+ };
70
+ }
71
+ /** The factory tables' idioms: a table and its require, the require's definer, and its interop wrappers (above). */
72
+ function tableLinks(program, definitions) {
73
+ const parents = new Map();
74
+ const functions = [];
75
+ walkTree(program, {
76
+ enter(node, parent) {
77
+ if (parent !== null)
78
+ parents.set(node, parent);
79
+ if (isFunction(node))
80
+ functions.push(node);
81
+ },
82
+ });
83
+ // The chunk registries (§6 idiom 6): what the program pushes into them, and its loaders' reads of them. A loader's
84
+ // table can start empty, as a runtime chunk's does (`var e = {}`): its chunks push all its factories.
85
+ const { pushes, loads } = registriesOf(program);
86
+ const empty = loads.length > 0;
87
+ // The require functions, each with its table.
88
+ const requires = [];
89
+ for (const fn of functions) {
90
+ if (fn.type !== 'FunctionDeclaration')
91
+ continue;
92
+ const id = fn['id'];
93
+ const [param] = fn['params'];
94
+ if (id === null || param?.type !== 'Identifier')
95
+ continue;
96
+ const table = tableRead(fn, param['name'], id['name']);
97
+ if (table === undefined)
98
+ continue;
99
+ const key = referenceKey(fn, id['name']);
100
+ if (key !== undefined)
101
+ requires.push({ fn, key, table });
102
+ }
103
+ const registry = {
104
+ registries: new Set([...pushes.map((push) => push.registry), ...loads.map((load) => load.registry)]),
105
+ pushed: pushes.reduce((sum, push) => sum + push.factories.length, 0),
106
+ loader: loads.length > 0,
107
+ };
108
+ if (requires.length === 0) {
109
+ // A `.cw` wrapper with no table, over a concatenated CommonJS module: `r.cw(function (t, e) { … })`.
110
+ const idioms = new Set(wraps() ? ['interop'] : []);
111
+ if (registry.loader)
112
+ idioms.add('chunk-loader');
113
+ return { ...none, ...registry, idioms, tables: idioms.has('interop') };
114
+ }
115
+ // Each factory's module id, and the key its require parameter has.
116
+ const factoryIds = new Map();
117
+ const factories = new Map();
118
+ const requireKeys = requires.map(({ fn, key, table }) => ({
119
+ key,
120
+ fn,
121
+ table,
122
+ }));
123
+ let modules = 0;
124
+ for (const { fn, table } of requires) {
125
+ for (const [id, factory] of table.factories) {
126
+ modules++;
127
+ factoryIds.set(factory, id);
128
+ factories.set(factory, { module: id, require: fn, call: table.call });
129
+ const param = factory['params'][2];
130
+ if (param?.type !== 'Identifier')
131
+ continue;
132
+ const key = referenceKey(factory, param['name']);
133
+ if (key !== undefined)
134
+ requireKeys.push({ key, fn, table });
135
+ }
136
+ }
137
+ // The factories chunks push into a registry a loader reads, joined to the table of the require around the loader: the
138
+ // innermost require whose enclosing code holds the loader's read of the registry.
139
+ const chunkFactories = new Map();
140
+ const pushCallbacks = new Map();
141
+ const chunked = new Map();
142
+ for (const push of pushes) {
143
+ const load = loads.find((each) => each.registry === push.registry);
144
+ if (load === undefined)
145
+ continue;
146
+ let found;
147
+ for (const { fn, table } of requires) {
148
+ let owner = program;
149
+ for (let at = parents.get(fn); at !== undefined; at = parents.get(at)) {
150
+ if (isFunction(at)) {
151
+ owner = at;
152
+ break;
153
+ }
154
+ }
155
+ if (owner.start > load.read.start || load.read.end > owner.end)
156
+ continue;
157
+ if (found === undefined || owner.start > found.owner.start)
158
+ found = { fn, table, owner };
159
+ }
160
+ if (found === undefined)
161
+ continue;
162
+ let byId = chunked.get(found.table);
163
+ if (byId === undefined) {
164
+ chunked.set(found.table, (byId = new Map()));
165
+ chunkFactories.set(found.table.literal, byId);
166
+ }
167
+ // The callback's first parameter is the require: `t => t(t.s = 4429)`.
168
+ const callback = push.callback;
169
+ const first = callback === undefined ? undefined : callback['params'][0];
170
+ if (callback !== undefined && first?.type === 'Identifier') {
171
+ pushCallbacks.set(callback, push.call);
172
+ const key = referenceKey(callback, first['name']);
173
+ if (key !== undefined)
174
+ requireKeys.push({ key, fn: found.fn, table: found.table, callback: true });
175
+ }
176
+ for (const { id, factory } of push.factories) {
177
+ const list = byId.get(id);
178
+ if (list === undefined)
179
+ byId.set(id, [factory]);
180
+ else
181
+ list.push(factory);
182
+ factoryIds.set(factory, id);
183
+ factories.set(factory, { module: id, require: found.fn, call: found.table.call });
184
+ const param = factory['params'][2];
185
+ if (param?.type !== 'Identifier')
186
+ continue;
187
+ const key = referenceKey(factory, param['name']);
188
+ if (key !== undefined)
189
+ requireKeys.push({ key, fn: found.fn, table: found.table });
190
+ }
191
+ }
192
+ /** Whether a require's table, or a chunk pushed into it, has a factory of module `id`. */
193
+ const linksTo = (table, id) => table.factories.has(id) || (chunked.get(table)?.has(id) ?? false);
194
+ /** The require function a name is, with its table. */
195
+ const requireOf = (node) => {
196
+ if (node.type !== 'Identifier')
197
+ return undefined;
198
+ const key = definitions.keyOf(node);
199
+ return requireKeys.find((each) => sameKey(each.key, key));
200
+ };
201
+ /** The module a node is in: the id of the factory around it. */
202
+ const moduleOf = (node) => {
203
+ for (let at = node; at !== undefined; at = parents.get(at)) {
204
+ const id = factoryIds.get(at);
205
+ if (id !== undefined)
206
+ return id;
207
+ }
208
+ return undefined;
209
+ };
210
+ const idioms = new Set();
211
+ const defines = [];
212
+ const definers = new Set();
213
+ const links = new Map();
214
+ const firsts = new Map();
215
+ const broken = [];
216
+ const brokenCalls = new Map();
217
+ const brokenBinds = new Map();
218
+ const starts = new Map();
219
+ walkTree(program, {
220
+ enter(node) {
221
+ if (node.type === 'AssignmentExpression' && node['operator'] === '=') {
222
+ // The definer: `R.d = function…`.
223
+ const left = node['left'];
224
+ const right = node['right'];
225
+ if (member(left, 'd') && requireOf(left['object']) !== undefined && isFunction(right))
226
+ definers.add(right);
227
+ return;
228
+ }
229
+ if (node.type !== 'CallExpression')
230
+ return;
231
+ const callee = calleeExpression(node['callee']);
232
+ const args = node['arguments'];
233
+ const require = requireOf(callee);
234
+ if (require !== undefined) {
235
+ const { fn, table } = require;
236
+ // A require call.
237
+ // webpack 4's entry passes its id through an assignment: `n(n.s = 5)`.
238
+ let [arg] = args;
239
+ while (arg?.type === 'AssignmentExpression' && arg['operator'] === '=')
240
+ arg = arg['right'];
241
+ const id = arg === undefined ? undefined : literalKey(arg);
242
+ if (id === undefined) {
243
+ const link = { call: node, why: 'non-constant' };
244
+ broken.push(link);
245
+ brokenCalls.set(node, link);
246
+ }
247
+ else if (!linksTo(table, id)) {
248
+ const link = { call: node, why: 'missing' };
249
+ broken.push(link);
250
+ brokenCalls.set(node, link);
251
+ }
252
+ else {
253
+ idioms.add('factory-table');
254
+ links.set(node, { idiom: 'factory-table', module: id });
255
+ if (require.callback === true)
256
+ starts.set(node, fn);
257
+ let own = firsts.get(fn);
258
+ if (own === undefined)
259
+ firsts.set(fn, (own = new Map()));
260
+ if (!own.has(id))
261
+ own.set(id, node);
262
+ }
263
+ return;
264
+ }
265
+ if (callee.type !== 'MemberExpression' || requireOf(callee['object']) === undefined)
266
+ return;
267
+ // A require bound to a module's id, which a loader's promise runs: `n.bind(n, 6954)`.
268
+ if (member(callee, 'bind') && args.length === 2) {
269
+ const id = literalKey(args[1]);
270
+ const { table } = requireOf(callee['object']);
271
+ if (id === undefined || !linksTo(table, id)) {
272
+ const link = { call: node, why: id === undefined ? 'non-constant' : 'missing' };
273
+ broken.push(link);
274
+ brokenBinds.set(node, link);
275
+ }
276
+ return;
277
+ }
278
+ if (member(callee, 'n') || member(callee, 'cw')) {
279
+ idioms.add('interop');
280
+ return;
281
+ }
282
+ if (!member(callee, 'd') || args.some((arg) => arg.type === 'SpreadElement'))
283
+ return;
284
+ const [object, list, getter] = args;
285
+ if (object === undefined || list === undefined)
286
+ return;
287
+ const module = moduleOf(node);
288
+ const add = (key, part) => {
289
+ if (key === undefined)
290
+ return;
291
+ idioms.add('getter-export');
292
+ const link = module === undefined ? undefined : { idiom: 'getter-export', module, export: key };
293
+ defines.push({ site: node, object, key, ...part, ...(link === undefined ? {} : { link }) });
294
+ };
295
+ if (list.type === 'ObjectExpression') {
296
+ for (const property of list['properties']) {
297
+ if (property.type !== 'Property' || property['kind'] !== 'init')
298
+ continue;
299
+ const key = property['key'];
300
+ const name = property['computed'] === true ? literalKey(key) : key.type === 'Identifier' ? key['name'] : literalKey(key);
301
+ add(name, { getter: property['value'] });
302
+ }
303
+ }
304
+ else if (list.type === 'ArrayExpression') {
305
+ const elements = list['elements'];
306
+ for (let at = 0; at + 1 < elements.length;) {
307
+ const name = elements[at] === null ? undefined : literalKey(elements[at]);
308
+ const second = elements[at + 1];
309
+ if (name === undefined || second === null || second === undefined || second.type === 'SpreadElement')
310
+ break;
311
+ if (second.type === 'Literal' && second['value'] === 0) {
312
+ const value = elements[at + 2];
313
+ if (value === null || value === undefined || value.type === 'SpreadElement')
314
+ break;
315
+ add(name, { value });
316
+ at += 3;
317
+ }
318
+ else {
319
+ add(name, { getter: second });
320
+ at += 2;
321
+ }
322
+ }
323
+ }
324
+ else if (getter !== undefined && getter.type !== 'SpreadElement') {
325
+ add(literalKey(list), { getter });
326
+ }
327
+ },
328
+ });
329
+ // The calls through what a broken link returns.
330
+ const unlinkedCalls = new Map();
331
+ if (brokenCalls.size > 0 || brokenBinds.size > 0) {
332
+ // What a broken link gives flows through a loader's promise (§6 idiom 6): `n.e(5).then(n.bind(n, 6954))` and
333
+ // `n.e(5).then(() => n(6954))` are promises of the module, which `.then(m => …)` passes on as `m`, and `await`
334
+ // unwraps.
335
+ const promised = new Map();
336
+ // The identifiers whose definitions are being followed. Declared before the first walk, which follows them too.
337
+ const following = new Set();
338
+ walkTree(program, {
339
+ enter(node) {
340
+ if (node.type !== 'CallExpression')
341
+ return;
342
+ const callee = calleeExpression(node['callee']);
343
+ const [handler] = node['arguments'];
344
+ if (!member(callee, 'then') || handler === undefined)
345
+ return;
346
+ const link = brokenFunction(handler);
347
+ if (link !== undefined)
348
+ promised.set(node, link);
349
+ },
350
+ });
351
+ /** The broken link a promise is of. */
352
+ function brokenPromise(node) {
353
+ const found = promised.get(node);
354
+ if (found !== undefined)
355
+ return found;
356
+ if (node.type !== 'Identifier')
357
+ return undefined;
358
+ return defined(node, brokenPromise);
359
+ }
360
+ /** The broken link a function gives: a bound require, or a function that returns a broken module. */
361
+ function brokenFunction(node) {
362
+ const bound = brokenBinds.get(node);
363
+ if (bound !== undefined)
364
+ return bound;
365
+ if (node.type !== 'ArrowFunctionExpression' || node['expression'] !== true)
366
+ return undefined;
367
+ return brokenValue(node['body']);
368
+ }
369
+ /** The broken link whose module a value is. */
370
+ function brokenValue(node) {
371
+ const found = brokenCalls.get(node);
372
+ if (found !== undefined)
373
+ return found;
374
+ if (node.type === 'AwaitExpression')
375
+ return brokenPromise(node['argument']);
376
+ if (node.type !== 'Identifier')
377
+ return undefined;
378
+ const key = definitions.keyOf(node);
379
+ if (key === undefined)
380
+ return undefined;
381
+ // A parameter of a handler that a broken module's promise passes it to: `.then(m => …)`.
382
+ const fn = key.scope === 'global' ? undefined : key.scope;
383
+ if (fn !== undefined && isFunction(fn)) {
384
+ const [first] = fn['params'];
385
+ const call = parents.get(fn);
386
+ if (first?.type === 'Identifier' && first['name'] === key.name && call?.type === 'CallExpression') {
387
+ const callee = calleeExpression(call['callee']);
388
+ if (call['arguments'][0] === fn && member(callee, 'then')) {
389
+ const link = brokenPromise(callee['object']);
390
+ if (link !== undefined)
391
+ return link;
392
+ }
393
+ }
394
+ }
395
+ return defined(node, brokenValue);
396
+ }
397
+ /** The broken link of the value an identifier's definitions give, as `of` finds it. */
398
+ function defined(id, of) {
399
+ const key = definitions.keyOf(id);
400
+ // A variable defined through itself gives nothing more.
401
+ if (key === undefined || following.has(id))
402
+ return undefined;
403
+ following.add(id);
404
+ try {
405
+ for (const definition of definitions.of(key)) {
406
+ const source = definition.source;
407
+ if (source !== undefined && definition.pattern.type === 'Identifier') {
408
+ const link = of(source);
409
+ if (link !== undefined)
410
+ return link;
411
+ }
412
+ }
413
+ return undefined;
414
+ }
415
+ finally {
416
+ following.delete(id);
417
+ }
418
+ }
419
+ walkTree(program, {
420
+ enter(node) {
421
+ if (node.type !== 'CallExpression')
422
+ return;
423
+ let callee = calleeExpression(node['callee']);
424
+ if (callee.type === 'MemberExpression')
425
+ callee = callee['object'];
426
+ const link = brokenValue(callee);
427
+ if (link !== undefined && link.call !== node)
428
+ unlinkedCalls.set(node, link);
429
+ },
430
+ });
431
+ }
432
+ const tables = idioms.size > 0;
433
+ if (registry.loader)
434
+ idioms.add('chunk-loader');
435
+ return {
436
+ ...none,
437
+ ...registry,
438
+ idioms,
439
+ tables,
440
+ modules,
441
+ defines,
442
+ definers,
443
+ links,
444
+ firsts,
445
+ factories,
446
+ broken,
447
+ unlinkedCalls,
448
+ chunkFactories,
449
+ pushCallbacks,
450
+ starts,
451
+ };
452
+ /** Whether the program calls a `.cw` wrapper on a CommonJS factory: `x.cw(function (t, e) { … })`. */
453
+ function wraps() {
454
+ let found = false;
455
+ walkTree(program, {
456
+ enter(node) {
457
+ if (found)
458
+ return false;
459
+ if (node.type !== 'CallExpression')
460
+ return undefined;
461
+ const [factory, ...rest] = node['arguments'];
462
+ if (!member(calleeExpression(node['callee']), 'cw') || rest.length > 0 || factory === undefined)
463
+ return undefined;
464
+ found = isFunction(factory) && factory['params'].length >= 1;
465
+ return undefined;
466
+ },
467
+ });
468
+ return found;
469
+ }
470
+ /**
471
+ * The table a function reads with its parameter `param` as the key in a call that passes the function `self` on:
472
+ * `T[param](…, self)` or `T[param].call(…, self)`.
473
+ */
474
+ function tableRead(fn, param, self) {
475
+ let found;
476
+ walkTree(fn['body'], {
477
+ enter(node) {
478
+ if (found !== undefined || isFunction(node))
479
+ return false;
480
+ if (node.type !== 'CallExpression')
481
+ return undefined;
482
+ const args = node['arguments'];
483
+ if (!args.some((arg) => arg.type === 'Identifier' && arg['name'] === self))
484
+ return undefined;
485
+ let callee = node['callee'];
486
+ if (member(callee, 'call'))
487
+ callee = callee['object'];
488
+ if (callee.type !== 'MemberExpression' || callee['computed'] !== true)
489
+ return undefined;
490
+ const key = callee['property'];
491
+ const object = callee['object'];
492
+ if (key.type !== 'Identifier' || key['name'] !== param || object.type !== 'Identifier')
493
+ return undefined;
494
+ found = tableOf(object);
495
+ if (found !== undefined)
496
+ found = { ...found, call: node };
497
+ return undefined;
498
+ },
499
+ });
500
+ return found;
501
+ }
502
+ /** The factory table a name holds: a declarator's literal, or the literal an IIFE passes the parameter it is. */
503
+ function tableOf(id) {
504
+ const key = definitions.keyOf(id);
505
+ if (key === undefined)
506
+ return undefined;
507
+ for (const definition of definitions.of(key)) {
508
+ if (definition.source !== undefined && definition.pattern.type === 'Identifier') {
509
+ const table = factoryTable(definition.source, empty);
510
+ if (table !== undefined)
511
+ return table;
512
+ }
513
+ }
514
+ // A parameter of a function called at once with the table: webpack 4's `!function(e){…}([…])`.
515
+ const fn = key.scope === 'global' ? undefined : key.scope;
516
+ if (fn === undefined || !isFunction(fn))
517
+ return undefined;
518
+ const index = fn['params'].findIndex((param) => param.type === 'Identifier' && param['name'] === key.name);
519
+ let call = parents.get(fn);
520
+ while (call?.type === 'ParenthesizedExpression')
521
+ call = parents.get(call);
522
+ if (index < 0 || call?.type !== 'CallExpression' || calleeExpression(call['callee']) !== fn)
523
+ return undefined;
524
+ const arg = call['arguments'][index];
525
+ return arg === undefined ? undefined : factoryTable(arg, empty);
526
+ }
527
+ /** The key a name has in the scope of `fn`, from a reference to it in `fn`'s own code. */
528
+ function referenceKey(fn, name) {
529
+ let key;
530
+ walkTree(fn['body'], {
531
+ enter(node, parent) {
532
+ if (key !== undefined)
533
+ return false;
534
+ if (node.type !== 'Identifier' || node['name'] !== name)
535
+ return undefined;
536
+ // Not a property name.
537
+ if (parent?.type === 'MemberExpression' && parent['property'] === node && parent['computed'] !== true)
538
+ return undefined;
539
+ if (parent?.type === 'Property' && parent['key'] === node && parent['shorthand'] !== true)
540
+ return undefined;
541
+ const found = definitions.keyOf(node);
542
+ if (found !== undefined && (found.scope === fn || fn.type === 'FunctionDeclaration'))
543
+ key = found;
544
+ return undefined;
545
+ },
546
+ });
547
+ return key;
548
+ }
549
+ }
550
+ /**
551
+ * A factory table (above): an object literal whose values are all functions, or an array literal of functions. It is
552
+ * empty only when `empty` allows, for a loader's table.
553
+ */
554
+ function factoryTable(literal, empty = false) {
555
+ const factories = new Map();
556
+ if (literal.type === 'ObjectExpression') {
557
+ for (const property of literal['properties']) {
558
+ if (property.type !== 'Property' || property['kind'] !== 'init' || property['computed'] === true)
559
+ return undefined;
560
+ const key = property['key'];
561
+ const id = key.type === 'Identifier' ? key['name'] : literalKey(key);
562
+ const value = property['value'];
563
+ if (id === undefined || !isFunction(value))
564
+ return undefined;
565
+ factories.set(id, value);
566
+ }
567
+ }
568
+ else if (literal.type === 'ArrayExpression') {
569
+ for (const [index, element] of literal['elements'].entries()) {
570
+ if (element === null)
571
+ continue;
572
+ if (!isFunction(element))
573
+ return undefined;
574
+ factories.set(String(index), element);
575
+ }
576
+ }
577
+ else
578
+ return undefined;
579
+ return factories.size === 0 && !empty ? undefined : { literal, factories };
580
+ }
581
+ /** Whether a node is `o.name`, spelled with a dot. */
582
+ function member(node, name) {
583
+ if (node.type !== 'MemberExpression' || node['computed'] === true)
584
+ return false;
585
+ const property = node['property'];
586
+ return property.type === 'Identifier' && property['name'] === name;
587
+ }
588
+ /**
589
+ * The idioms of code whose modules are hoisted into one scope, each module run by a function of its own (§6):
590
+ *
591
+ * - **Lazy module thunk**: a maker `(cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports)`,
592
+ * whose thunk calls its factory with a fresh `{ exports }`, as esbuild's `__commonJS` and Rolldown's `__commonJSMin`
593
+ * are, or `(fn, res) => () => (fn && (res = fn((fn = 0))), res)`, the variant that only runs init, as esbuild's
594
+ * `__esm` is. A `try` around the call, or other code beside it, changes nothing. Each call of the maker with a factory
595
+ * is a module, named by the variable its thunk goes to, and each call of that thunk runs it. The module object can
596
+ * be a variable the maker declares rather than a parameter, and the thunk can call its factory through a variable
597
+ * that holds it, with `.call`: webpack's `R.cw = t => { var e; return () => { if (t) { var r = t; t = 0, e = {
598
+ * exports: {} }, r.call(e.exports, e, e.exports) } return e.exports } }`, whose factories take `(module, exports)`. A
599
+ * maker can be set on an object a variable holds (`R.cw = …`, `{ cw: … }`), and is then called through it.
600
+ * - **Memoized getter**: a function with no parameters that first tests a flag declared outside it, returning a value
601
+ * when it is set, and sets the flag to `1` or `true` (Rollup commonjs's `requireX`: `if (f) return x; f = 1; …`, `return f ? x : ((f = 1), (x = …))`). It is a
602
+ * module, named by its name, and each call of it runs it.
603
+ * - **Getter export definer**: a function whose own code runs `for (k in all) define(target, k, { get: all[k] })`
604
+ * over its parameter `all`, where `target` is its other parameter (esbuild's `__export(target, all)`), or a fresh
605
+ * object it returns (Rolldown's `__exportAll(all)`). Each call with an object literal of getters defines its exports,
606
+ * of a module named by the target's variable, or by the variable the returned object goes to.
607
+ * - **Interop wrappers**: a function that reads its first parameter's `__esModule`, and either defines `"default"` with
608
+ * that parameter as its value (`__toESM`, which makes a namespace object of a CommonJS module) or reads its `default`
609
+ * (`getDefaultExportFromCjs`). A call of one counts, and links when its argument is a call that runs a module.
610
+ *
611
+ * A module's name is build-local, like a module id: it appears in the trace, never in identity.
612
+ */
613
+ function hoistedLinks(program, definitions) {
614
+ const parents = new Map();
615
+ const functions = [];
616
+ const calls = [];
617
+ walkTree(program, {
618
+ enter(node, parent) {
619
+ if (parent !== null)
620
+ parents.set(node, parent);
621
+ if (isFunction(node))
622
+ functions.push(node);
623
+ else if (node.type === 'CallExpression')
624
+ calls.push(node);
625
+ },
626
+ });
627
+ /** Whether `node` is a reference to the parameter `name` of `fn`. */
628
+ const isParam = (node, fn, name) => node?.type === 'Identifier' && node['name'] === name && definitions.keyOf(node)?.scope === fn;
629
+ const paramNames = (fn) => fn['params'].map((param) => (param.type === 'Identifier' ? param['name'] : undefined));
630
+ /** The variable a value goes to: a declarator's, or a plain assignment's. */
631
+ const bindingOf = (value) => {
632
+ const parent = parents.get(value);
633
+ if (parent?.type === 'VariableDeclarator' && parent['init'] === value) {
634
+ const id = parent['id'];
635
+ return id.type === 'Identifier' ? id : undefined;
636
+ }
637
+ if (parent?.type === 'AssignmentExpression' && parent['right'] === value && parent['operator'] === '=') {
638
+ const left = parent['left'];
639
+ return left.type === 'Identifier' ? left : undefined;
640
+ }
641
+ return undefined;
642
+ };
643
+ /** The name a function is called by, with a test of whether a callee's variable is that name. */
644
+ const nameOf = (fn) => {
645
+ if (fn.type === 'FunctionDeclaration') {
646
+ const id = fn['id'];
647
+ if (id === null)
648
+ return undefined;
649
+ const name = id['name'];
650
+ // A function declaration's calls resolve to the declaration itself, or, at a classic script's top level, to the
651
+ // global object's property it binds.
652
+ const topLevel = [...ancestors(fn)].every((each) => !isFunction(each));
653
+ // Where a parameter shadows the name, a call outside the function is keyed by its `id` (`shadowedNameOf`).
654
+ return {
655
+ name,
656
+ is: (key) => key?.name === name &&
657
+ (key.scope === fn || shadowedNameOf(key.scope) === fn || (key.scope === 'global' && topLevel)),
658
+ };
659
+ }
660
+ const id = bindingOf(fn);
661
+ if (id === undefined)
662
+ return undefined;
663
+ const own = definitions.keyOf(id);
664
+ return own === undefined ? undefined : { name: id['name'], is: (key) => sameKey(key, own) };
665
+ };
666
+ function* ancestors(node) {
667
+ for (let at = parents.get(node); at !== undefined; at = parents.get(at))
668
+ yield at;
669
+ }
670
+ /** The calls whose callee is a variable `is` accepts, past `(0, f)`. */
671
+ const callsOf = (is) => calls.filter((call) => {
672
+ const callee = calleeExpression(call['callee']);
673
+ return callee.type === 'Identifier' && is(definitions.keyOf(callee));
674
+ });
675
+ /**
676
+ * The calls of a function set as a property of an object a variable holds, through that variable: `R.cw(…)` after
677
+ * `R.cw = fn`, or after `const R = { cw: fn }`.
678
+ */
679
+ const memberCallsOf = (fn) => {
680
+ const parent = parents.get(fn);
681
+ let holder;
682
+ let name;
683
+ if (parent?.type === 'AssignmentExpression' && parent['right'] === fn && parent['operator'] === '=') {
684
+ const left = parent['left'];
685
+ const object = left['object'];
686
+ if (left.type !== 'MemberExpression' || left['computed'] === true || object?.type !== 'Identifier')
687
+ return undefined;
688
+ holder = object;
689
+ name = left['property']['name'];
690
+ }
691
+ else if (parent?.type === 'Property' && parent['value'] === fn && parent['kind'] === 'init' && parent['computed'] !== true) {
692
+ const literal = parents.get(parent);
693
+ holder = literal === undefined ? undefined : bindingOf(literal);
694
+ name = propertyName(parent);
695
+ }
696
+ const key = holder === undefined ? undefined : definitions.keyOf(holder);
697
+ if (key === undefined || name === undefined)
698
+ return undefined;
699
+ return calls.filter((call) => {
700
+ const callee = calleeExpression(call['callee']);
701
+ if (!member(callee, name))
702
+ return false;
703
+ const object = callee['object'];
704
+ return object.type === 'Identifier' && sameKey(definitions.keyOf(object), key);
705
+ });
706
+ };
707
+ const idioms = new Set();
708
+ const moduleCalls = new Map();
709
+ const defines = [];
710
+ const definers = new Set();
711
+ const namespaces = new Map();
712
+ const factoryCalls = new Map();
713
+ const makes = new Map();
714
+ let modules = 0;
715
+ for (const fn of functions) {
716
+ const maker = thunkMaker(fn);
717
+ const kind = maker === undefined ? (memoizedGetter(fn) ? 'getter' : undefined) : 'thunk';
718
+ if (kind === undefined)
719
+ continue;
720
+ const named = nameOf(fn);
721
+ // A thunk maker can be set on an object instead (`R.cw = t => …`), and called through it (`R.cw(factory)`).
722
+ const made = named !== undefined ? callsOf(named.is) : maker !== undefined ? memberCallsOf(fn) : undefined;
723
+ if (made === undefined)
724
+ continue;
725
+ if (maker !== undefined)
726
+ factoryCalls.set(maker.call, maker.factoryCall);
727
+ if (kind === 'getter') {
728
+ if (named === undefined)
729
+ continue;
730
+ // A memoized getter is itself the module.
731
+ modules++;
732
+ idioms.add('memoized-getter');
733
+ for (const call of callsOf(named.is)) {
734
+ if (call['arguments'].length === 0)
735
+ moduleCalls.set(call, { idiom: 'memoized-getter', module: named.name });
736
+ }
737
+ continue;
738
+ }
739
+ // Each call of a thunk maker with a factory is a module, whose thunk runs it.
740
+ for (const make of made) {
741
+ const [factory] = make['arguments'];
742
+ if (factory === undefined || !isFunction(factory))
743
+ continue;
744
+ const thunk = bindingOf(make);
745
+ const key = thunk === undefined ? undefined : definitions.keyOf(thunk);
746
+ if (key === undefined)
747
+ continue;
748
+ makes.set(make, fn);
749
+ modules++;
750
+ idioms.add('lazy-thunk');
751
+ const module = thunk['name'];
752
+ for (const call of callsOf((each) => sameKey(each, key))) {
753
+ if (call['arguments'].length === 0)
754
+ moduleCalls.set(call, { idiom: 'lazy-thunk', module });
755
+ }
756
+ }
757
+ }
758
+ for (const fn of functions) {
759
+ const definer = exportDefiner(fn);
760
+ if (definer === undefined)
761
+ continue;
762
+ const named = nameOf(fn);
763
+ if (named === undefined)
764
+ continue;
765
+ definers.add(fn);
766
+ for (const call of callsOf(named.is)) {
767
+ const args = call['arguments'];
768
+ const list = args[definer.list];
769
+ const object = definer.target === undefined ? call : args[definer.target];
770
+ if (list?.type !== 'ObjectExpression' || object === undefined || object.type === 'SpreadElement')
771
+ continue;
772
+ const holder = definer.target === undefined ? bindingOf(call) : object.type === 'Identifier' ? object : undefined;
773
+ const module = holder === undefined ? undefined : holder['name'];
774
+ for (const property of list['properties']) {
775
+ if (property.type !== 'Property' || property['kind'] !== 'init')
776
+ continue;
777
+ const key = property['key'];
778
+ const name = property['computed'] === true ? literalKey(key) : key.type === 'Identifier' ? key['name'] : literalKey(key);
779
+ if (name === undefined)
780
+ continue;
781
+ idioms.add('getter-export');
782
+ const link = module === undefined ? undefined : { idiom: 'getter-export', module, export: name };
783
+ defines.push({ site: call, object, key: name, getter: property['value'], ...(link === undefined ? {} : { link }) });
784
+ }
785
+ }
786
+ }
787
+ for (const fn of functions) {
788
+ const wrapper = interopWrapper(fn);
789
+ if (wrapper === undefined)
790
+ continue;
791
+ const named = nameOf(fn);
792
+ if (named === undefined)
793
+ continue;
794
+ if (wrapper === 'namespace')
795
+ definers.add(fn);
796
+ for (const call of callsOf(named.is)) {
797
+ const [arg] = call['arguments'];
798
+ if (arg === undefined || arg.type === 'SpreadElement')
799
+ continue;
800
+ idioms.add('interop');
801
+ if (wrapper === 'namespace')
802
+ namespaces.set(call, arg);
803
+ const inner = moduleCalls.get(arg);
804
+ if (inner !== undefined)
805
+ moduleCalls.set(call, { idiom: 'interop', module: inner.module });
806
+ }
807
+ }
808
+ return { ...none, idioms, modules, defines, definers, moduleCalls, namespaces, factoryCalls, makes };
809
+ /** Whether a variable is declared in a function's code: its parameters', its body's, or a nested scope's there. */
810
+ function within(key, fn) {
811
+ return key.scope !== 'global' && fn.start <= key.scope.start && key.scope.end <= fn.end;
812
+ }
813
+ /** Whether a function is a lazy module thunk maker (above), and which, with its thunk's call of its factory. */
814
+ function thunkMaker(fn) {
815
+ const [cb] = paramNames(fn);
816
+ if (cb === undefined)
817
+ return undefined;
818
+ const inner = returnedFunction(fn);
819
+ if (inner === undefined)
820
+ return undefined;
821
+ const body = inner['body'];
822
+ // The module object: a variable of the maker's own, a parameter or one it declares, that the thunk sets to a fresh
823
+ // `{ exports }`.
824
+ let mod;
825
+ let object;
826
+ walkTree(body, {
827
+ enter(node) {
828
+ if (mod !== undefined || isFunction(node))
829
+ return false;
830
+ if (node.type !== 'AssignmentExpression' || node['operator'] !== '=')
831
+ return undefined;
832
+ const left = node['left'];
833
+ const right = node['right'];
834
+ const key = left.type === 'Identifier' ? definitions.keyOf(left) : undefined;
835
+ if (key === undefined || !within(key, fn) || within(key, inner) || key.name === cb || right.type !== 'ObjectExpression')
836
+ return undefined;
837
+ if (right['properties'].some((each) => propertyName(each) === 'exports')) {
838
+ mod = key;
839
+ object = right;
840
+ }
841
+ return undefined;
842
+ },
843
+ });
844
+ // The factory: `cb`, or a variable of the thunk's that only ever holds it (`var r = t; t = 0`).
845
+ const isFactory = (node) => {
846
+ if (isParam(node, fn, cb))
847
+ return true;
848
+ const key = node.type === 'Identifier' ? definitions.keyOf(node) : undefined;
849
+ if (key === undefined || !within(key, inner))
850
+ return false;
851
+ const defined = definitions.of(key);
852
+ return defined.length > 0 && defined.every((each) => each.source !== undefined && isParam(each.source, fn, cb));
853
+ };
854
+ let found;
855
+ walkTree(body, {
856
+ enter(node) {
857
+ if (found !== undefined || isFunction(node))
858
+ return false;
859
+ if (node.type !== 'CallExpression')
860
+ return undefined;
861
+ const callee = node['callee'];
862
+ // `cb(…)`, or `cb.call(this, …)`.
863
+ const through = member(callee, 'call');
864
+ if (!isFactory(through ? callee['object'] : callee))
865
+ return undefined;
866
+ const args = node['arguments'].slice(through ? 1 : 0);
867
+ const [first] = args;
868
+ // `cb(cb = 0)`: init only.
869
+ if (first?.type === 'AssignmentExpression' && isParam(first['left'], fn, cb)) {
870
+ const right = first['right'];
871
+ if (right.type === 'Literal' && right['value'] === 0)
872
+ found = { kind: 'init', call: node, factoryCall: {} };
873
+ return undefined;
874
+ }
875
+ // `cb((mod = { exports: {} }).exports, mod)`, `r.call(mod.exports, mod, mod.exports)`.
876
+ if (mod === undefined)
877
+ return undefined;
878
+ const module = args.findIndex((arg) => arg.type === 'Identifier' && sameKey(definitions.keyOf(arg), mod));
879
+ found = { kind: 'commonjs', call: node, factoryCall: { object: object, ...(module < 0 ? {} : { module }) } };
880
+ return undefined;
881
+ },
882
+ });
883
+ return found;
884
+ }
885
+ /** Whether a function is a memoized getter (above). */
886
+ function memoizedGetter(fn) {
887
+ if (fn['params'].length > 0 || fn['async'] === true || fn['generator'] === true)
888
+ return false;
889
+ const body = fn['body'];
890
+ if (body.type !== 'BlockStatement')
891
+ return false;
892
+ const [first] = body['body'];
893
+ // The flag the getter tests first: `if (f) return x`, `return f ? … : …`, `return f || (…), x`. The early return
894
+ // gives the memoized value: a run-once guard (`if (done) return; done = true; …`) is not a module.
895
+ let test;
896
+ if (first?.type === 'IfStatement' && returnsValue(first['consequent']))
897
+ test = first['test'];
898
+ else if (first?.type === 'ReturnStatement' && first['argument'] !== null) {
899
+ let argument = first['argument'];
900
+ if (argument.type === 'SequenceExpression')
901
+ argument = argument['expressions'][0];
902
+ if (argument.type === 'ConditionalExpression')
903
+ test = argument['test'];
904
+ else if (argument.type === 'LogicalExpression')
905
+ test = argument['left'];
906
+ }
907
+ if (test?.type !== 'Identifier')
908
+ return false;
909
+ const flag = definitions.keyOf(test);
910
+ if (flag === undefined || flag.scope === fn)
911
+ return false;
912
+ let sets = false;
913
+ walkTree(body, {
914
+ enter(node) {
915
+ if (sets || isFunction(node))
916
+ return false;
917
+ if (node.type !== 'AssignmentExpression' || node['operator'] !== '=')
918
+ return undefined;
919
+ const left = node['left'];
920
+ const right = node['right'];
921
+ const truthy = (right.type === 'Literal' && (right['value'] === 1 || right['value'] === true)) ||
922
+ (right.type === 'UnaryExpression' && right['operator'] === '!' && right['argument'].type === 'Literal' && right['argument']['value'] === 0);
923
+ if (left.type === 'Identifier' && truthy && sameKey(definitions.keyOf(left), flag))
924
+ sets = true;
925
+ return undefined;
926
+ },
927
+ });
928
+ return sets;
929
+ }
930
+ /** Whether a statement is `return x`, alone or as a block's only statement. */
931
+ function returnsValue(statement) {
932
+ const [only, ...rest] = statement.type === 'BlockStatement' ? statement['body'] : [statement];
933
+ return rest.length === 0 && only?.type === 'ReturnStatement' && only['argument'] !== null;
934
+ }
935
+ /**
936
+ * Whether a function is a getter export definer (above): the index of its parameter whose getters it defines, and of
937
+ * the one it defines them on, or none when it defines them on a fresh object it returns.
938
+ */
939
+ function exportDefiner(fn) {
940
+ const names = paramNames(fn);
941
+ let found;
942
+ walkTree(fn['body'], {
943
+ enter(node) {
944
+ if (found !== undefined || isFunction(node))
945
+ return false;
946
+ if (node.type !== 'ForInStatement')
947
+ return undefined;
948
+ const right = node['right'];
949
+ const list = names.findIndex((name) => name !== undefined && isParam(right, fn, name));
950
+ const left = node['left'];
951
+ const variable = left.type === 'VariableDeclaration' ? left['declarations'][0]['id'] : left;
952
+ if (list < 0 || variable.type !== 'Identifier')
953
+ return undefined;
954
+ // `define(target, k, { get: all[k], … })`
955
+ walkTree(node['body'], {
956
+ enter(part) {
957
+ if (found !== undefined || isFunction(part))
958
+ return false;
959
+ if (part.type !== 'CallExpression')
960
+ return undefined;
961
+ const [target, key, descriptor] = part['arguments'];
962
+ if (target?.type !== 'Identifier' || key?.type !== 'Identifier' || key['name'] !== variable['name'])
963
+ return undefined;
964
+ if (descriptor?.type !== 'ObjectExpression')
965
+ return undefined;
966
+ const getter = descriptor['properties'].find((each) => propertyName(each) === 'get')?.['value'];
967
+ if (getter?.type !== 'MemberExpression' || getter['computed'] !== true || !isParam(getter['object'], fn, names[list]))
968
+ return undefined;
969
+ const index = names.findIndex((name) => name !== undefined && isParam(target, fn, name));
970
+ if (index >= 0)
971
+ found = { list, target: index };
972
+ else if (definitions.keyOf(target)?.scope !== 'global' && returns(fn, target['name']))
973
+ found = { list };
974
+ return undefined;
975
+ },
976
+ });
977
+ return undefined;
978
+ },
979
+ });
980
+ return found;
981
+ }
982
+ /** Whether a function is an interop wrapper (above), and which. */
983
+ function interopWrapper(fn) {
984
+ const [param] = paramNames(fn);
985
+ if (param === undefined)
986
+ return undefined;
987
+ let esModule = false;
988
+ let namespace = false;
989
+ let reads = false;
990
+ walkTree(fn['body'], {
991
+ enter(node) {
992
+ if (isFunction(node))
993
+ return false;
994
+ if (node.type === 'MemberExpression' && isParam(node['object'], fn, param)) {
995
+ const property = node['property'];
996
+ const name = node['computed'] === true ? stringKey(property) : property['name'];
997
+ if (name === '__esModule')
998
+ esModule = true;
999
+ if (name === 'default')
1000
+ reads = true;
1001
+ }
1002
+ else if (node.type === 'CallExpression') {
1003
+ // `define(ns, "default", { value: m, … })`
1004
+ const [, key, descriptor] = node['arguments'];
1005
+ if (key !== undefined && stringKey(key) === 'default' && descriptor?.type === 'ObjectExpression') {
1006
+ const value = descriptor['properties'].find((each) => propertyName(each) === 'value')?.['value'];
1007
+ if (isParam(value, fn, param))
1008
+ namespace = true;
1009
+ }
1010
+ }
1011
+ return undefined;
1012
+ },
1013
+ });
1014
+ return !esModule ? undefined : namespace ? 'namespace' : reads ? 'default' : undefined;
1015
+ }
1016
+ /** The function a function returns: its arrow's body, or its only `return`'s. */
1017
+ function returnedFunction(fn) {
1018
+ const body = fn['body'];
1019
+ if (isFunction(body))
1020
+ return body;
1021
+ if (body.type !== 'BlockStatement')
1022
+ return undefined;
1023
+ const statements = body['body'];
1024
+ const last = statements.at(-1);
1025
+ const argument = last?.type === 'ReturnStatement' ? last['argument'] : null;
1026
+ return argument !== null && isFunction(argument) ? argument : undefined;
1027
+ }
1028
+ /** Whether a function's own code returns the variable `name`, alone or last in a sequence. */
1029
+ function returns(fn, name) {
1030
+ let found = false;
1031
+ walkTree(fn['body'], {
1032
+ enter(node) {
1033
+ if (found || isFunction(node))
1034
+ return false;
1035
+ if (node.type !== 'ReturnStatement' || node['argument'] === null)
1036
+ return undefined;
1037
+ const argument = calleeExpression(node['argument']);
1038
+ found = argument.type === 'Identifier' && argument['name'] === name;
1039
+ return undefined;
1040
+ },
1041
+ });
1042
+ return found;
1043
+ }
1044
+ }
1045
+ /** The name a property's key gives it: an identifier or a literal, spelled plainly or computed. */
1046
+ function propertyName(property) {
1047
+ const key = property['key'];
1048
+ if (key === undefined)
1049
+ return undefined;
1050
+ if (property['computed'] === true)
1051
+ return stringKey(key);
1052
+ return key.type === 'Identifier' ? key['name'] : literalKey(key);
1053
+ }
1054
+ /** A literal's key, or a template literal's with no substitutions (Rolldown quotes its strings so). */
1055
+ function stringKey(node) {
1056
+ if (node.type !== 'TemplateLiteral' || node['expressions'].length > 0)
1057
+ return literalKey(node);
1058
+ const [quasi] = node['quasis'];
1059
+ return quasi?.['value']?.cooked ?? undefined;
1060
+ }