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,286 @@
1
+ // Whether code declares a name in scope at a node (§2 Discovery: a worker call is dropped only when a declaration of
2
+ // its root name is in scope at the call). A syntactic scope walk up the node's ancestors, by ECMAScript's scoping rules:
3
+ // `var` and top-level functions belong to the nearest function, static block or program; `let`, `const`, `class` and
4
+ // block-level functions to their block; parameters, a catch clause's parameter, a function expression's and a class's
5
+ // own name to what they enclose. A function's parameter list doesn't see what its body declares. In sloppy code, a
6
+ // block-level function also declares its name in the nearest function or program, as Annex B's block-function hoisting
7
+ // does (§5 post-pass 2). Syntax only.
8
+ /**
9
+ * A check of whether a declaration of a name is in scope at a node of one program, given `path`, the chain of nodes
10
+ * from the program down to it, each the parent of the next. The names each scope declares are gathered once.
11
+ */
12
+ export function scopeCheck(sourceType) {
13
+ const declared = new Map();
14
+ return (path, name) => {
15
+ // Module code is strict, and so is code under a `"use strict"` directive or in a class.
16
+ let strict = sourceType === 'module';
17
+ for (let i = 0; i < path.length - 1; i++) {
18
+ const scope = path[i];
19
+ const child = path[i + 1];
20
+ strict ||= becomesStrict(scope, child);
21
+ const names = namesFor(declared, scope, child, strict);
22
+ if (names?.has(name))
23
+ return true;
24
+ }
25
+ return false;
26
+ };
27
+ }
28
+ /** Whether the code under `child` of `node` is strict code though the code around `node` isn't. */
29
+ function becomesStrict(node, child) {
30
+ switch (node.type) {
31
+ case 'Program':
32
+ return hasUseStrict(node['body']);
33
+ case 'FunctionDeclaration':
34
+ case 'FunctionExpression':
35
+ case 'ArrowFunctionExpression': {
36
+ const body = node['body'];
37
+ return child !== node['id'] && body.type === 'BlockStatement' && hasUseStrict(body['body']);
38
+ }
39
+ case 'ClassDeclaration':
40
+ case 'ClassExpression':
41
+ return child !== node['id'];
42
+ default:
43
+ return false;
44
+ }
45
+ }
46
+ /** Whether a program's or function body's directive prologue holds `"use strict"`. */
47
+ function hasUseStrict(statements) {
48
+ for (const statement of statements) {
49
+ if (typeof statement['directive'] !== 'string')
50
+ return false;
51
+ if (statement['directive'] === 'use strict')
52
+ return true;
53
+ }
54
+ return false;
55
+ }
56
+ /**
57
+ * The names `scope` declares for the code under its child `child`, or undefined when it declares none there. What a
58
+ * scope declares for the code it encloses is gathered once.
59
+ */
60
+ function namesFor(cache, scope, child, strict) {
61
+ // A function's or class's name, and a switch statement's discriminant, are outside the scope the node makes.
62
+ if (child === scope['id'] || child === scope['discriminant'])
63
+ return undefined;
64
+ // Code in a function's parameter list sees its parameters and a function expression's own name, but not what its body
65
+ // declares, which gets an environment of its own (ECMAScript FunctionDeclarationInstantiation).
66
+ if (isFunction(scope) && child !== scope['body'])
67
+ return new Set(parameterNames(scope));
68
+ let names = cache.get(scope);
69
+ if (names === undefined) {
70
+ const declared = declaredFor(scope, strict);
71
+ if (declared === undefined)
72
+ return undefined;
73
+ cache.set(scope, (names = new Set(declared)));
74
+ }
75
+ return names;
76
+ }
77
+ /**
78
+ * The names a node declares for the code it encloses, if it makes a scope: a function's for its body (its parameter
79
+ * list sees only `parameterNames`, and a function declaration's own name is its enclosing scope's), a switch
80
+ * statement's for its case block, a class's for its heritage and body (a class declaration's name is its enclosing
81
+ * scope's too).
82
+ */
83
+ function declaredFor(scope, strict) {
84
+ switch (scope.type) {
85
+ case 'Program':
86
+ return varScoped(scope['body'], strict);
87
+ case 'FunctionDeclaration':
88
+ case 'FunctionExpression':
89
+ case 'ArrowFunctionExpression': {
90
+ const body = scope['body'];
91
+ return [
92
+ ...parameterNames(scope),
93
+ ...(body.type === 'BlockStatement' ? varScoped(body['body'], strict) : []),
94
+ ];
95
+ }
96
+ case 'StaticBlock':
97
+ return varScoped(scope['body'], true);
98
+ case 'BlockStatement':
99
+ return lexicallyDeclared(scope['body']);
100
+ case 'SwitchStatement':
101
+ return lexicallyDeclared(scope['cases'].flatMap((node) => node['consequent']));
102
+ case 'ForStatement':
103
+ return lexicalDeclaration(scope['init']);
104
+ case 'ForInStatement':
105
+ case 'ForOfStatement':
106
+ // The loop's `let` or `const` names are in scope, in their temporal dead zone, while its object is evaluated.
107
+ return lexicalDeclaration(scope['left']);
108
+ case 'CatchClause':
109
+ return patternNames(scope['param']);
110
+ case 'ClassDeclaration':
111
+ case 'ClassExpression':
112
+ return patternNames(scope['id']);
113
+ default:
114
+ return undefined;
115
+ }
116
+ }
117
+ /** Whether a node is a function, which makes a scope for its parameters and body. */
118
+ function isFunction(node) {
119
+ return (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression');
120
+ }
121
+ /** The names a function's parameter list and body both see: its parameters, and a function expression's own name. */
122
+ function parameterNames(fn) {
123
+ return [
124
+ ...(fn.type === 'FunctionExpression' ? patternNames(fn['id']) : []),
125
+ ...fn['params'].flatMap(patternNames),
126
+ ];
127
+ }
128
+ /** The names a variable declaration binds. */
129
+ function declaredNames(declaration) {
130
+ return declaration['declarations'].flatMap((declarator) => patternNames(declarator['id']));
131
+ }
132
+ /** The names a `let`, `const` or `using` declaration binds, or none for a `var` declaration or anything else. */
133
+ function lexicalDeclaration(node) {
134
+ if (node?.type !== 'VariableDeclaration' || node['kind'] === 'var')
135
+ return [];
136
+ return declaredNames(node);
137
+ }
138
+ /**
139
+ * The names a statement list declares lexically, as a block's or a case block's does: its `let`, `const` and `using`
140
+ * declarations, classes and functions, a labelled function included.
141
+ */
142
+ function lexicallyDeclared(statements) {
143
+ return statements.flatMap((statement) => {
144
+ const node = unlabelled(statement);
145
+ if (node.type === 'VariableDeclaration')
146
+ return lexicalDeclaration(node);
147
+ if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') {
148
+ return patternNames(node['id']);
149
+ }
150
+ return [];
151
+ });
152
+ }
153
+ /** The statement a chain of labels labels. */
154
+ function unlabelled(statement) {
155
+ let node = statement;
156
+ while (node.type === 'LabeledStatement')
157
+ node = node['body'];
158
+ return node;
159
+ }
160
+ /**
161
+ * The names a function body's, static block's or program's statements declare in its var scope: every declaration at
162
+ * its top level (a program's imports and exports included), each `var` below it outside nested functions and classes,
163
+ * and, in sloppy code, each plain function declared in a block below it (Annex B.3.3), unless a `let`, `const`, class
164
+ * or function of that name in a block between them would clash with a `var` of it.
165
+ */
166
+ function varScoped(statements, strict) {
167
+ const names = [];
168
+ const stack = [];
169
+ for (const statement of statements) {
170
+ const node = unlabelled(exported(statement));
171
+ switch (node.type) {
172
+ case 'ImportDeclaration':
173
+ for (const specifier of node['specifiers'])
174
+ names.push(...patternNames(specifier['local']));
175
+ break;
176
+ case 'VariableDeclaration':
177
+ names.push(...declaredNames(node));
178
+ break;
179
+ case 'FunctionDeclaration':
180
+ case 'ClassDeclaration':
181
+ names.push(...patternNames(node['id']));
182
+ break;
183
+ default:
184
+ stack.push({ node, enclosing: [], direct: false });
185
+ }
186
+ }
187
+ for (let item = stack.pop(); item !== undefined; item = stack.pop()) {
188
+ const { node, enclosing, direct } = item;
189
+ const push = (child, scope, inList = false) => {
190
+ if (child === null || child === undefined)
191
+ return;
192
+ const inside = scope === undefined ? enclosing : [...enclosing, scope];
193
+ stack.push({ node: unlabelled(child), enclosing: inside, direct: inList });
194
+ };
195
+ switch (node.type) {
196
+ case 'VariableDeclaration':
197
+ if (node['kind'] === 'var') {
198
+ names.push(...declaredNames(node));
199
+ }
200
+ break;
201
+ case 'FunctionDeclaration': {
202
+ if (strict || node['generator'] === true || node['async'] === true)
203
+ break;
204
+ const [name] = patternNames(node['id']);
205
+ // The block the function is declared in declares it lexically; only the blocks outside that one can clash.
206
+ const between = direct ? enclosing.slice(0, -1) : enclosing;
207
+ if (name !== undefined && !between.some((scope) => scope.includes(name)))
208
+ names.push(name);
209
+ break;
210
+ }
211
+ case 'BlockStatement': {
212
+ const body = node['body'];
213
+ const scope = lexicallyDeclared(body);
214
+ for (const child of body)
215
+ push(child, scope, true);
216
+ break;
217
+ }
218
+ case 'IfStatement':
219
+ push(node['consequent']);
220
+ push(node['alternate']);
221
+ break;
222
+ case 'ForStatement':
223
+ case 'ForInStatement':
224
+ case 'ForOfStatement': {
225
+ const head = (node.type === 'ForStatement' ? node['init'] : node['left']);
226
+ const scope = lexicalDeclaration(head);
227
+ if (head?.type === 'VariableDeclaration')
228
+ push(head);
229
+ push(node['body'], scope);
230
+ break;
231
+ }
232
+ case 'WhileStatement':
233
+ case 'DoWhileStatement':
234
+ case 'WithStatement':
235
+ push(node['body']);
236
+ break;
237
+ case 'TryStatement': {
238
+ push(node['block']);
239
+ const handler = node['handler'];
240
+ if (handler !== null) {
241
+ // A `var` may redeclare a catch clause's plain parameter, but not a destructured one's names (Annex B.3.4).
242
+ const param = handler['param'];
243
+ push(handler['body'], param === null || param.type === 'Identifier' ? undefined : patternNames(param));
244
+ }
245
+ push(node['finalizer']);
246
+ break;
247
+ }
248
+ case 'SwitchStatement': {
249
+ const consequents = node['cases'].flatMap((node) => node['consequent']);
250
+ const scope = lexicallyDeclared(consequents);
251
+ for (const child of consequents)
252
+ push(child, scope, true);
253
+ break;
254
+ }
255
+ default:
256
+ break;
257
+ }
258
+ }
259
+ return names;
260
+ }
261
+ /** The declaration an `export` statement exports, or the statement itself. */
262
+ function exported(statement) {
263
+ if (statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration') {
264
+ const declaration = statement['declaration'];
265
+ if (declaration !== null)
266
+ return declaration;
267
+ }
268
+ return statement;
269
+ }
270
+ /** The names a binding pattern binds. */
271
+ function patternNames(node) {
272
+ switch (node?.type) {
273
+ case 'Identifier':
274
+ return [node['name']];
275
+ case 'ObjectPattern':
276
+ return node['properties'].flatMap((property) => patternNames((property.type === 'RestElement' ? property['argument'] : property['value'])));
277
+ case 'ArrayPattern':
278
+ return node['elements'].flatMap(patternNames);
279
+ case 'AssignmentPattern':
280
+ return patternNames(node['left']);
281
+ case 'RestElement':
282
+ return patternNames(node['argument']);
283
+ default:
284
+ return [];
285
+ }
286
+ }
@@ -0,0 +1,56 @@
1
+ /** What an artifact's trailing comments name: a source map URL and a debug ID, either possibly absent. */
2
+ export interface Annotations {
3
+ sourceMappingURL: string | null;
4
+ debugId: string | null;
5
+ }
6
+ /**
7
+ * The annotations of an artifact, as ECMA-426 extracts a source map URL without parsing (§ Linking generated code, in
8
+ * the current draft), with a line scan in place of a lexer: the last `//# sourceMappingURL=` (or `//@`) line comment
9
+ * that only whitespace and line comments follow, and the same for `//# debugId=`. A line comment that names neither
10
+ * leaves them. Code resets both, and so does a block comment, which ECMA-426 no longer reads an annotation from. An
11
+ * empty value names nothing, and resets its annotation. A block comment that runs past its line counts as code.
12
+ *
13
+ * With no lexer, a `//` inside a string, a template or a regular expression reads as a comment. So a line comment whose
14
+ * own text (after the `//`) holds a `"`, `'` or backtick counts as code, as the draft's check has it: it resets both,
15
+ * whatever its form, and an annotation after it still names (§2 Source maps). An annotation inside a literal is reset,
16
+ * because a later line closes the literal with a quote, and that line is code or a quoted comment.
17
+ */
18
+ export declare function annotations(text: string): Annotations;
19
+ /**
20
+ * The text a `data:` URL holds, decoded as UTF-8 whatever its media type (§17: maps are read as JSON whatever their
21
+ * type), or null when it is malformed (Fetch § data: URL processor).
22
+ */
23
+ export declare function dataUrlText(url: URL): string | null;
24
+ /**
25
+ * The raw JSON of a map's text, with a leading BOM and a `)]}'` first line stripped (ECMA-426 § Fetching source maps),
26
+ * or undefined when it isn't JSON.
27
+ */
28
+ export declare function rawMap(text: string): unknown;
29
+ /** The `debugId` of a map's raw JSON, when it has one. */
30
+ export declare function mapDebugId(raw: unknown): string | null;
31
+ /** What a debug ID is compared by: two are equal when they are equal ignoring ASCII case. */
32
+ export declare function debugIdKey(debugId: string): string;
33
+ /**
34
+ * How an artifact's text is split into lines for its map (§2 Verification): at §5's terminators (`ecmascript`: LF, CR,
35
+ * CRLF, U+2028 and U+2029), or only at LF and CR (`lf-cr`), which some tools use when the artifact holds a raw U+2028
36
+ * or U+2029.
37
+ */
38
+ export type LineReading = 'ecmascript' | 'lf-cr';
39
+ /**
40
+ * The line reading under which a candidate map verifies against an artifact (§2 Source maps), whatever method found
41
+ * it, or null when it doesn't verify:
42
+ *
43
+ * - the map is `version` 3, and reads as a map (`FlattenMap` flattens a sectioned one)
44
+ * - every mapped generated line and column exists in the artifact: a column may be the end of its line
45
+ * - the `debugId`s are equal, ignoring ASCII case, when both sides have one
46
+ * - the token spot checks pass: no mapped position splits a token of the artifact, which it would when both the code
47
+ * units around it are identifier parts
48
+ *
49
+ * Lines end at an ECMAScript LineTerminatorSequence (§5 Positions). Tools that split lines only at LF and CR differ
50
+ * when the artifact holds a raw U+2028 or U+2029, so such an artifact verifies when either reading holds. Attribution
51
+ * (§10) looks positions up with the reading that verified, and with the LF/CR reading when both do.
52
+ */
53
+ export declare function verification(artifact: {
54
+ text: string;
55
+ debugId: string | null;
56
+ }, raw: unknown): LineReading | null;
@@ -0,0 +1,209 @@
1
+ // Source-map syntax (§2 Source maps, §19 Discovery): the annotations at the end of an artifact, `data:` URLs, and
2
+ // the verification of a candidate map against the artifact it would pair with. Maps are read with
3
+ // `@jridgewell/trace-mapping` (`FlattenMap`, which flattens a sectioned map), and `debugId` from the raw JSON.
4
+ import { decodedMappings, FlattenMap } from '@jridgewell/trace-mapping';
5
+ /** The comment contents that name a source map URL or a debug ID (ECMA-426 § MatchSourceMapURL, and Debug IDs). */
6
+ const patterns = {
7
+ sourceMappingURL: /^[@#]\s*sourceMappingURL=(\S*?)\s*$/,
8
+ debugId: /^[@#]\s*debugId=(\S*?)\s*$/,
9
+ };
10
+ /** ECMAScript WhiteSpace: tab, vertical tab, form feed, ZWNBSP, and every space separator. */
11
+ const whiteSpace = /[\t\v\f\ufeff\p{Zs}]/u;
12
+ /** The code points whose presence in a line comment's own text makes it count as code (ECMA-426, without parsing). */
13
+ const quotes = /["'`]/;
14
+ /**
15
+ * The annotations of an artifact, as ECMA-426 extracts a source map URL without parsing (§ Linking generated code, in
16
+ * the current draft), with a line scan in place of a lexer: the last `//# sourceMappingURL=` (or `//@`) line comment
17
+ * that only whitespace and line comments follow, and the same for `//# debugId=`. A line comment that names neither
18
+ * leaves them. Code resets both, and so does a block comment, which ECMA-426 no longer reads an annotation from. An
19
+ * empty value names nothing, and resets its annotation. A block comment that runs past its line counts as code.
20
+ *
21
+ * With no lexer, a `//` inside a string, a template or a regular expression reads as a comment. So a line comment whose
22
+ * own text (after the `//`) holds a `"`, `'` or backtick counts as code, as the draft's check has it: it resets both,
23
+ * whatever its form, and an annotation after it still names (§2 Source maps). An annotation inside a literal is reset,
24
+ * because a later line closes the literal with a quote, and that line is code or a quoted comment.
25
+ */
26
+ export function annotations(text) {
27
+ const found = { sourceMappingURL: null, debugId: null };
28
+ /** What a comment names for `key`: its value, null for an empty one, or undefined when it doesn't name `key`. */
29
+ const match = (comment, key) => {
30
+ const value = patterns[key].exec(comment)?.[1];
31
+ return value === undefined ? undefined : value || null;
32
+ };
33
+ for (const line of text.split(/\r\n|[\n\r\u2028\u2029]/)) {
34
+ let position = 0;
35
+ while (position < line.length) {
36
+ const first = line[position++];
37
+ if (whiteSpace.test(first))
38
+ continue;
39
+ const second = line[position];
40
+ if (first === '/' && second === '/') {
41
+ const comment = line.slice(position + 1);
42
+ if (quotes.test(comment)) {
43
+ found.sourceMappingURL = null;
44
+ found.debugId = null;
45
+ break;
46
+ }
47
+ const url = match(comment, 'sourceMappingURL');
48
+ const debugId = match(comment, 'debugId');
49
+ if (url !== undefined)
50
+ found.sourceMappingURL = url;
51
+ if (debugId !== undefined)
52
+ found.debugId = debugId;
53
+ break;
54
+ }
55
+ if (first === '/' && second === '*') {
56
+ found.sourceMappingURL = null;
57
+ found.debugId = null;
58
+ // Skipped whole, so that a `//` inside it is not read as a line comment.
59
+ const end = line.indexOf('*/', position + 1);
60
+ if (end === -1)
61
+ break;
62
+ position = end + 2;
63
+ continue;
64
+ }
65
+ found.sourceMappingURL = null;
66
+ found.debugId = null;
67
+ // Code up to the next `/` would only reset them again.
68
+ const slash = line.indexOf('/', position);
69
+ if (slash === -1)
70
+ break;
71
+ position = slash;
72
+ }
73
+ }
74
+ return found;
75
+ }
76
+ const decoder = new TextDecoder('utf-8');
77
+ /**
78
+ * The text a `data:` URL holds, decoded as UTF-8 whatever its media type (§17: maps are read as JSON whatever their
79
+ * type), or null when it is malformed (Fetch § data: URL processor).
80
+ */
81
+ export function dataUrlText(url) {
82
+ // The URL serialized without its fragment, even an empty one, which `url.hash` doesn't show: a serialized URL's first
83
+ // `#` starts its fragment.
84
+ const fragment = url.href.indexOf('#');
85
+ const body = url.href.slice('data:'.length, fragment === -1 ? undefined : fragment);
86
+ const comma = body.indexOf(',');
87
+ if (comma === -1)
88
+ return null;
89
+ const bytes = percentDecode(body.slice(comma + 1));
90
+ if (!/;[\t\n\f\r ]*base64[\t\n\f\r ]*$/i.test(body.slice(0, comma)))
91
+ return decoder.decode(bytes);
92
+ try {
93
+ // Base64 is ASCII: any other byte makes `atob` throw.
94
+ const binary = atob(decoder.decode(bytes).replace(/[\t\n\f\r ]/g, ''));
95
+ return decoder.decode(Uint8Array.from(binary, (character) => character.charCodeAt(0)));
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ /** The bytes of a URL component's percent-encoding (URL § percent-decode): an invalid escape is left as it is. */
102
+ function percentDecode(text) {
103
+ const bytes = [...new TextEncoder().encode(text)];
104
+ const out = [];
105
+ for (let i = 0; i < bytes.length; i++) {
106
+ const hex = String.fromCharCode(bytes[i + 1] ?? 0, bytes[i + 2] ?? 0);
107
+ if (bytes[i] === 0x25 && /^[0-9a-f]{2}$/i.test(hex)) {
108
+ out.push(Number.parseInt(hex, 16));
109
+ i += 2;
110
+ }
111
+ else
112
+ out.push(bytes[i]);
113
+ }
114
+ return Uint8Array.from(out);
115
+ }
116
+ /**
117
+ * The raw JSON of a map's text, with a leading BOM and a `)]}'` first line stripped (ECMA-426 § Fetching source maps),
118
+ * or undefined when it isn't JSON.
119
+ */
120
+ export function rawMap(text) {
121
+ let json = text.startsWith('\ufeff') ? text.slice(1) : text;
122
+ if (json.startsWith(")]}'"))
123
+ json = json.slice(json.search(/[\n\r]|$/));
124
+ try {
125
+ return JSON.parse(json);
126
+ }
127
+ catch {
128
+ return undefined;
129
+ }
130
+ }
131
+ /** The `debugId` of a map's raw JSON, when it has one. */
132
+ export function mapDebugId(raw) {
133
+ return isObject(raw) && typeof raw.debugId === 'string' ? raw.debugId : null;
134
+ }
135
+ /** What a debug ID is compared by: two are equal when they are equal ignoring ASCII case. */
136
+ export function debugIdKey(debugId) {
137
+ return asciiLowercase(debugId);
138
+ }
139
+ /** The line terminators of each reading. */
140
+ const lineTerminators = {
141
+ ecmascript: /\r\n|[\n\r\u2028\u2029]/,
142
+ 'lf-cr': /\r\n|[\n\r]/,
143
+ };
144
+ /**
145
+ * The line reading under which a candidate map verifies against an artifact (§2 Source maps), whatever method found
146
+ * it, or null when it doesn't verify:
147
+ *
148
+ * - the map is `version` 3, and reads as a map (`FlattenMap` flattens a sectioned one)
149
+ * - every mapped generated line and column exists in the artifact: a column may be the end of its line
150
+ * - the `debugId`s are equal, ignoring ASCII case, when both sides have one
151
+ * - the token spot checks pass: no mapped position splits a token of the artifact, which it would when both the code
152
+ * units around it are identifier parts
153
+ *
154
+ * Lines end at an ECMAScript LineTerminatorSequence (§5 Positions). Tools that split lines only at LF and CR differ
155
+ * when the artifact holds a raw U+2028 or U+2029, so such an artifact verifies when either reading holds. Attribution
156
+ * (§10) looks positions up with the reading that verified, and with the LF/CR reading when both do.
157
+ */
158
+ export function verification(artifact, raw) {
159
+ if (!isObject(raw) || raw.version !== 3)
160
+ return null;
161
+ if ('sections' in raw ? !Array.isArray(raw.sections) : typeof raw.mappings !== 'string')
162
+ return null;
163
+ const debugId = mapDebugId(raw);
164
+ if (debugId !== null && artifact.debugId !== null && debugIdKey(debugId) !== debugIdKey(artifact.debugId)) {
165
+ return null;
166
+ }
167
+ let mapped;
168
+ try {
169
+ mapped = decodedMappings(new FlattenMap(raw));
170
+ }
171
+ catch {
172
+ return null;
173
+ }
174
+ const readings = /[\u2028\u2029]/.test(artifact.text) ? ['lf-cr', 'ecmascript'] : ['ecmascript'];
175
+ return readings.find((reading) => positionsHold(artifact.text.split(lineTerminators[reading]), mapped)) ?? null;
176
+ }
177
+ /** Whether every mapped generated position is in `lines`, and starts a token there. */
178
+ function positionsHold(lines, mapped) {
179
+ for (const [index, segments] of mapped.entries()) {
180
+ if (segments.length === 0)
181
+ continue;
182
+ const line = lines[index];
183
+ if (line === undefined)
184
+ return false;
185
+ for (const [column] of segments) {
186
+ if (!(column >= 0 && column <= line.length))
187
+ return false;
188
+ if (column > 0 && identifierPart(line.charCodeAt(column - 1)) && identifierPart(line.charCodeAt(column))) {
189
+ return false;
190
+ }
191
+ }
192
+ }
193
+ return true;
194
+ }
195
+ const identifierPartPattern = /[$\u200c\u200d\p{ID_Continue}]/u;
196
+ /** Whether a UTF-16 code unit is an identifier part (ECMA-262 IdentifierPartChar). NaN, past the end, is not. */
197
+ function identifierPart(code) {
198
+ if (Number.isNaN(code))
199
+ return false;
200
+ if (code < 0x80) {
201
+ return (code >= 0x30 && code <= 0x39) || (code >= 0x41 && code <= 0x5a) || (code >= 0x61 && code <= 0x7a) ||
202
+ code === 0x24 || code === 0x5f;
203
+ }
204
+ return identifierPartPattern.test(String.fromCharCode(code));
205
+ }
206
+ const asciiLowercase = (text) => text.replace(/[A-Z]/g, (letter) => String.fromCharCode(letter.charCodeAt(0) + 32));
207
+ function isObject(value) {
208
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
209
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A worker marker (§2 Realm): a top-level `importScripts` call, or a `self.addEventListener` call for an `install`,
3
+ * `fetch` or `message` event, with the call's UTF-16 range.
4
+ */
5
+ export interface WorkerMarker {
6
+ marker: 'importScripts' | 'install' | 'fetch' | 'message';
7
+ start: number;
8
+ end: number;
9
+ }
10
+ /**
11
+ * The source type of a file no reference loads, and its worker markers in source order. Its source type comes from its
12
+ * syntax (`bySyntax`), and code that parses as neither has no markers.
13
+ */
14
+ export declare function sweptCode(text: string): {
15
+ sourceType: 'script' | 'module';
16
+ markers: WorkerMarker[];
17
+ };
18
+ /**
19
+ * The worker markers in code of the source type `sourceType`, in source order: none when it doesn't parse as that type.
20
+ */
21
+ export declare function workerMarkersAs(text: string, sourceType: 'script' | 'module'): WorkerMarker[];
22
+ /**
23
+ * The source type of code no reference says how to load, as a sweep-only file's (§2 Sweep): a `module` when it parses
24
+ * only as a module, because it holds an `import` or `export` declaration, `import.meta` or a top-level `await`, and a
25
+ * `script` otherwise. Code that parses as neither is a `script`, which the resolver reports as a `parse-failure`.
26
+ */
27
+ export declare function sourceTypeBySyntax(text: string): 'script' | 'module';