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,443 @@
1
+ // monkeypatchlist's scope post-passes (§5 Parsing and scope), which run above the binding-resolution seam (§19). The seam
2
+ // resolves names under standard ES2015+ block scoping; these passes correct it for how browsers run the code:
3
+ //
4
+ // 1. In a classic script, global-scope `var` and `function` declarations bind properties of the global object, and
5
+ // `let`, `const` and `class` do not.
6
+ // 2. Annex B block-function hoisting: in sloppy code, a plain function declared in a block also gets a var binding in
7
+ // its function, or in a classic script's global object (ECMA-262 B.3.2).
8
+ // 3. `with` and direct-`eval` taint: a `with` block on a name's resolution path could redirect it to an object
9
+ // property, and a direct `eval` in a sloppy function on it could declare a shadowing `var`. Both lower target
10
+ // confidence (§9) rather than change the resolution.
11
+ // 4. An unqualified `delete x` of a name these rules bind on the global object is a global delete. The resolver reports
12
+ // it, and `isStrict` rules out strict code, where it is a SyntaxError.
13
+ //
14
+ // For constant propagation (§5 Resolver capabilities 1), `constant` gives the `const` declaration a local name resolves
15
+ // to after these passes, with the taint between the two.
16
+ //
17
+ // They also place the declarations of nested code (§2): a literal direct eval's code, grafted in place of its literal
18
+ // as an arrow function (`ScopeOptions.directEvals`), declares its top-level `var`s and functions in its caller's var
19
+ // scope when it is sloppy, and in a var scope of its own when it is strict. Global code resolves a name the host
20
+ // script declares lexically at its top level to that declaration (`ScopeOptions.lexicalGlobals`), and strict eval code
21
+ // keeps its top-level `var`s to itself (`ScopeOptions.evalCode`).
22
+ import { isEvalCall, walkTree } from "./ast.js";
23
+ export function scopeRules(program, sourceType, bindings, options = {}) {
24
+ const { directEvals = new Set(), lexicalGlobals = new Set(), evalCode = false, imports } = options;
25
+ const resolution = bindings.resolve(program, sourceType);
26
+ const regions = collectRegions(program, sourceType, directEvals);
27
+ let declarations;
28
+ /**
29
+ * Whether a region's top-level `var`s and functions bind the global object's properties: a classic script's, unless
30
+ * it is strict eval code, and sloppy direct-eval code's whose caller's var scope does.
31
+ */
32
+ const bindsGlobals = (region) => {
33
+ if (region.kind === 'eval')
34
+ return !region.strict && bindsGlobals(varScopeAround(region));
35
+ return region.kind === 'program' && sourceType === 'script' && !(evalCode && region.strict);
36
+ };
37
+ /**
38
+ * Post-pass 2: an Annex B var binding in a function the reference sits in hides whatever the seam found outside it.
39
+ * Returns the var scope with that binding, if there is one, or else the var scope the seam's declaration is in.
40
+ */
41
+ const varScopeOf = (id, resolved) => {
42
+ const name = id['name'];
43
+ for (let region = innermost(regions, id.start); region !== null; region = region.parent) {
44
+ if (!isVarScope(region))
45
+ continue;
46
+ if (resolved !== 'unresolved' && declaredWithin(resolved, region))
47
+ return { scope: region };
48
+ const hoisted = region.annexB.get(name);
49
+ if (hoisted !== undefined)
50
+ return { hoisted, region };
51
+ }
52
+ return {};
53
+ };
54
+ /** What a reference resolves to: a declaration that binds the global object's property, none at all, or a local. */
55
+ const bind = (id) => {
56
+ const name = id['name'];
57
+ const resolved = resolution.reference(id);
58
+ const found = varScopeOf(id, resolved);
59
+ if ('hoisted' in found)
60
+ return bindsGlobals(found.region) ? { kind: 'block-function', node: found.hoisted } : 'local';
61
+ const { scope } = found;
62
+ // Global code sees the host script's top-level lexical declarations before the global object's properties.
63
+ if (resolved === 'unresolved')
64
+ return lexicalGlobals.has(name) ? 'local' : undefined;
65
+ const declaration = resolved.declaration;
66
+ const kind = declaration.type === 'FunctionDeclaration' ? 'function' : 'var';
67
+ const varScoped = kind === 'function' || (declaration.type === 'VariableDeclaration' && declaration['kind'] === 'var');
68
+ // A top-level var or function of direct-eval code is declared in the var scope that code's declarations go to.
69
+ if (scope?.kind === 'eval' && resolved.scopeKind === 'function' && varScoped && declaration !== scope.node) {
70
+ return bindsGlobals(scope) ? { kind, node: declaration } : 'local';
71
+ }
72
+ // Post-pass 1: a classic script's global-scope var and function bind the global object's property.
73
+ if (resolved.scopeKind !== 'global' || !varScoped || !bindsGlobals(regions[0]))
74
+ return 'local';
75
+ return { kind, node: declaration };
76
+ };
77
+ /**
78
+ * Post-pass 3: the `with` blocks and the functions around a reference, out to the first region that holds `until`,
79
+ * are on its resolution path. A `with` block could redirect the name to an object property, and a direct eval in one
80
+ * of those functions, in sloppy code, could declare a `var` there that shadows it.
81
+ */
82
+ const taintOf = (id, until) => {
83
+ const withs = [];
84
+ const evals = [];
85
+ for (let region = innermost(regions, id.start); region !== null; region = region.parent) {
86
+ if (until !== undefined && region.start <= until.start && until.end <= region.end)
87
+ break;
88
+ if (region.kind === 'with')
89
+ withs.unshift(region.site);
90
+ if (region.kind === 'function')
91
+ evals.push(...region.evals.filter((call) => bind(call['callee']) !== 'local'));
92
+ }
93
+ evals.sort((a, b) => a.start - b.start);
94
+ return { withs, evals };
95
+ };
96
+ /** The variable an import binding of a link group is, when `id` reads one (`ScopeOptions.imports`). */
97
+ const linked = (id, resolved) => {
98
+ if (imports === undefined || resolved === 'unresolved')
99
+ return undefined;
100
+ return imports(resolved.declaration, id['name']);
101
+ };
102
+ return {
103
+ resolve(id) {
104
+ const declaration = bind(id);
105
+ if (declaration === 'local')
106
+ return 'local';
107
+ // A name bound on the global object has the whole scope chain on its path. At the top of a classic script, a var
108
+ // a direct eval declares is the global object's property itself.
109
+ return { ...(declaration === undefined ? {} : { declaration }), ...taintOf(id) };
110
+ },
111
+ constant(id) {
112
+ const resolved = resolution.reference(id);
113
+ if (resolved === 'unresolved')
114
+ return undefined;
115
+ const import_ = linked(id, resolved);
116
+ const declaration = import_?.declaration ?? resolved.declaration;
117
+ if (declaration.type !== 'VariableDeclaration' || declaration['kind'] !== 'const')
118
+ return undefined;
119
+ // An import's variable is in another module: nothing between the two can shadow it.
120
+ if (import_ !== undefined)
121
+ return { declaration, withs: [], evals: [] };
122
+ if ('hoisted' in varScopeOf(id, resolved))
123
+ return undefined;
124
+ // A constant's path ends at the region it is declared in. A direct eval in the constant's own var scope can't
125
+ // shadow it: its `var` would clash with the `const`, a SyntaxError.
126
+ return { declaration, ...taintOf(id, declaration) };
127
+ },
128
+ declaration(id) {
129
+ const resolved = resolution.reference(id);
130
+ if (resolved === 'unresolved' || 'hoisted' in varScopeOf(id, resolved))
131
+ return undefined;
132
+ return linked(id, resolved)?.declaration ?? resolved.declaration;
133
+ },
134
+ nameOf(id) {
135
+ if (imports === undefined)
136
+ return id['name'];
137
+ const resolved = resolution.reference(id);
138
+ return linked(id, resolved)?.name ?? id['name'];
139
+ },
140
+ isStrict: (offset) => innermost(regions, offset).strict,
141
+ declared: (node) => (declarations ??= globalDeclarations()).get(node),
142
+ };
143
+ /** The declarations that bind names of the global object's, by node (`declared`). */
144
+ function globalDeclarations() {
145
+ const found = new Map();
146
+ const regionOf = new Map(regions.map((region) => [region.node, region]));
147
+ // The var scopes the walk is in: the program, and the direct-eval code grafted in it. It never enters a function.
148
+ const scopes = [];
149
+ const lexical = sourceType === 'script' && !evalCode;
150
+ const configurableIn = (region) => region.kind === 'eval' || evalCode;
151
+ walkTree(program, {
152
+ enter(node, parent) {
153
+ const region = regionOf.get(node);
154
+ const opens = region !== undefined && (region.kind === 'program' || region.kind === 'eval');
155
+ if (!opens && (functionTypes.has(node.type) || node.type === 'StaticBlock' || node.type === 'ClassExpression')) {
156
+ if (node.type === 'FunctionDeclaration')
157
+ functionDeclaration(node, parent);
158
+ return false;
159
+ }
160
+ if (node.type === 'ClassDeclaration') {
161
+ if (lexical && parent === program)
162
+ found.set(node, { kind: 'class', configurable: false });
163
+ return false;
164
+ }
165
+ if (opens)
166
+ scopes.push(region);
167
+ if (node.type === 'VariableDeclaration') {
168
+ const scope = scopes.at(-1);
169
+ if (node['kind'] === 'var') {
170
+ const loop = (parent?.type === 'ForInStatement' || parent?.type === 'ForOfStatement') && parent['left'] === node;
171
+ if (bindsGlobals(scope))
172
+ found.set(node, { kind: 'var', configurable: configurableIn(scope), ...(loop ? { loop } : {}) });
173
+ }
174
+ else if (lexical && parent === program && (node['kind'] === 'let' || node['kind'] === 'const')) {
175
+ found.set(node, { kind: node['kind'], configurable: false });
176
+ }
177
+ }
178
+ },
179
+ leave(node) {
180
+ if (scopes.at(-1)?.node === node)
181
+ scopes.pop();
182
+ },
183
+ });
184
+ return found;
185
+ /** A function declaration: at the top level of the var scope, or a block-level one Annex B hoists there. */
186
+ function functionDeclaration(node, parent) {
187
+ const scope = scopes.at(-1);
188
+ const name = node['id']?.['name'];
189
+ if (name === undefined || !bindsGlobals(scope))
190
+ return;
191
+ // The top level of eval code is the body of the arrow function grafted in place of its literal.
192
+ const topLevel = scope.kind === 'eval' ? parent === scope.node['body'] : parent === program;
193
+ if (topLevel)
194
+ found.set(node, { kind: 'function', configurable: configurableIn(scope) });
195
+ else if (scope.annexB.get(name) === node)
196
+ found.set(node, { kind: 'block-function', configurable: configurableIn(scope) });
197
+ }
198
+ }
199
+ }
200
+ /**
201
+ * The names a classic script declares lexically at its top level (`let`, `const`, `using`, `class`). They are global
202
+ * bindings that are not properties of the global object, and global code sees them. A module's are its own.
203
+ */
204
+ export function globalLexicalNames(program, sourceType) {
205
+ return sourceType === 'module' ? new Set() : lexicallyDeclared(program['body'], true);
206
+ }
207
+ /**
208
+ * A classic script's top-level `let`, `const` and `class` declarations, in document order, each with the identifier it
209
+ * binds: the names the scan-wide lexical-name check collects (§3 Declarations in classic scripts). A module's are its
210
+ * own, so it has none.
211
+ */
212
+ export function globalLexicalDeclarations(program, sourceType) {
213
+ if (sourceType === 'module')
214
+ return [];
215
+ return program['body'].flatMap((statement) => {
216
+ if (statement.type === 'ClassDeclaration') {
217
+ const id = statement['id'];
218
+ return id === null ? [] : [{ name: id['name'], kind: 'class', id }];
219
+ }
220
+ if (statement.type !== 'VariableDeclaration')
221
+ return [];
222
+ const kind = statement['kind'];
223
+ if (kind !== 'let' && kind !== 'const')
224
+ return [];
225
+ return statement['declarations'].flatMap((declarator) => boundIdentifiers(declarator['id']).map((id) => ({ name: id['name'], kind, id })));
226
+ });
227
+ }
228
+ /** Whether a region is a var scope for the names in it: the program, a function, or grafted direct-eval code. */
229
+ const isVarScope = (region) => region.kind === 'program' || region.kind === 'function' || region.kind === 'eval';
230
+ /**
231
+ * Whether a region is the var scope its code's `var`s and direct evals go to: sloppy direct-eval code declares them
232
+ * in its caller's var scope (ECMA-262 EvalDeclarationInstantiation), and strict eval code in its own.
233
+ */
234
+ const declaresVars = (region) => isVarScope(region) && !(region.kind === 'eval' && !region.strict);
235
+ /** The var scope that code in `region`, but outside any var scope in it, declares its `var`s in. */
236
+ function varScopeAround(region) {
237
+ let around = region.parent;
238
+ while (!declaresVars(around))
239
+ around = around.parent;
240
+ return around;
241
+ }
242
+ /**
243
+ * Whether the seam's declaration lies in `region`'s var scope or a scope inside it. A function's parameters and its
244
+ * `arguments` are declared by the function node in its own `function` scope; its name is declared outside it.
245
+ */
246
+ function declaredWithin(resolved, region) {
247
+ const declaration = resolved.declaration;
248
+ if (declaration === region.node)
249
+ return resolved.scopeKind === 'function';
250
+ return region.start <= declaration.start && declaration.end <= region.end;
251
+ }
252
+ /** The innermost region that contains `offset`. Regions are in document order, each after the regions around it. */
253
+ function innermost(regions, offset) {
254
+ let low = 0;
255
+ let high = regions.length - 1;
256
+ while (low < high) {
257
+ const middle = (low + high + 1) >> 1;
258
+ if (regions[middle].start <= offset)
259
+ low = middle;
260
+ else
261
+ high = middle - 1;
262
+ }
263
+ let region = regions[low];
264
+ while (region !== null && !(region.start <= offset && offset < region.end))
265
+ region = region.parent;
266
+ return region ?? regions[0];
267
+ }
268
+ const functionTypes = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
269
+ /**
270
+ * Walks the program once, and returns its regions in document order, with their strictness, parameters and Annex B
271
+ * bindings.
272
+ */
273
+ function collectRegions(program, sourceType, directEvals) {
274
+ const regions = [];
275
+ const open = [];
276
+ // The names each enclosing block-like scope declares lexically, with the node that opened it. A function or the
277
+ // program opens a var scope's entry: the names at its top level.
278
+ const lexical = [];
279
+ const openRegion = (kind, node, strict, params = new Set(), site) => {
280
+ const region = {
281
+ kind,
282
+ node,
283
+ ...(site === undefined ? {} : { site }),
284
+ start: node.start,
285
+ end: node.end,
286
+ parent: open.at(-1) ?? null,
287
+ strict,
288
+ params,
289
+ annexB: new Map(),
290
+ evals: [],
291
+ };
292
+ regions.push(region);
293
+ open.push(region);
294
+ };
295
+ const strict = () => open.at(-1).strict;
296
+ walkTree(program, {
297
+ enter(node, parent) {
298
+ if (parent?.type === 'WithStatement' && parent['body'] === node)
299
+ openRegion('with', node, strict(), new Set(), parent);
300
+ if (node.type === 'Program') {
301
+ const body = node['body'];
302
+ openRegion('program', node, sourceType === 'module' || hasUseStrict(body));
303
+ lexical.push({ owner: node, names: lexicallyDeclared(body, true), varScope: true });
304
+ }
305
+ else if (functionTypes.has(node.type)) {
306
+ const body = node['body'];
307
+ const statements = body.type === 'BlockStatement' ? body['body'] : [];
308
+ const params = new Set(node['params'].flatMap(boundNames));
309
+ openRegion(directEvals.has(node) ? 'eval' : 'function', node, strict() || hasUseStrict(statements), params);
310
+ lexical.push({ owner: node, names: lexicallyDeclared(statements, true), varScope: true });
311
+ }
312
+ else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
313
+ openRegion('class', node, true);
314
+ }
315
+ else if (node.type === 'BlockStatement' && !(parent !== null && functionTypes.has(parent.type))) {
316
+ // A function's body is a BlockStatement too, but it belongs to the function's var scope.
317
+ const statements = node['body'];
318
+ lexical.push({ owner: node, names: lexicallyDeclared(statements, false), varScope: false });
319
+ hoistBlockFunctions(statements);
320
+ }
321
+ else if (node.type === 'SwitchStatement') {
322
+ const statements = node['cases'].flatMap((clause) => clause['consequent']);
323
+ lexical.push({ owner: node, names: lexicallyDeclared(statements, false), varScope: false });
324
+ hoistBlockFunctions(statements);
325
+ }
326
+ else if (isEvalCall(node)) {
327
+ if (!strict())
328
+ open.findLast(declaresVars).evals.push(node);
329
+ }
330
+ else {
331
+ const names = lexicalHead(node);
332
+ if (names.length > 0)
333
+ lexical.push({ owner: node, names: new Set(names), varScope: false });
334
+ }
335
+ },
336
+ leave(node) {
337
+ if (open.at(-1)?.node === node)
338
+ open.pop();
339
+ if (lexical.at(-1)?.owner === node)
340
+ lexical.pop();
341
+ },
342
+ });
343
+ return regions;
344
+ /**
345
+ * Annex B (B.3.2.1, B.3.2.2): in sloppy code, each plain function declared directly in a block or case clause gets a
346
+ * var binding in its var scope, unless its name is a parameter, or declaring `var` of it there would clash with a
347
+ * lexical declaration in an enclosing block or at the top of the var scope.
348
+ */
349
+ function hoistBlockFunctions(statements) {
350
+ if (strict())
351
+ return;
352
+ const region = open.findLast(isVarScope);
353
+ for (const statement of statements) {
354
+ if (statement.type !== 'FunctionDeclaration' || statement['async'] === true || statement['generator'] === true) {
355
+ continue;
356
+ }
357
+ const name = statement['id']['name'];
358
+ if (region.params.has(name) || clashes(name))
359
+ continue;
360
+ if (!region.annexB.has(name))
361
+ region.annexB.set(name, statement);
362
+ }
363
+ }
364
+ /** Whether an enclosing block, below the one just entered, or the var scope's top level declares `name` lexically. */
365
+ function clashes(name) {
366
+ for (let i = lexical.length - 2; i >= 0; i--) {
367
+ if (lexical[i].names.has(name))
368
+ return true;
369
+ if (lexical[i].varScope)
370
+ return false;
371
+ }
372
+ return false;
373
+ }
374
+ }
375
+ /**
376
+ * The names a `for` head declares lexically, or a catch clause whose parameter is a pattern. A catch parameter that is
377
+ * a plain identifier doesn't clash with a `var` of its name in the clause (B.3.4).
378
+ */
379
+ function lexicalHead(node) {
380
+ if (node.type === 'ForStatement' || node.type === 'ForInStatement' || node.type === 'ForOfStatement') {
381
+ const head = (node.type === 'ForStatement' ? node['init'] : node['left']);
382
+ if (head?.type !== 'VariableDeclaration' || head['kind'] === 'var')
383
+ return [];
384
+ return head['declarations'].flatMap((declarator) => boundNames(declarator['id']));
385
+ }
386
+ if (node.type === 'CatchClause') {
387
+ const param = node['param'];
388
+ return param === null || param.type === 'Identifier' ? [] : boundNames(param);
389
+ }
390
+ return [];
391
+ }
392
+ /**
393
+ * The names a statement list declares lexically: `let`, `const`, `using` and `class`, and in a block (not at the top of
394
+ * a function or script, where they are var-scoped) function declarations.
395
+ */
396
+ function lexicallyDeclared(statements, topLevel) {
397
+ const names = new Set();
398
+ for (const statement of statements) {
399
+ if (statement.type === 'VariableDeclaration' && statement['kind'] !== 'var') {
400
+ for (const declarator of statement['declarations']) {
401
+ for (const name of boundNames(declarator['id']))
402
+ names.add(name);
403
+ }
404
+ }
405
+ else if (statement.type === 'ClassDeclaration' || (!topLevel && statement.type === 'FunctionDeclaration')) {
406
+ const id = statement['id'];
407
+ if (id !== null)
408
+ names.add(id['name']);
409
+ }
410
+ }
411
+ return names;
412
+ }
413
+ /** The names a binding pattern binds. */
414
+ export function boundNames(pattern) {
415
+ return boundIdentifiers(pattern).map((id) => id['name']);
416
+ }
417
+ /** The identifiers a binding pattern binds, in document order. */
418
+ export function boundIdentifiers(pattern) {
419
+ switch (pattern.type) {
420
+ case 'Identifier':
421
+ return [pattern];
422
+ case 'ObjectPattern':
423
+ return pattern['properties'].flatMap((property) => boundIdentifiers((property.type === 'RestElement' ? property['argument'] : property['value'])));
424
+ case 'ArrayPattern':
425
+ return pattern['elements'].flatMap((element) => (element === null ? [] : boundIdentifiers(element)));
426
+ case 'AssignmentPattern':
427
+ return boundIdentifiers(pattern['left']);
428
+ case 'RestElement':
429
+ return boundIdentifiers(pattern['argument']);
430
+ default:
431
+ return [];
432
+ }
433
+ }
434
+ /** Whether a directive prologue holds a Use Strict Directive: exactly `"use strict"` or `'use strict'`. */
435
+ function hasUseStrict(statements) {
436
+ for (const statement of statements) {
437
+ if (typeof statement['directive'] !== 'string')
438
+ return false;
439
+ if (statement['directive'] === 'use strict')
440
+ return true;
441
+ }
442
+ return false;
443
+ }
@@ -0,0 +1,56 @@
1
+ import { type AstNode } from './ast.ts';
2
+ import type { BindingResolver } from './binding.ts';
3
+ import { Folder, type ChunkLoadSite } from './chunk-loads.ts';
4
+ import type { GeneratedLocation } from './types.ts';
5
+ /**
6
+ * An injected script's `src` write (above): where its value is, the URLs it folds to, where each alternative that
7
+ * doesn't fold is, and the element's `type` alternatives (undefined: no `type` is set; null: one that doesn't fold)
8
+ * the constants its `integrity` may be (none when it has no write, or one that doesn't fold), and whether its
9
+ * `noModule` is set: every write is truthy, or every one is falsy or there is none, or the writes differ or one doesn't
10
+ * fold.
11
+ */
12
+ export interface InjectedSite {
13
+ location: GeneratedLocation;
14
+ urls: string[];
15
+ unknown: GeneratedLocation[];
16
+ types: (string | null | undefined)[];
17
+ integrity: string[];
18
+ noModule: 'no' | 'yes' | 'maybe';
19
+ }
20
+ /** A worker URL that isn't a literal: where it is, the URLs it folds to, and where each unknown alternative is. */
21
+ export interface WorkerSite {
22
+ location: GeneratedLocation;
23
+ urls: string[];
24
+ unknown: GeneratedLocation[];
25
+ }
26
+ /** What an artifact's code loads that only the resolver can see. */
27
+ export interface ScriptLoads {
28
+ chunks: ChunkLoadSite[];
29
+ injected: InjectedSite[];
30
+ workers: WorkerSite[];
31
+ }
32
+ /**
33
+ * The script loads of an artifact's code at `url` (its own URL, for `import.meta.url`). None when it doesn't parse.
34
+ */
35
+ export declare function scriptLoads(text: string, sourceType: 'script' | 'module', bindings: BindingResolver, url: string): ScriptLoads;
36
+ /** What code does to one script element it creates. */
37
+ interface Element {
38
+ src: AstNode[];
39
+ type: AstNode[];
40
+ integrity: AstNode[];
41
+ /** Each `noModule` write: the value assigned, or whether `setAttribute` sets it (any value) or `removeAttribute` clears it. */
42
+ noModule: (AstNode | boolean)[];
43
+ text: AstNode[];
44
+ inserts: AstNode[];
45
+ }
46
+ /**
47
+ * The script elements a program creates, each by its creation call, with what the program sets on them and the
48
+ * insertion calls that insert them, in document order.
49
+ */
50
+ export declare function scriptElements(program: AstNode, folder: Folder): Map<AstNode, Element>;
51
+ /**
52
+ * The code an injected element with no `src` carries, by the call that first inserts it: the expressions its
53
+ * text is set to.
54
+ */
55
+ export declare function scriptTexts(program: AstNode, folder: Folder): Map<AstNode, AstNode[]>;
56
+ export {};