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,359 @@
1
+ // Script loads only the resolver can see (§2 Discovery, §19 References only the resolver can see): the chunk URLs a
2
+ // chunk loader builds (`chunk-loads.ts`), the scripts code injects, and worker URLs that aren't literals.
3
+ //
4
+ // - An **injected script** is a script element the code creates (`X.createElement("script")`), gives a `src` and
5
+ // inserts: any Node or Element insertion method, on any receiver, with the element as a node argument, inserts
6
+ // it, and `src` counts whether it is set before or after. Writes are followed flow-insensitively, per element: every
7
+ // `src` write is an alternative, as is every `type`. A `setAttribute` whose key doesn't fold touches neither.
8
+ // - Its `src` alternatives that fold are its URLs, each unresolved: discovery resolves them against each document's
9
+ // base URL. An alternative that doesn't fold is one `unresolved-reference`. Its `type` is passed on as
10
+ // folded, for discovery to judge as it judges an element's; an `integrity` that folds is carried, with each
11
+ // constant it may be, for verification (§17). Its `noModule` is passed on as `no`, `yes` or, when the
12
+ // writes differ or one doesn't fold, `maybe`: discovery judges what it does to a classic script.
13
+ // - **Literal code** an injected element carries, with no `src` (`s.text`, `.textContent`, `.innerText`, or an
14
+ // appended `X.createTextNode(code)`), is nested code, compiled where the element is inserted.
15
+ // - A **worker URL** that isn't a literal (`new Worker`, `new SharedWorker`, `navigator.serviceWorker.register`,
16
+ // `importScripts`) folds as `Folder` folds it; each constant alternative is a reference, and each alternative
17
+ // that doesn't fold is one `unresolved-reference` at that alternative. Discovery owns what a worker call
18
+ // is: which realm can make it, and whether its callee is the native.
19
+ //
20
+ // A URL expression that calls a chunk loader's URL function is a chunk load, not one of these.
21
+ import { walkTree } from "./ast.js";
22
+ import { chunkCandidates, Folder, foldChunkSites, isScriptCreation, mayLoadChunks, propertyName, } from "./chunk-loads.js";
23
+ import { definitionsOf } from "./definitions.js";
24
+ import { lineStarts, locate } from "./line-starts.js";
25
+ import { parse } from "./parse.js";
26
+ import { scopeRules } from "./scope.js";
27
+ const none = { chunks: [], injected: [], workers: [] };
28
+ /**
29
+ * The script loads of an artifact's code at `url` (its own URL, for `import.meta.url`). None when it doesn't parse.
30
+ */
31
+ export function scriptLoads(text, sourceType, bindings, url) {
32
+ const chunks = mayLoadChunks(text);
33
+ const elements = /createElement/.test(text);
34
+ const workers = /Worker|importScripts/.test(text);
35
+ if (!chunks && !elements && !workers)
36
+ return none;
37
+ const parsed = parse(text, sourceType);
38
+ if ('error' in parsed)
39
+ return none;
40
+ const { program } = parsed;
41
+ const candidates = chunkCandidates(program);
42
+ const loadsChunks = chunks && candidates.functions.size > 0 && candidates.sites.length > 0;
43
+ // Only code that creates a script element, or calls a worker function with a URL that isn't a literal, needs folding.
44
+ const needed = syntaxNeeds(program, elements, workers);
45
+ if (!loadsChunks && !needed.elements && !needed.workers)
46
+ return none;
47
+ const definitions = definitionsOf(program, scopeRules(program, sourceType, bindings));
48
+ const folder = new Folder(program, definitions, candidates.functions, url);
49
+ const starts = lineStarts(text);
50
+ const at = (node) => locate(starts, node.start, node.end);
51
+ return {
52
+ chunks: loadsChunks ? foldChunkSites(candidates, folder, starts) : [],
53
+ injected: needed.elements ? injectedSites(program, folder, at) : [],
54
+ workers: needed.workers ? workerSites(program, folder, at) : [],
55
+ };
56
+ }
57
+ /** Whether a program creates a script element, and calls a worker function with a URL that isn't a literal. */
58
+ function syntaxNeeds(program, elements, workers) {
59
+ const found = { elements: false, workers: false };
60
+ if (!elements && !workers)
61
+ return found;
62
+ walkTree(program, {
63
+ enter(node) {
64
+ if (found.elements === elements && found.workers === workers)
65
+ return false;
66
+ if (elements && node.type === 'CallExpression' && isScriptCreation(node))
67
+ found.elements = true;
68
+ if (workers && (node.type === 'NewExpression' || node.type === 'CallExpression') && isWorkerCall(node)) {
69
+ if (node['arguments'].some((arg) => arg.type !== 'SpreadElement' && !isLiteral(arg)))
70
+ found.workers = true;
71
+ }
72
+ return undefined;
73
+ },
74
+ });
75
+ return found;
76
+ }
77
+ /** The insertion methods of Node and Element, and which of their arguments are nodes: all, or one index. */
78
+ const insertions = new Map([
79
+ ['appendChild', 0],
80
+ ['insertBefore', 0],
81
+ ['replaceChild', 0],
82
+ ['insertAdjacentElement', 1],
83
+ ['append', 'all'],
84
+ ['prepend', 'all'],
85
+ ['before', 'all'],
86
+ ['after', 'all'],
87
+ ['replaceWith', 'all'],
88
+ ]);
89
+ /** The members that set a script element's code, as text. */
90
+ const textMembers = new Set(['text', 'textContent', 'innerText']);
91
+ /**
92
+ * The script elements a program creates, each by its creation call, with what the program sets on them and the
93
+ * insertion calls that insert them, in document order.
94
+ */
95
+ export function scriptElements(program, folder) {
96
+ const elements = new Map();
97
+ if (!syntaxNeeds(program, true, false).elements)
98
+ return elements;
99
+ const of = (node) => [...folder.elementsOf(node)].map((call) => {
100
+ let element = elements.get(call);
101
+ if (element === undefined)
102
+ elements.set(call, (element = { src: [], type: [], integrity: [], noModule: [], text: [], inserts: [] }));
103
+ return element;
104
+ });
105
+ walkTree(program, {
106
+ enter(node) {
107
+ if (node.type === 'AssignmentExpression' && node['operator'] === '=') {
108
+ const left = node['left'];
109
+ if (left.type !== 'MemberExpression')
110
+ return;
111
+ const name = propertyName(left);
112
+ const value = node['right'];
113
+ if (name === 'src' || name === 'type' || name === 'integrity')
114
+ for (const element of of(left['object']))
115
+ element[name].push(value);
116
+ else if (name === 'noModule')
117
+ for (const element of of(left['object']))
118
+ element.noModule.push(value);
119
+ else if (name !== undefined && textMembers.has(name))
120
+ for (const element of of(left['object']))
121
+ element.text.push(value);
122
+ return;
123
+ }
124
+ if (node.type !== 'CallExpression')
125
+ return;
126
+ const callee = node['callee'];
127
+ if (callee.type !== 'MemberExpression')
128
+ return;
129
+ const method = propertyName(callee);
130
+ if (method === undefined)
131
+ return;
132
+ const args = node['arguments'];
133
+ const receiver = callee['object'];
134
+ if (method === 'removeAttribute' && args.length >= 1 && args[0].type !== 'SpreadElement') {
135
+ const key = folder.strings(args[0]);
136
+ if (!key.unknown && key.values.length === 1 && key.values[0].toLowerCase() === 'nomodule')
137
+ for (const element of of(receiver))
138
+ element.noModule.push(false);
139
+ return;
140
+ }
141
+ if (method === 'setAttribute' && args.length >= 2 && args[0].type !== 'SpreadElement') {
142
+ // An attribute name is lowercased on an HTML element. A name that doesn't fold touches neither.
143
+ const key = folder.strings(args[0]);
144
+ if (key.unknown || key.values.length !== 1)
145
+ return;
146
+ const name = key.values[0].toLowerCase();
147
+ if (name === 'src' || name === 'type' || name === 'integrity')
148
+ for (const element of of(receiver))
149
+ element[name].push(args[1]);
150
+ // A boolean attribute is set whatever its value (HTML § Boolean attributes).
151
+ else if (name === 'nomodule')
152
+ for (const element of of(receiver))
153
+ element.noModule.push(true);
154
+ return;
155
+ }
156
+ const nodes = insertions.get(method);
157
+ if (nodes === undefined)
158
+ return;
159
+ const inserted = nodes === 'all' ? args : args.slice(nodes, nodes + 1);
160
+ for (const arg of inserted) {
161
+ if (arg.type === 'SpreadElement')
162
+ continue;
163
+ for (const element of of(arg))
164
+ element.inserts.push(node);
165
+ // A text node appended to a script element is its code.
166
+ const text = textNodeCode(arg);
167
+ if (text !== undefined && (method === 'appendChild' || method === 'append'))
168
+ for (const element of of(receiver))
169
+ element.text.push(text);
170
+ }
171
+ },
172
+ });
173
+ return elements;
174
+ }
175
+ /** The code of `X.createTextNode(code)`. */
176
+ function textNodeCode(node) {
177
+ if (node.type !== 'CallExpression')
178
+ return undefined;
179
+ const callee = node['callee'];
180
+ const [code] = node['arguments'];
181
+ if (callee.type !== 'MemberExpression' || propertyName(callee) !== 'createTextNode' || code === undefined)
182
+ return undefined;
183
+ return code.type === 'SpreadElement' ? undefined : code;
184
+ }
185
+ /**
186
+ * The code an injected element with no `src` carries, by the call that first inserts it: the expressions its
187
+ * text is set to.
188
+ */
189
+ export function scriptTexts(program, folder) {
190
+ const texts = new Map();
191
+ for (const element of scriptElements(program, folder).values()) {
192
+ if (element.src.length > 0 || element.text.length === 0)
193
+ continue;
194
+ const [insert] = element.inserts;
195
+ if (insert === undefined)
196
+ continue;
197
+ texts.set(insert, [...(texts.get(insert) ?? []), ...element.text]);
198
+ }
199
+ return texts;
200
+ }
201
+ /** The injected scripts of a program (above), by `src` write, in document order. */
202
+ function injectedSites(program, folder, at) {
203
+ const sites = [];
204
+ for (const element of scriptElements(program, folder).values()) {
205
+ if (element.inserts.length === 0 || element.src.length === 0)
206
+ continue;
207
+ const types = [];
208
+ if (element.type.length === 0)
209
+ types.push(undefined);
210
+ for (const type of element.type) {
211
+ const folded = folder.strings(type);
212
+ types.push(...folded.values);
213
+ if (folded.unknown)
214
+ types.push(null);
215
+ }
216
+ const integrities = element.integrity.map((value) => folder.strings(value));
217
+ // Every write's constants are the alternatives; a write that doesn't fold makes the value none.
218
+ const integrity = integrities.some((each) => each.unknown) ? [] : [...new Set(integrities.flatMap((each) => each.values))].sort();
219
+ const noModule = noModuleOf(element.noModule);
220
+ for (const src of element.src) {
221
+ const folded = alternatives(src, folder, at);
222
+ if (folded === undefined)
223
+ continue;
224
+ sites.push({ location: at(src), ...folded, types: [...new Set(types)], integrity, noModule });
225
+ }
226
+ }
227
+ return sites.sort((a, b) => a.location.start - b.location.start);
228
+ }
229
+ /** Whether an expression is truthy, when it is a constant: a literal, `!`, `void` or `undefined`. */
230
+ function truthiness(node) {
231
+ switch (node.type) {
232
+ case 'Literal':
233
+ return Boolean(node['value']);
234
+ case 'ParenthesizedExpression':
235
+ return truthiness(node['expression']);
236
+ case 'Identifier':
237
+ return node['name'] === 'undefined' ? false : undefined;
238
+ case 'UnaryExpression': {
239
+ if (node['operator'] === 'void')
240
+ return false;
241
+ const argument = node['operator'] === '!' ? truthiness(node['argument']) : undefined;
242
+ return argument === undefined ? undefined : !argument;
243
+ }
244
+ default:
245
+ return undefined;
246
+ }
247
+ }
248
+ /**
249
+ * Whether an element's `noModule` is set: every write is an alternative, as `src`'s are. `yes` when each is
250
+ * truthy, `no` when each is falsy or there is none, and `maybe` when they differ or one doesn't fold.
251
+ */
252
+ function noModuleOf(writes) {
253
+ const values = new Set();
254
+ for (const write of writes) {
255
+ if (typeof write === 'boolean')
256
+ values.add(write);
257
+ else
258
+ for (const alternative of branches(write))
259
+ values.add(truthiness(alternative));
260
+ }
261
+ if (values.size === 0 || (values.size === 1 && values.has(false)))
262
+ return 'no';
263
+ return values.size === 1 && values.has(true) ? 'yes' : 'maybe';
264
+ }
265
+ /** The worker functions a callee path names (`new` or a call), without the global object's names before it. */
266
+ function isWorkerCall(node) {
267
+ const path = [];
268
+ let at = node['callee'];
269
+ for (;;) {
270
+ if (at.type === 'ChainExpression' || at.type === 'ParenthesizedExpression')
271
+ at = at['expression'];
272
+ else if (at.type === 'SequenceExpression')
273
+ at = at['expressions'].at(-1);
274
+ else
275
+ break;
276
+ }
277
+ while (at.type === 'MemberExpression') {
278
+ const name = propertyName(at);
279
+ if (name === undefined)
280
+ return false;
281
+ path.unshift(name);
282
+ at = at['object'];
283
+ }
284
+ if (at.type !== 'Identifier')
285
+ return false;
286
+ path.unshift(at['name']);
287
+ while (path.length > 1 && ['window', 'self', 'globalThis'].includes(path[0]))
288
+ path.shift();
289
+ const spelt = path.join('.');
290
+ if (node.type === 'NewExpression')
291
+ return spelt === 'Worker' || spelt === 'SharedWorker';
292
+ return spelt === 'navigator.serviceWorker.register' || spelt === 'importScripts';
293
+ }
294
+ /** The worker URLs of a program that aren't literals (above), in document order. */
295
+ function workerSites(program, folder, at) {
296
+ const sites = [];
297
+ walkTree(program, {
298
+ enter(node) {
299
+ if ((node.type !== 'NewExpression' && node.type !== 'CallExpression') || !isWorkerCall(node))
300
+ return;
301
+ const args = node['arguments'];
302
+ const importScripts = node.type === 'CallExpression' && !/register$/.test(calleeName(node));
303
+ for (const url of importScripts ? args : args.slice(0, 1)) {
304
+ if (url.type === 'SpreadElement' || isLiteral(url))
305
+ continue;
306
+ // Only `importScripts` loads chunks: another worker call's URL is its worker's however it is built.
307
+ const folded = alternatives(url, folder, at, importScripts);
308
+ if (folded !== undefined)
309
+ sites.push({ location: at(url), ...folded });
310
+ }
311
+ },
312
+ });
313
+ return sites;
314
+ }
315
+ /** The last name a callee spells. */
316
+ function calleeName(call) {
317
+ let at = call['callee'];
318
+ while (at.type === 'ChainExpression' || at.type === 'ParenthesizedExpression')
319
+ at = at['expression'];
320
+ if (at.type === 'SequenceExpression')
321
+ at = at['expressions'].at(-1);
322
+ if (at.type === 'Identifier')
323
+ return at['name'];
324
+ return at.type === 'MemberExpression' ? (propertyName(at) ?? '') : '';
325
+ }
326
+ const isLiteral = (node) => (node.type === 'Literal' && typeof node['value'] === 'string') ||
327
+ (node.type === 'TemplateLiteral' && node['expressions'].length === 0);
328
+ /**
329
+ * A URL expression's alternatives: the branches of a conditional and both sides of `||`, `??` and `&&`, each
330
+ * folded. The URLs they fold to, and where each alternative that doesn't is. Undefined, when the site `loadsChunks`,
331
+ * if it calls a chunk loader's URL function, which makes it a chunk load.
332
+ */
333
+ function alternatives(node, folder, at, loadsChunks = true) {
334
+ const urls = new Set();
335
+ const unknown = [];
336
+ for (const alternative of branches(node)) {
337
+ const folded = folder.strings(alternative);
338
+ if (folded.chunk && loadsChunks)
339
+ return undefined;
340
+ for (const value of folded.values)
341
+ urls.add(value);
342
+ if (folded.unknown)
343
+ unknown.push(at(alternative));
344
+ }
345
+ return { urls: [...urls].sort(), unknown };
346
+ }
347
+ function branches(node) {
348
+ switch (node.type) {
349
+ case 'ParenthesizedExpression':
350
+ return branches(node['expression']);
351
+ case 'ConditionalExpression':
352
+ return [...branches(node['consequent']), ...branches(node['alternate'])];
353
+ case 'LogicalExpression':
354
+ // Either side may be the value: the left one when it decides the operator, the right one otherwise.
355
+ return [...branches(node['left']), ...branches(node['right'])];
356
+ default:
357
+ return [node];
358
+ }
359
+ }
@@ -0,0 +1,177 @@
1
+ import { type AstNode } from './ast.ts';
2
+ import type { Definitions } from './definitions.ts';
3
+ import type { ModuleLink } from './module-links.ts';
4
+ import type { ScopeRules } from './scope.ts';
5
+ /** Free names a `typeof` test shows are defined, each with the test. */
6
+ type Guards = ReadonlyMap<string, AstNode>;
7
+ /** A call of a known function, where its arguments are. */
8
+ export interface CallSite {
9
+ /** The call or `new` expression. */
10
+ call: AstNode;
11
+ /** The function called. */
12
+ fn: AstNode;
13
+ /** The arguments, from the first one bound to the first parameter (after `.call`'s or `.apply`'s `this`). */
14
+ args: readonly AstNode[];
15
+ /** The index from which the arguments can't be listed: a spread, or `.apply` of anything but an array literal. */
16
+ unlisted?: number;
17
+ /** Whether `this` at the call is the script's top-level `this`. */
18
+ topLevel: boolean;
19
+ /** The `typeof` guards around the call, which show a free name is defined there. */
20
+ guards: Guards;
21
+ /** The function whose own code holds the call, if any. */
22
+ within: AstNode | undefined;
23
+ /** The loops and the function around the call, innermost first, out to the function whose own code holds it. */
24
+ contexts: readonly AstNode[];
25
+ /**
26
+ * For a callback an iteration native calls (§5 Resolver capabilities 4), the native and the collection it visits:
27
+ * `names` in `names.forEach(cb)`, `names` in `Array.from(names, cb)`. Its arguments are the elements' (`Binding`).
28
+ */
29
+ iterates?: {
30
+ native: string;
31
+ collection: AstNode;
32
+ };
33
+ /**
34
+ * What `this` is in the call (§5 Resolver capabilities 3): the instance a `new` makes (`new`), or an expression
35
+ * evaluated where the call is: the object a method is read from (`o` in `o.m()`), or a `thisArg` (`f.call(t)`), or
36
+ * `none` for a call that passes no `this`: a plain call `f()`, `f.call()`, or an iteration native's callback with no
37
+ * `thisArg`. ECMA-262 binds the global object as a sloppy function's `this` there, and `undefined` as a strict
38
+ * one's. Left out when the call's `this` is unknown, as a spread `thisArg` is.
39
+ */
40
+ receiver?: 'new' | 'none' | AstNode;
41
+ /**
42
+ * For a call the syntax doesn't name, the module links its callee's value came through (§6): `(0, o.H7)(…)` calls
43
+ * module 8974's export `H7`. The trace records each as a `module-link` step before the call's.
44
+ */
45
+ links?: {
46
+ node: AstNode;
47
+ link: ModuleLink;
48
+ }[];
49
+ }
50
+ /** Where a call is: what a call site needs, whichever function it turns out to call. */
51
+ export interface CallInfo {
52
+ topLevel: boolean;
53
+ guards: Guards;
54
+ within: AstNode | undefined;
55
+ contexts: readonly AstNode[];
56
+ }
57
+ /** A call site, with the frame its arguments are evaluated in: none for a call no function around it needs. */
58
+ export interface Binding {
59
+ site: CallSite;
60
+ caller: Frame | undefined;
61
+ /** For an iteration native's callback, the index of the element this call visits. */
62
+ element?: number;
63
+ /**
64
+ * For a call of a function value (§5 Resolver capabilities 3), the frame the function was created in: its closure,
65
+ * whose bindings the function reads. For a call by name, the frame of the code the call is in that holds the function.
66
+ */
67
+ closure?: Frame | undefined;
68
+ /** A key for the receiver when the binding has a precomputed one, so that two receivers make two frames. */
69
+ receiverKey?: string;
70
+ }
71
+ /** An instantiation of a function (above). */
72
+ export interface Frame {
73
+ fn: AstNode;
74
+ bindings: readonly Binding[];
75
+ /** A key that two instantiations of the same function in the same context share. */
76
+ id: string;
77
+ /** How many frames the chain out from this one holds. */
78
+ depth: number;
79
+ /**
80
+ * How the frame came about: one call, several merged by the context cap, every call, widened, or none: a function
81
+ * object that no resolved call reaches, judged from where it was created (`creation`, §5, §7).
82
+ */
83
+ how: 'call' | 'merged' | 'widened' | 'creation';
84
+ /**
85
+ * For a loop's iteration, or an iteration native's callback, the frame of the code it runs within: the function or
86
+ * loop around the loop, or the code that calls the native. Its parameters and loop variables are read through it.
87
+ */
88
+ outer?: Frame | undefined;
89
+ /** For a loop's iteration, its index. */
90
+ iteration?: number;
91
+ /** The loop or call whose iterations the iteration cap cut, when this frame is one of the iterations done. */
92
+ cut?: AstNode;
93
+ /**
94
+ * For an iteration over a catalog-bounded list (§5 Resolver capabilities 9): no cap bounds its iterations, nor
95
+ * the frames of the calls made in them, and only the step budget bounds them.
96
+ */
97
+ bounded?: true;
98
+ /** For a `creation` frame, the node its function object was created or assumed to be held at, for the trace. */
99
+ created?: AstNode;
100
+ /**
101
+ * For a `creation` frame whose function has a `this` of its own, the object it is shown to be held on (a value of the
102
+ * resolution's), and the node that made that object.
103
+ */
104
+ receiver?: unknown;
105
+ receiverNode?: AstNode;
106
+ }
107
+ export interface CallGraph {
108
+ /** The calls of known functions a call expression is: one per function its callee can be. */
109
+ sitesOf(call: AstNode): readonly CallSite[];
110
+ /** Whether the callee of a call can be something other than the functions `sitesOf` lists. */
111
+ open(call: AstNode): boolean;
112
+ /** Every call of a function, in document order. */
113
+ callsOf(fn: AstNode): readonly CallSite[];
114
+ /** The function whose own code holds `node`, if any. */
115
+ functionOf(node: AstNode): AstNode | undefined;
116
+ /** Where a call or `new` expression is. */
117
+ infoOf(call: AstNode): CallInfo | undefined;
118
+ /** Every call and `new` expression of the program, in document order. */
119
+ allCalls(): readonly AstNode[];
120
+ }
121
+ export declare const isFunction: (node: AstNode) => boolean;
122
+ /** A loop statement, which may be unrolled (§5 Resolver capabilities 4). */
123
+ export declare const isLoop: (node: AstNode) => boolean;
124
+ /**
125
+ * The natives that call a callback once per element of an array (ECMA-262), by method name, with the index of the
126
+ * callback's parameter that takes the element. `from` is `Array.from(items, mapfn)`.
127
+ */
128
+ export declare const iterationNatives: ReadonlyMap<string, {
129
+ element: number;
130
+ }>;
131
+ /**
132
+ * The calls in a program, and the functions they call. `guardsAt(parent, child)` gives the `typeof` guards a branch
133
+ * adds, as the resolver's walk takes them.
134
+ */
135
+ export declare function callGraph(program: AstNode, scope: ScopeRules, definitions: Definitions, guardsAt: (parent: AstNode, child: AstNode) => Guards): CallGraph;
136
+ export declare const isClass: (node: AstNode) => boolean;
137
+ /**
138
+ * The code a `new` of a class runs as its constructor: its `constructor` method's function, or the class itself when it
139
+ * has none. Its frames are the class's constructions, whose `this` is the instance made.
140
+ */
141
+ export declare function constructorOf(cls: AstNode): AstNode;
142
+ /**
143
+ * A frame id that stays bounded. A merged frame's id names every frame it merges, and a call's names its caller and
144
+ * closure, so under raised caps ids grew geometrically with the depth of the chain, until a join exceeded the maximum
145
+ * string length and threw `RangeError: Invalid string length`. Past `maxFrameIdLength` an id is replaced
146
+ * by its leading characters and a digest of the whole, which is as deterministic as the id, and tells frames apart as
147
+ * well (128 bits).
148
+ */
149
+ export declare function frameId(id: string): string;
150
+ /**
151
+ * A frame for one call of `site.fn`, with the frame its arguments are evaluated in. `closure` is the frame the function
152
+ * was created in, which it reads its free variables through (`outer`), and `receiverKey` tells apart the frames of one
153
+ * call whose receivers differ: a method is instantiated once per receiver object (§5 Resolver capabilities 5).
154
+ */
155
+ export declare function callFrame(site: CallSite, caller: Frame | undefined, closure?: Frame, receiverKey?: string): Frame;
156
+ /**
157
+ * Frames in document order: by where their first call is, then the function called, then their ids, which name the
158
+ * positions of the calls out along their chains. Whatever order the resolver found them in, the order it works through
159
+ * them in is this one, so results are independent of visit order (§18 Context policy).
160
+ */
161
+ export declare function framesInDocumentOrder<T extends Frame>(frames: readonly T[]): T[];
162
+ /** Call sites in document order: by where the call is, then the function called. */
163
+ export declare const sitesInDocumentOrder: (sites: readonly CallSite[]) => CallSite[];
164
+ /** The widened frame of a function: every call of it, with the arguments evaluated in no frame (above). */
165
+ export declare function widenedFrame(fn: AstNode, calls: readonly CallSite[]): Frame;
166
+ /** Frames of one function merged into one, whose parameters take the arguments of every one of their calls. */
167
+ export declare function mergedFrame(fn: AstNode, unordered: readonly Frame[]): Frame;
168
+ /** Whether `fn` is instantiated in `frame` or a frame out from it. */
169
+ export declare function onChain(frame: Frame | undefined, fn: AstNode): boolean;
170
+ /**
171
+ * The frame `frame` runs `code` in: `frame` itself or one it runs within (`outer`), out through loops and iteration
172
+ * natives' callbacks. Undefined when none of them instantiates `code`.
173
+ */
174
+ export declare function frameOf(frame: Frame | undefined, code: AstNode): Frame | undefined;
175
+ /** The loop or call whose iterations the iteration cap cut, when `frame` is one of the iterations done, or runs in one. */
176
+ export declare function cutIn(frame: Frame | undefined): AstNode | undefined;
177
+ export {};