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,487 @@
1
+ // Link groups (§6 Link groups, §19, ADR 0010): an entry artifact plus every artifact reachable from it through its
2
+ // static and dynamic imports, analyzed as one program with one heap and one realm.
3
+ //
4
+ // The group's members are joined into one program, so that the rest of the resolver sees one AST, as it would for a
5
+ // scope-hoisted single-file build:
6
+ //
7
+ // - The group's text is its members' texts one after another, each starting on a line of its own, and every node's
8
+ // offsets are shifted into it. `GroupLines` gives each location back in the coordinates of its own artifact.
9
+ // - The entry's code stays at the top level, as it runs. Every other member's code is the body of a function of its
10
+ // own, which gives it a module scope of its own. A member the entry's static imports reach runs at load, before the
11
+ // code that imports it: its function is called at the top level, in evaluation order. A member only dynamic imports
12
+ // reach runs when one of them does (`loadedBy`), never at load (§6, §7 Install context).
13
+ // - An import binding is the variable the module it names exports (`bound`), followed through re-exports, so a read
14
+ // or call of it is a read or call of that variable. An import whose module isn't in the group is a broken link
15
+ // (`unlinked`).
16
+ // - A member that doesn't parse fails every module graph that holds it, which gives nothing (Principle 1), and is
17
+ // recorded (§13 `parse-failure`).
18
+ import { walkTree } from "./ast.js";
19
+ import { registriesOf } from "./chunk-registry.js";
20
+ import { GroupLines, lineStarts } from "./line-starts.js";
21
+ import { parse } from "./parse.js";
22
+ import { boundNames } from "./scope.js";
23
+ /**
24
+ * The link group of `entry`: its members, pulled in through the module requests `resolveImport` resolves, and joined
25
+ * into one program. A group of one artifact is that artifact's program, unchanged. Undefined when the entry's own
26
+ * module graph fails; `failures` then says why.
27
+ */
28
+ export function linkGroup(entry, { artifact, resolveImport, chunks, initialChunks }) {
29
+ const byUrl = new Map();
30
+ const queue = [];
31
+ const add = (source, sourceType) => {
32
+ const parsed = parse(source.text, sourceType);
33
+ const member = 'error' in parsed
34
+ ? { source, statements: [], error: parsed.error, requests: [] }
35
+ : { source, program: parsed.program, statements: [...parsed.program['body']], requests: [] };
36
+ byUrl.set(source.url, member);
37
+ queue.push(member);
38
+ return member;
39
+ };
40
+ add(entry, entry.sourceType);
41
+ for (let next = 0; next < queue.length; next++) {
42
+ const member = queue[next];
43
+ if (member.program === undefined)
44
+ continue;
45
+ member.requests = requestsOf(member.program);
46
+ for (const request of member.requests) {
47
+ const url = resolveImport?.(member.source.url, request.specifier);
48
+ const found = url === undefined ? undefined : (byUrl.get(url)?.source ?? artifact(url));
49
+ if (found === undefined)
50
+ continue;
51
+ // An import always loads a module (HTML § Fetch a single module script).
52
+ request.target = byUrl.get(found.url) ?? add({ ...found, sourceType: 'module' }, 'module');
53
+ }
54
+ // The chunks a loader of the member's code loads (§6 idiom 6): every build-directory artifact that pushes an
55
+ // id its id table maps into the registry it reads, each with its own source type. A stale copy pushing the same id
56
+ // joins too, as an alternative (Principle 3).
57
+ // The page's initial chunks join too.
58
+ for (const load of registriesOf(member.program).loads) {
59
+ const pull = (url, kind) => {
60
+ if (byUrl.has(url))
61
+ return;
62
+ const found = artifact(url);
63
+ if (found !== undefined)
64
+ add(found, found.sourceType).chunk = kind;
65
+ };
66
+ for (const url of initialChunks?.(load.registry) ?? [])
67
+ pull(url, 'initial');
68
+ if (chunks === undefined)
69
+ continue;
70
+ for (const id of load.ids === 'any' ? [null] : [...load.ids].sort()) {
71
+ for (const url of chunks(load.registry, id))
72
+ pull(url, 'lazy');
73
+ }
74
+ }
75
+ }
76
+ const failures = queue.flatMap(({ source, error }) => (error === undefined ? [] : [{ source, error }]));
77
+ // A module graph fails when a module its static imports reach doesn't parse (Principle 1).
78
+ const failed = new Map();
79
+ const fails = (member) => {
80
+ const known = failed.get(member);
81
+ if (known !== undefined)
82
+ return known;
83
+ failed.set(member, false);
84
+ const result = member.error !== undefined ||
85
+ member.requests.some((request) => !request.dynamic && request.target !== undefined && fails(request.target));
86
+ failed.set(member, result);
87
+ return result;
88
+ };
89
+ const first = queue[0];
90
+ const pulled = queue.map((member) => member.source);
91
+ if (fails(first))
92
+ return { failures, pulled };
93
+ // The members the entry's static imports reach run at load, each after the modules it imports (ECMA-262
94
+ // InnerModuleEvaluation), the entry last. The others run only when a dynamic import does.
95
+ const loaded = [];
96
+ const visiting = new Set();
97
+ const visit = (member) => {
98
+ if (visiting.has(member))
99
+ return;
100
+ visiting.add(member);
101
+ for (const request of member.requests) {
102
+ if (!request.dynamic && request.target !== undefined)
103
+ visit(request.target);
104
+ }
105
+ loaded.push(member);
106
+ };
107
+ visit(first);
108
+ // A page's initial chunks run at load, after its entry, in URL order.
109
+ for (const member of queue)
110
+ if (member.chunk === 'initial' && member.error === undefined)
111
+ visit(member);
112
+ const isLoaded = new Set(loaded);
113
+ const included = queue.filter((member) => !fails(member));
114
+ const order = [...loaded, ...included.filter((member) => !isLoaded.has(member))];
115
+ // The group's text, and each member's code shifted into it.
116
+ let text = '';
117
+ const bases = new Map();
118
+ for (const member of order) {
119
+ bases.set(member, text.length);
120
+ if (text.length > 0)
121
+ shift(member.program, text.length);
122
+ text += member.source.text;
123
+ if (member !== order.at(-1))
124
+ text += '\n';
125
+ }
126
+ const lines = new GroupLines(order.map((member) => {
127
+ const { url, hash, origin } = member.source;
128
+ const base = bases.get(member);
129
+ const starts = lineStarts(member.source.text);
130
+ return { url, hash, ...(origin === undefined ? {} : { origin }), base, length: member.source.text.length, starts };
131
+ }));
132
+ const members = [first, ...order.filter((member) => member !== first)].map((member) => member.source);
133
+ const unlinked = new Map();
134
+ for (const member of order) {
135
+ const count = member.requests.filter((request) => request.target === undefined).length;
136
+ if (count > 0)
137
+ unlinked.set(member.source.url, count);
138
+ }
139
+ if (order.length === 1) {
140
+ const { program } = first;
141
+ return {
142
+ group: {
143
+ text,
144
+ program: program,
145
+ sourceType: entry.sourceType,
146
+ lines,
147
+ members,
148
+ failures,
149
+ bound: () => undefined,
150
+ wrappers: new Map(),
151
+ loadedBy: new Map(),
152
+ chunks: new Set(),
153
+ unlinked,
154
+ unlinkedBindings: unlinkedBindingsOf(order),
155
+ },
156
+ failures,
157
+ pulled,
158
+ };
159
+ }
160
+ // Each member other than the entry runs in a function of its own; its imports become declarations of that function.
161
+ const sourceType = entry.sourceType;
162
+ const declarationOf = new Map();
163
+ const wrappers = new Map();
164
+ const wrapperOf = new Map();
165
+ const body = [];
166
+ for (const member of order) {
167
+ if (member === first) {
168
+ body.push(...member.statements);
169
+ continue;
170
+ }
171
+ const base = bases.get(member);
172
+ const end = base + member.source.text.length;
173
+ const span = { start: base, end, range: [base, end] };
174
+ const statements = member.statements.map((statement) => {
175
+ if (statement.type !== 'ImportDeclaration')
176
+ return statement;
177
+ const specifiers = statement['specifiers'];
178
+ const at = { start: statement.start, end: statement.end, range: [statement.start, statement.end] };
179
+ if (specifiers.length === 0)
180
+ return { type: 'EmptyStatement', ...at };
181
+ const declaration = {
182
+ type: 'VariableDeclaration',
183
+ kind: 'let',
184
+ declare: false,
185
+ declarations: specifiers.map((specifier) => ({
186
+ type: 'VariableDeclarator',
187
+ id: specifier['local'],
188
+ init: null,
189
+ definite: false,
190
+ start: specifier.start,
191
+ end: specifier.end,
192
+ range: [specifier.start, specifier.end],
193
+ })),
194
+ ...at,
195
+ };
196
+ declarationOf.set(statement, declaration);
197
+ return declaration;
198
+ });
199
+ // A module's code is strict mode code, wherever it sits.
200
+ if (sourceType === 'script' && member.source.sourceType === 'module') {
201
+ const at = { start: base, end: base, range: [base, base] };
202
+ const literal = { type: 'Literal', value: 'use strict', raw: '"use strict"', ...at };
203
+ statements.unshift({ type: 'ExpressionStatement', expression: literal, directive: 'use strict', ...at });
204
+ }
205
+ const wrapper = {
206
+ type: 'FunctionExpression',
207
+ id: null,
208
+ generator: false,
209
+ async: false,
210
+ params: [],
211
+ body: { type: 'BlockStatement', body: statements, ...span },
212
+ expression: false,
213
+ ...span,
214
+ };
215
+ wrappers.set(wrapper, member.source);
216
+ wrapperOf.set(member, wrapper);
217
+ const expression = isLoaded.has(member)
218
+ ? { type: 'CallExpression', callee: wrapper, arguments: [], optional: false, ...span }
219
+ : wrapper;
220
+ body.push({ type: 'ExpressionStatement', expression, ...span });
221
+ }
222
+ const entryProgram = first.program;
223
+ const program = {
224
+ ...entryProgram,
225
+ body,
226
+ start: 0,
227
+ end: text.length,
228
+ range: [0, text.length],
229
+ };
230
+ // The `import(…)` calls that run each member only dynamic imports reach: those whose static graph holds it.
231
+ const loadedBy = new Map();
232
+ for (const member of included) {
233
+ for (const request of member.requests) {
234
+ if (!request.dynamic || request.target === undefined || fails(request.target))
235
+ continue;
236
+ for (const runs of staticGraph(request.target)) {
237
+ if (isLoaded.has(runs))
238
+ continue;
239
+ const wrapper = wrapperOf.get(runs);
240
+ const calls = loadedBy.get(wrapper);
241
+ if (calls === undefined)
242
+ loadedBy.set(wrapper, [request.node]);
243
+ else if (!calls.includes(request.node))
244
+ calls.push(request.node);
245
+ }
246
+ }
247
+ }
248
+ // Each import binding, by the declaration the binding-resolution seam gives it: the entry's import declaration, or the
249
+ // declaration a member's import became.
250
+ const bindings = new Map();
251
+ for (const member of included) {
252
+ for (const statement of member.statements) {
253
+ if (statement.type !== 'ImportDeclaration')
254
+ continue;
255
+ const request = member.requests.find((each) => each.node === statement);
256
+ const names = new Map();
257
+ for (const specifier of statement['specifiers']) {
258
+ const local = specifier['local']['name'];
259
+ const imported = importedName(specifier);
260
+ const target = request?.target;
261
+ const found = target === undefined || imported === undefined || fails(target) ? undefined : exported(target, imported, new Set());
262
+ names.set(local, found ?? { declaration: statement, name: local });
263
+ }
264
+ bindings.set(member === first ? statement : (declarationOf.get(statement) ?? statement), names);
265
+ }
266
+ }
267
+ return {
268
+ group: {
269
+ text,
270
+ program,
271
+ sourceType,
272
+ lines,
273
+ members,
274
+ failures,
275
+ bound: (declaration, name) => bindings.get(declaration)?.get(name),
276
+ wrappers,
277
+ loadedBy,
278
+ chunks: new Set(order.filter((member) => member.chunk === 'lazy').map((member) => member.source.url)),
279
+ unlinked,
280
+ unlinkedBindings: unlinkedBindingsOf(order),
281
+ },
282
+ failures,
283
+ pulled,
284
+ };
285
+ /** The included members a member's static imports reach, itself first. */
286
+ function staticGraph(start) {
287
+ const seen = new Set([start]);
288
+ const list = [start];
289
+ for (let index = 0; index < list.length; index++) {
290
+ for (const request of list[index].requests) {
291
+ const target = request.target;
292
+ if (request.dynamic || target === undefined || seen.has(target) || fails(target))
293
+ continue;
294
+ seen.add(target);
295
+ list.push(target);
296
+ }
297
+ }
298
+ return list;
299
+ }
300
+ /** The variable a module exports as `name`, followed through re-exports (ECMA-262 ResolveExport). */
301
+ function exported(module, name, seen) {
302
+ const key = `${module.source.url} ${name}`;
303
+ if (seen.has(key))
304
+ return undefined;
305
+ seen.add(key);
306
+ const targetOf = (statement) => {
307
+ const target = module.requests.find((request) => request.node === statement)?.target;
308
+ return target === undefined || fails(target) ? undefined : target;
309
+ };
310
+ for (const statement of module.statements) {
311
+ if (statement.type === 'ExportNamedDeclaration') {
312
+ const declaration = statement['declaration'];
313
+ if (declaration !== null) {
314
+ if (declares(declaration, name))
315
+ return { declaration, name };
316
+ continue;
317
+ }
318
+ for (const specifier of statement['specifiers']) {
319
+ if (moduleExportName(specifier['exported']) !== name)
320
+ continue;
321
+ const local = moduleExportName(specifier['local']);
322
+ if (statement['source'] === null)
323
+ return local === undefined ? undefined : declared(module, local, seen);
324
+ const target = targetOf(statement);
325
+ return target === undefined || local === undefined ? undefined : exported(target, local, seen);
326
+ }
327
+ }
328
+ else if (statement.type === 'ExportDefaultDeclaration' && name === 'default') {
329
+ const declaration = statement['declaration'];
330
+ if (declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') {
331
+ const id = declaration['id'];
332
+ return { declaration, name: id === null ? 'default' : id['name'] };
333
+ }
334
+ return declaration.type === 'Identifier' ? declared(module, declaration['name'], seen) : undefined;
335
+ }
336
+ }
337
+ // `export *` re-exports every name but `default` (ECMA-262 GetExportedNames).
338
+ if (name === 'default')
339
+ return undefined;
340
+ for (const statement of module.statements) {
341
+ if (statement.type !== 'ExportAllDeclaration' || statement['exported'] !== null)
342
+ continue;
343
+ const target = targetOf(statement);
344
+ const found = target === undefined ? undefined : exported(target, name, seen);
345
+ if (found !== undefined)
346
+ return found;
347
+ }
348
+ return undefined;
349
+ }
350
+ /** The variable a module's top level declares as `name`, or the one its import of that name is. */
351
+ function declared(module, name, seen) {
352
+ for (const statement of module.statements) {
353
+ const declaration = statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration'
354
+ ? statement['declaration']
355
+ : statement;
356
+ if (declaration === null)
357
+ continue;
358
+ if (declaration.type === 'ImportDeclaration') {
359
+ for (const specifier of declaration['specifiers']) {
360
+ if (specifier['local']['name'] !== name)
361
+ continue;
362
+ const target = module.requests.find((request) => request.node === declaration)?.target;
363
+ const imported = importedName(specifier);
364
+ if (target === undefined || fails(target) || imported === undefined)
365
+ return undefined;
366
+ return exported(target, imported, seen);
367
+ }
368
+ }
369
+ else if (declares(declaration, name))
370
+ return { declaration, name };
371
+ }
372
+ return undefined;
373
+ }
374
+ }
375
+ /** Whether a declaration statement declares `name`. */
376
+ function declares(declaration, name) {
377
+ switch (declaration.type) {
378
+ case 'FunctionDeclaration':
379
+ case 'ClassDeclaration':
380
+ return declaration['id']?.['name'] === name;
381
+ case 'VariableDeclaration':
382
+ return declaration['declarations'].some((declarator) => boundNames(declarator['id']).includes(name));
383
+ default:
384
+ return false;
385
+ }
386
+ }
387
+ /** The name an import specifier imports: `default` for a default import, none for a namespace import. */
388
+ function importedName(specifier) {
389
+ if (specifier.type === 'ImportDefaultSpecifier')
390
+ return 'default';
391
+ if (specifier.type !== 'ImportSpecifier')
392
+ return undefined;
393
+ return moduleExportName(specifier['imported']);
394
+ }
395
+ /** A ModuleExportName: an identifier, or a string literal. */
396
+ function moduleExportName(node) {
397
+ if (node.type === 'Identifier')
398
+ return node['name'];
399
+ return node.type === 'Literal' && typeof node['value'] === 'string' ? node['value'] : undefined;
400
+ }
401
+ /**
402
+ * The module requests of a program (§6 Module links): its static imports and re-exports, and its dynamic imports with a
403
+ * literal specifier, in document order. A static request with a `type` attribute names a module that isn't JavaScript,
404
+ * which is no link.
405
+ */
406
+ function requestsOf(program) {
407
+ const requests = [];
408
+ for (const statement of program['body']) {
409
+ if (statement.type !== 'ImportDeclaration' &&
410
+ statement.type !== 'ExportAllDeclaration' &&
411
+ !(statement.type === 'ExportNamedDeclaration' && statement['source'] !== null)) {
412
+ continue;
413
+ }
414
+ const attributes = statement['attributes'] ?? [];
415
+ if (attributes.some((attribute) => moduleExportName(attribute['key']) === 'type'))
416
+ continue;
417
+ const specifier = statement['source']['value'];
418
+ requests.push({ node: statement, specifier, dynamic: false });
419
+ }
420
+ walkTree(program, {
421
+ enter(node) {
422
+ if (node.type !== 'ImportExpression')
423
+ return;
424
+ const specifier = literalString(node['source']);
425
+ if (specifier !== undefined)
426
+ requests.push({ node, specifier, dynamic: true });
427
+ },
428
+ });
429
+ return requests.sort((a, b) => a.node.start - b.node.start);
430
+ }
431
+ /** A string literal's value, or a template literal's with no substitutions. */
432
+ function literalString(node) {
433
+ if (node.type === 'Literal')
434
+ return typeof node['value'] === 'string' ? node['value'] : undefined;
435
+ if (node.type !== 'TemplateLiteral' || node['expressions'].length > 0)
436
+ return undefined;
437
+ return node['quasis'][0]['value'].cooked ?? undefined;
438
+ }
439
+ /**
440
+ * The import bindings of each member whose static import names no member of the group, by their import declaration and
441
+ * name, each with the import declaration.
442
+ */
443
+ function unlinkedBindingsOf(order) {
444
+ const found = new Map();
445
+ for (const member of order) {
446
+ for (const request of member.requests) {
447
+ if (request.target !== undefined || request.node.type !== 'ImportDeclaration')
448
+ continue;
449
+ const names = new Map();
450
+ for (const specifier of request.node['specifiers']) {
451
+ names.set(specifier['local']['name'], request.node);
452
+ }
453
+ found.set(request.node, names);
454
+ }
455
+ }
456
+ return found;
457
+ }
458
+ /** Shifts every offset in a parsed tree by `by`: each object's `start`, `end` and `range`, once each. */
459
+ function shift(root, by) {
460
+ const seen = new Set();
461
+ const stack = [root];
462
+ while (stack.length > 0) {
463
+ const value = stack.pop();
464
+ if (typeof value !== 'object' || value === null || seen.has(value))
465
+ continue;
466
+ seen.add(value);
467
+ if (Array.isArray(value)) {
468
+ stack.push(...value);
469
+ continue;
470
+ }
471
+ if (value instanceof RegExp)
472
+ continue;
473
+ const node = value;
474
+ if (typeof node['start'] === 'number')
475
+ node['start'] = node['start'] + by;
476
+ if (typeof node['end'] === 'number')
477
+ node['end'] = node['end'] + by;
478
+ const range = node['range'];
479
+ if (Array.isArray(range) && range.length === 2 && typeof range[0] === 'number') {
480
+ node['range'] = [range[0] + by, range[1] + by];
481
+ seen.add(node['range']);
482
+ }
483
+ for (const key of Object.keys(node))
484
+ if (key !== 'range')
485
+ stack.push(node[key]);
486
+ }
487
+ }
@@ -0,0 +1,117 @@
1
+ import { type AstNode } from './ast.ts';
2
+ import { type Definitions } from './definitions.ts';
3
+ /** The idioms R1 promises to link (§6), as the report names them. */
4
+ export type Idiom = 'chunk-loader' | 'factory-table' | 'getter-export' | 'interop' | 'lazy-thunk' | 'memoized-getter';
5
+ /** Where a value came through a module link: the idiom, the module's id, and the export's name. */
6
+ export interface ModuleLink {
7
+ idiom: Exclude<Idiom, 'chunk-loader'>;
8
+ module: string;
9
+ export?: string;
10
+ }
11
+ /**
12
+ * A field of a module's exports the definer defines (above): its getter, an expression whose value is the getter
13
+ * function, or its value. `site` is the definer's call, `object` the exports expression, and `node` the expression
14
+ * that gives the field.
15
+ */
16
+ export interface ExportDefine {
17
+ site: AstNode;
18
+ object: AstNode;
19
+ key: string;
20
+ getter?: AstNode;
21
+ value?: AstNode;
22
+ /** The module the define is in, with the export's name, when the define is in a factory. */
23
+ link?: ModuleLink;
24
+ }
25
+ /**
26
+ * A broken link: a require call whose id isn't a constant, or names no factory, or an import whose module isn't in the
27
+ * link group (§6 Degradation), whose `call` is its import declaration.
28
+ */
29
+ export interface BrokenLink {
30
+ call: AstNode;
31
+ why: 'non-constant' | 'missing' | 'import';
32
+ }
33
+ /**
34
+ * A lazy module thunk's call of its factory: the module object literal it makes (`{ exports: {} }`), and the index of
35
+ * the factory's parameter that takes that object. An init-only thunk (`__esm`) makes none.
36
+ */
37
+ export interface FactoryCall {
38
+ object?: AstNode;
39
+ module?: number;
40
+ }
41
+ export interface ModuleLinks {
42
+ /** The idioms the artifact uses. */
43
+ idioms: ReadonlySet<Idiom>;
44
+ /**
45
+ * Whether the artifact links modules through a factory table's idioms, whose exports are all read through calls the
46
+ * syntax names no function for, and computed heap reads: there the evaluator follows calls and heap reads further.
47
+ */
48
+ tables: boolean;
49
+ /** How many factories the artifact's tables hold. */
50
+ modules: number;
51
+ /** The definer's calls' defines, in document order. */
52
+ defines: readonly ExportDefine[];
53
+ /** The definer functions, whose own defines the model stands for. */
54
+ definers: ReadonlySet<AstNode>;
55
+ /** The require calls that link, each with the module it links. */
56
+ links: ReadonlyMap<AstNode, ModuleLink>;
57
+ /**
58
+ * Each require function's linked modules, each with its first require call in document order, which stands for every
59
+ * require of it by that require function. Keyed by require first, since two runtimes in one artifact may reuse ids.
60
+ */
61
+ firsts: ReadonlyMap<AstNode, ReadonlyMap<string, AstNode>>;
62
+ /**
63
+ * Each factory in a table: its module's id, the require function that runs it, and the require's call of the table
64
+ * (`t[r](…)`, `e[r].call(…)`) that runs it.
65
+ */
66
+ factories: ReadonlyMap<AstNode, {
67
+ module: string;
68
+ require: AstNode;
69
+ call: AstNode;
70
+ }>;
71
+ /** The broken links. */
72
+ broken: readonly BrokenLink[];
73
+ /** The calls through what a broken link returns, each with the broken link. */
74
+ unlinkedCalls: ReadonlyMap<AstNode, BrokenLink>;
75
+ /**
76
+ * The calls that run a module and give its exports, other than a require's: a lazy module thunk's or a memoized
77
+ * getter's call (`require_x()`, `requireX()`), and an interop wrapper's call over one (`__toESM(require_x())`), each
78
+ * with the module it links.
79
+ */
80
+ moduleCalls: ReadonlyMap<AstNode, ModuleLink>;
81
+ /**
82
+ * The interop wrappers' calls that make a namespace object of a CommonJS module (`__toESM(m)`), each with the
83
+ * expression whose value is the module's exports: the namespace's `default` is that value, and its other keys are
84
+ * that value's own.
85
+ */
86
+ namespaces: ReadonlyMap<AstNode, AstNode>;
87
+ /**
88
+ * The lazy module thunks' calls of their factories (`cb(…)`, `r.call(…)` in a thunk), which read only what the thunk
89
+ * closes over, each with the module object the thunk makes, if any.
90
+ */
91
+ factoryCalls: ReadonlyMap<AstNode, FactoryCall>;
92
+ /** The thunk makers' calls that make a module (`maker(factory)`, `R.cw(factory)`), each with its thunk maker. */
93
+ makes: ReadonlyMap<AstNode, AstNode>;
94
+ /**
95
+ * The factories chunks push into a chunk registry that a loader of the program reads (§6 idiom 6), by the literal of
96
+ * the table its require reads and module id: the table holds them as well as its own. Several chunks can push the same
97
+ * id, and each is an alternative (Principle 3).
98
+ */
99
+ chunkFactories: ReadonlyMap<AstNode, ReadonlyMap<string, readonly AstNode[]>>;
100
+ /**
101
+ * The callbacks chunks push with their factories, each with its push: the loader calls one with its require where the
102
+ * chunk pushes it (§6 idiom 6), which starts the chunk's entry modules.
103
+ */
104
+ pushCallbacks: ReadonlyMap<AstNode, AstNode>;
105
+ /**
106
+ * The require calls a pushed callback makes through its require parameter, which start a chunk's entry modules
107
+ * (`t => t(t.s = 4429)`), each with the require function: each is a resolved call of its module's factory.
108
+ */
109
+ starts: ReadonlyMap<AstNode, AstNode>;
110
+ /** The constant names of the chunk registries the program reads or pushes into, each a known non-native array (§6). */
111
+ registries: ReadonlySet<string>;
112
+ /** For the report alone: how many factories the program's own chunk pushes hold, and whether it has a loader. */
113
+ pushed: number;
114
+ loader: boolean;
115
+ }
116
+ /** The module-linking idioms of a program (above). */
117
+ export declare function moduleLinks(program: AstNode, definitions: Definitions): ModuleLinks;