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,591 @@
1
+ // The references a script or module makes in its code, taken from one oxc parse (§5, §19 Discovery): the parse yields
2
+ // imports, exports and dynamic imports, so no separate module lexer is used, and its tree holds the worker URLs and
3
+ // `importScripts` calls. Syntax only: which realm has which global is for discovery to say.
4
+ import { visitorKeys } from 'oxc-parser';
5
+ import { scopeCheck } from "./scopes.js";
6
+ import { isNode, parseCode } from "./syntax.js";
7
+ /** The declarations that can name a module: static imports and re-exports. */
8
+ const declarations = new Set(['ImportDeclaration', 'ExportAllDeclaration', 'ExportNamedDeclaration']);
9
+ const none = { modules: [], workers: [], unsupported: false };
10
+ /**
11
+ * The references in `text`, parsed with `sourceType`. None when the code doesn't parse, as nothing of it runs: that
12
+ * includes a classic script with an `import` or `export` declaration, which oxc reports only among the semantic errors
13
+ * §5's options leave out.
14
+ *
15
+ * - **Modules:** its static imports and re-exports, and its dynamic imports, except those with an import attribute
16
+ * (§2 Import maps): a `type` names a module that is not JavaScript, and a browser supports no other key. A dynamic
17
+ * import's specifier is a literal when it is a string literal, or a template literal with no substitutions. One
18
+ * whose options have a part that can't be read (`importAttributes`) is a request whose attributes can't be read
19
+ * (`readableOptions: false`), and is followed only when another branch names a JavaScript module (`follow`).
20
+ * - **Workers:** the worker scripts it loads, with a literal URL or not (see `workerRequests`).
21
+ */
22
+ export function requests(text, sourceType) {
23
+ // A keyword is never written with escapes, so code without the word `import` or `export` requests no module. Code
24
+ // without `Worker` (in `Worker`, `SharedWorker` and `serviceWorker`) or `importScripts` loads no worker script.
25
+ const modules = /\b(?:import|export)\b/.test(text);
26
+ const workers = /Worker|importScripts/.test(text);
27
+ if (!modules && !workers)
28
+ return none;
29
+ const parsed = parseCode(text, sourceType);
30
+ if (parsed === undefined)
31
+ return none;
32
+ const { program } = parsed;
33
+ return {
34
+ modules: modules ? moduleRequests(program, parsed.module.dynamicImports, sourceType) : [],
35
+ workers: workers ? workerRequests(program, sourceType) : [],
36
+ unsupported: modules && unsupportedAttributes(program),
37
+ };
38
+ }
39
+ /** The module types a browser supports besides JavaScript (HTML § HostGetSupportedImportAttributes, module type). */
40
+ const supportedTypes = new Set(['json', 'css']);
41
+ /**
42
+ * Whether a static import or re-export of the program has an import attribute a browser doesn't support: a key other
43
+ * than `type`, or a `type` that names no supported module type (`"javascript"`), which fails the module's graph.
44
+ */
45
+ function unsupportedAttributes(program) {
46
+ return program['body'].some((statement) => declarations.has(statement.type) &&
47
+ (statement['attributes'] ?? []).some((attribute) => {
48
+ const key = attribute['key'];
49
+ const name = key.type === 'Identifier' ? key['name'] : key['value'];
50
+ return name !== 'type' || !supportedTypes.has(attribute['value']['value']);
51
+ }));
52
+ }
53
+ function moduleRequests(program, dynamicImports, sourceType) {
54
+ const requests = [];
55
+ for (const statement of program['body']) {
56
+ if (!declarations.has(statement.type))
57
+ continue;
58
+ const source = statement['source'];
59
+ // A static import's attributes are always literals, and any key keeps it from naming a JavaScript module: a
60
+ // supported `type` names a module that is not JavaScript, and anything else fails the module (`unsupported`).
61
+ if (source === null || (statement['attributes'] ?? []).length > 0)
62
+ continue;
63
+ const { start, end } = source;
64
+ requests.push({ specifier: source['value'], start, end, dynamic: false, readableOptions: true, follow: true });
65
+ }
66
+ let declared;
67
+ for (const { start } of dynamicImports) {
68
+ const expression = nodeAt(program, start, 'ImportExpression');
69
+ if (expression === undefined)
70
+ continue;
71
+ // `undefined` is the native value unless a declaration in scope at the import binds the name.
72
+ const isUndefined = (node) => (node.type === 'UnaryExpression' && node['operator'] === 'void') ||
73
+ (node.type === 'Identifier' && node['name'] === 'undefined' && !(declared ??= scopeCheck(sourceType))(pathTo(program, expression), 'undefined'));
74
+ const attributes = importAttributes(expression['options'], isUndefined);
75
+ const follow = attributes.has('none');
76
+ const readableOptions = !attributes.has('unreadable');
77
+ if (!follow && readableOptions)
78
+ continue;
79
+ const source = expression['source'];
80
+ requests.push({ specifier: literal(source), start: source.start, end: source.end, dynamic: true, readableOptions, follow });
81
+ }
82
+ return requests.sort((a, b) => a.start - b.start);
83
+ }
84
+ /**
85
+ * The import attributes a dynamic import's options give (ECMAScript § EvaluateImportCall, HTML §
86
+ * HostLoadImportedModule), as the set of outcomes of its branches (`outcomes`): `none`, so it names a JavaScript module;
87
+ * `some`, so it names none, because a `type` names a module that is not JavaScript, a browser supports no other key,
88
+ * and options, or a `with`, that are not an object make the import throw; or `unreadable`, when a part of the options
89
+ * the analyzer can't read could decide which (§2 Import attributes). A conditional or logical expression gives the
90
+ * outcomes of each of its branches, except that the left operand of an `&&` that gives the options, or their `with`,
91
+ * counts only as its falsy value (`evaluateOptions`). The options' `with` is read as the import reads it,
92
+ * from their own properties, then from the object a non-computed `__proto__:` gives them, and its attributes are its
93
+ * own enumerable string-keyed properties.
94
+ */
95
+ function importAttributes(options, isUndefined) {
96
+ if (options === null || options === undefined)
97
+ return new Set(['none']);
98
+ return outcomes(evaluateOptions(options, isUndefined), (object) => outcomes(lookupWith(object), attributesOf));
99
+ }
100
+ /**
101
+ * The value of the options, as `evaluate` reads them (`options`), except that the left operand of an `&&` whose result
102
+ * is the options counts only as its falsy value (`branches`).
103
+ */
104
+ function evaluateOptions(node, isUndefined) {
105
+ return branches(node, isUndefined, (leaf) => evaluate(leaf, isUndefined, true));
106
+ }
107
+ /**
108
+ * The value of an expression that gives the options or their `with`, branch by branch: each branch of a `?:`, and both
109
+ * operands of a logical expression, are alternatives, each read by `leaf`, except that the left operand of an `&&`
110
+ * counts only as its falsy value (`falsy`): `c && X` yields `c` only when `c` is falsy (§2 Import attributes).
111
+ * The left operands of `||` and `??` stay what `leaf` reads, as does an `&&` anywhere else.
112
+ */
113
+ function branches(node, isUndefined, leaf) {
114
+ switch (node.type) {
115
+ case 'ParenthesizedExpression':
116
+ return branches(node['expression'], isUndefined, leaf);
117
+ case 'ConditionalExpression':
118
+ return { any: [branches(node['consequent'], isUndefined, leaf), branches(node['alternate'], isUndefined, leaf)] };
119
+ case 'LogicalExpression': {
120
+ const left = node['left'];
121
+ return { any: [node['operator'] === '&&' ? falsy(evaluate(left, isUndefined)) : leaf(left), branches(node['right'], isUndefined, leaf)] };
122
+ }
123
+ default:
124
+ return leaf(node);
125
+ }
126
+ }
127
+ /**
128
+ * The falsy values a value may be (ECMAScript § ToBoolean): `undefined` itself; another primitive, for one that may be
129
+ * `null`, `false`, `0`, `NaN` or `""`; both, for a value the analyzer can't read; and none for an object, or a string
130
+ * whose text isn't empty.
131
+ */
132
+ function falsy(value) {
133
+ if (value === 'undefined' || value === 'primitive')
134
+ return value;
135
+ if (value === 'unknown')
136
+ return { any: ['undefined', 'primitive'] };
137
+ if (isAlternatives(value))
138
+ return { any: value.any.map(falsy) };
139
+ if ('string' in value)
140
+ return value.string === null || value.string === '' ? 'primitive' : { any: [] };
141
+ return { any: [] };
142
+ }
143
+ /** The outcomes of the options value, or of a branch of it: an object's by `inObject`, an alternative's by each branch. */
144
+ function outcomes(value, inObject) {
145
+ if (isAlternatives(value))
146
+ return new Set(value.any.flatMap((branch) => [...outcomes(branch, inObject)]));
147
+ if (value === 'unknown')
148
+ return new Set(['unreadable']);
149
+ if (value === 'undefined')
150
+ return new Set(['none']);
151
+ if (isPrimitive(value))
152
+ return new Set(['some']);
153
+ return inObject(value);
154
+ }
155
+ /** The outcome of a `with` that is an object: its own enumerable string keys are the attributes. */
156
+ function attributesOf(attributes) {
157
+ const keys = hasKeys(attributes);
158
+ return new Set([keys === 'maybe' ? 'unreadable' : keys === 'yes' ? 'some' : 'none']);
159
+ }
160
+ /** Whether a value is a primitive other than `undefined`. */
161
+ function isPrimitive(value) {
162
+ return value === 'primitive' || (typeof value === 'object' && 'string' in value);
163
+ }
164
+ function isAlternatives(value) {
165
+ return typeof value === 'object' && 'any' in value;
166
+ }
167
+ /**
168
+ * `Array.prototype`, as import attributes read it: its `with` is a method, a function that has no enumerable own
169
+ * property (ECMAScript § Array.prototype.with).
170
+ */
171
+ const functionValue = () => ({ own: new Map(), wild: false, proto: null });
172
+ const arrayPrototype = { own: new Map([['with', functionValue()]]), wild: false, proto: null };
173
+ /**
174
+ * The value of an expression, as far as import attributes read it: a free `undefined` or a `void` is `undefined`; a
175
+ * literal, another unary operation or a template literal a primitive, a string kept apart (`StringValue`); a regular
176
+ * expression or a function an object with no enumerable own property and no `with`; an array or object literal its
177
+ * own keys, as `evaluateArray` and `evaluateObject` read them. Anything else is `unknown`. When the value may be the
178
+ * options (`options`), so may each of its branches, and an object literal's `with` is read as `evaluateObject` says.
179
+ */
180
+ function evaluate(node, isUndefined, options = false) {
181
+ switch (node.type) {
182
+ case 'ParenthesizedExpression':
183
+ return evaluate(node['expression'], isUndefined, options);
184
+ case 'Literal':
185
+ if (node['regex'] !== undefined && node['regex'] !== null)
186
+ return functionValue();
187
+ return typeof node['value'] === 'string' ? { string: node['value'] } : 'primitive';
188
+ case 'TemplateLiteral':
189
+ // A template literal with substitutions is a string whose text isn't read.
190
+ return { string: literal(node) };
191
+ case 'Identifier':
192
+ case 'UnaryExpression':
193
+ if (isUndefined(node))
194
+ return 'undefined';
195
+ if (node.type === 'Identifier')
196
+ return 'unknown';
197
+ // `typeof` gives a string; every other unary operator a number, a BigInt or a boolean.
198
+ return node['operator'] === 'typeof' ? { string: null } : 'primitive';
199
+ case 'ConditionalExpression':
200
+ return { any: [evaluate(node['consequent'], isUndefined, options), evaluate(node['alternate'], isUndefined, options)] };
201
+ case 'LogicalExpression':
202
+ return { any: [evaluate(node['left'], isUndefined, options), evaluate(node['right'], isUndefined, options)] };
203
+ case 'FunctionExpression':
204
+ case 'ArrowFunctionExpression':
205
+ return functionValue();
206
+ case 'ArrayExpression':
207
+ return evaluateArray(node['elements'], isUndefined);
208
+ case 'ObjectExpression':
209
+ return evaluateObject(node['properties'], isUndefined, options);
210
+ default:
211
+ return 'unknown';
212
+ }
213
+ }
214
+ /** An array literal's value: a key for each element, and those its spreads give (`spreadInto`), with `Array.prototype`. */
215
+ function evaluateArray(elements, isUndefined) {
216
+ const array = { own: new Map(), wild: false, proto: arrayPrototype, array: true };
217
+ for (const element of elements) {
218
+ if (element === null)
219
+ continue;
220
+ if (element.type === 'SpreadElement')
221
+ spreadInto(array, evaluate(element['argument'], isUndefined), true);
222
+ else
223
+ array.own.set(String(array.own.size), 'unknown');
224
+ }
225
+ return array;
226
+ }
227
+ /**
228
+ * An object literal's value, property by property in order: a key the analyzer reads sets that key; a computed key it
229
+ * can't read, or a spread of something it can't read, may set any key (`wild`), so every key set before it may have
230
+ * changed; a non-computed `__proto__:` sets the prototype (ECMAScript § PropertyDefinitionEvaluation). When the object may
231
+ * be the options (`options`), what its spreads and prototype give may be too, and the value of its `with` is read
232
+ * branch by branch (`branches`).
233
+ */
234
+ function evaluateObject(properties, isUndefined, options = false) {
235
+ const object = { own: new Map(), wild: false, proto: null };
236
+ for (const property of properties) {
237
+ if (property.type === 'SpreadElement') {
238
+ spreadInto(object, evaluate(property['argument'], isUndefined, options), false);
239
+ continue;
240
+ }
241
+ const name = propertyName(property);
242
+ if (name === undefined) {
243
+ makeWild(object);
244
+ continue;
245
+ }
246
+ const plain = property['kind'] === 'init' && property['method'] !== true && property['shorthand'] !== true;
247
+ const isProto = name === '__proto__' && property['computed'] !== true && plain;
248
+ const node = property['value'];
249
+ const value = property['kind'] !== 'init'
250
+ ? 'unknown'
251
+ : options && name === 'with'
252
+ ? branches(node, isUndefined, (leaf) => evaluate(leaf, isUndefined))
253
+ : evaluate(node, isUndefined, options && isProto);
254
+ if (isProto) {
255
+ // Only an object or null sets the prototype.
256
+ // A prototype that is one of several alternatives is not read.
257
+ if (value === 'unknown' || isAlternatives(value))
258
+ object.proto = 'unknown';
259
+ else if (typeof value === 'object' && !isPrimitive(value))
260
+ object.proto = value;
261
+ else if (value === 'primitive' && isNull(node))
262
+ object.proto = null;
263
+ }
264
+ else {
265
+ object.own.delete(name);
266
+ object.own.set(name, value);
267
+ }
268
+ }
269
+ return object;
270
+ }
271
+ /**
272
+ * Copies what spreading `value` gives into `target`: an object's own keys, a known string's indices, index keys it
273
+ * can't count for a string it can't read (`indices`), nothing for another primitive, and for a value the analyzer
274
+ * can't read, any key (`wild`). Into an array, each key is an element.
275
+ */
276
+ function spreadInto(target, value, array) {
277
+ if (value === 'undefined' || value === 'primitive')
278
+ return;
279
+ // A spread of one of several alternatives is not read.
280
+ if (value === 'unknown' || isAlternatives(value))
281
+ return makeWild(target);
282
+ if ('string' in value && value.string === null) {
283
+ target.indices = true;
284
+ return;
285
+ }
286
+ const keys = 'string' in value ? [...(value.string ?? '')].map((_, i) => [String(i), 'primitive']) : value.own;
287
+ if (!('string' in value) && value.indices === true)
288
+ target.indices = true;
289
+ for (const [key, entry] of keys) {
290
+ const name = array ? String(target.own.size) : key;
291
+ target.own.delete(name);
292
+ target.own.set(name, entry);
293
+ }
294
+ if (!('string' in value) && value.wild)
295
+ makeWild(target);
296
+ }
297
+ /** A part the analyzer can't read may set any key of `object`, and so change any it has. */
298
+ function makeWild(object) {
299
+ object.wild = true;
300
+ for (const key of object.own.keys())
301
+ object.own.set(key, 'unknown');
302
+ }
303
+ /** The value of an object's `with`, as `Get` reads it: its own, then its prototype's, `undefined` when none has one. */
304
+ function lookupWith(object) {
305
+ if (object.array !== true) {
306
+ const own = object.own.get('with');
307
+ if (own !== undefined)
308
+ return own;
309
+ if (object.wild)
310
+ return 'unknown';
311
+ }
312
+ if (object.proto === 'unknown')
313
+ return 'unknown';
314
+ return object.proto === null ? 'undefined' : lookupWith(object.proto);
315
+ }
316
+ /**
317
+ * Whether an object has an own enumerable string key: `maybe` when only a part the analyzer can't read may add one,
318
+ * such as a spread of a string whose text it can't read (a template literal with substitutions, or a `typeof`).
319
+ */
320
+ function hasKeys(object) {
321
+ return object.own.size > 0 ? 'yes' : object.wild || object.indices === true ? 'maybe' : 'no';
322
+ }
323
+ /**
324
+ * The key a property names with a literal: an identifier, or any literal, computed or not, as ECMAScript §
325
+ * ToPropertyKey names it. Only whether it is `with` or `__proto__` matters.
326
+ */
327
+ function propertyName(property) {
328
+ const key = property['key'];
329
+ if (key.type === 'Identifier' && property['computed'] !== true)
330
+ return key['name'];
331
+ if (key.type === 'Literal')
332
+ return literalText(key);
333
+ return key.type === 'TemplateLiteral' ? (literal(key) ?? undefined) : undefined;
334
+ }
335
+ /** The names code calls the global object by, which may lead the path to a worker function. */
336
+ const globalNames = new Set(['window', 'self', 'globalThis']);
337
+ /**
338
+ * The worker scripts a program loads with a literal URL, in source order:
339
+ * - `new Worker(url, options)` and `new SharedWorker(url, options)`, where the options may be a `SharedWorker`'s name
340
+ * - `navigator.serviceWorker.register(url, options)`
341
+ * - `importScripts(url, …)`, one request for each URL
342
+ *
343
+ * The function may be reached through a global name: `new self.Worker(…)`, `window.navigator.serviceWorker.register(…)`.
344
+ * A worker's `sourceType` comes from its options (`workerType`).
345
+ *
346
+ * This is syntax: a call whose first name (`Worker`, `self`, `navigator`, …) a declaration in scope at the call binds
347
+ * (`class Worker {}`, a `navigator` parameter, `var self = this` in its function) is not a native's, so it loads
348
+ * nothing (§2). A URL argument that is not a literal is a request with no URL, which the resolver folds (§2 Computed
349
+ * URLs).
350
+ */
351
+ function workerRequests(program, sourceType) {
352
+ const calls = [];
353
+ walk(program, (node) => {
354
+ if (node.type !== 'NewExpression' && node.type !== 'CallExpression')
355
+ return;
356
+ const path = calleePath(node['callee']);
357
+ if (path === undefined)
358
+ return;
359
+ const through = path.length > 1 && globalNames.has(path[0]) ? path[0] : undefined;
360
+ const kind = workerKind(node.type === 'NewExpression', through === undefined ? path : path.slice(1));
361
+ if (kind !== undefined)
362
+ calls.push({ call: node, kind, path, through });
363
+ });
364
+ const declared = scopeCheck(sourceType);
365
+ const requests = [];
366
+ for (const { call, kind, path, through } of calls) {
367
+ const ancestry = pathTo(program, call);
368
+ if (declared(ancestry, path[0]))
369
+ continue;
370
+ const args = call['arguments'];
371
+ const urls = kind === 'import-scripts' ? args : args.slice(0, 1);
372
+ // `undefined` is the native value unless a declaration in scope at the call binds the name.
373
+ const isUndefined = (node) => (node.type === 'UnaryExpression' && node['operator'] === 'void') ||
374
+ (node.type === 'Identifier' && node['name'] === 'undefined' && !declared(ancestry, 'undefined'));
375
+ const type = kind === 'import-scripts' ? 'script' : workerType(kind, args[1], isUndefined);
376
+ if (type === undefined)
377
+ continue;
378
+ for (const argument of urls) {
379
+ if (argument.type === 'SpreadElement')
380
+ continue;
381
+ const url = literal(argument);
382
+ const { start, end } = argument;
383
+ requests.push({ kind, url, sourceType: type, ...(through === undefined ? {} : { through }), start, end });
384
+ }
385
+ }
386
+ return requests.sort((a, b) => a.start - b.start);
387
+ }
388
+ /**
389
+ * The nodes from `root` down to `target`, each the parent of the next, found by descending through the children that
390
+ * contain it, without walking the rest of the tree.
391
+ */
392
+ function pathTo(root, target) {
393
+ const path = [root];
394
+ for (let node = root; node !== target;) {
395
+ const child = childContaining(node, target.start, target.end);
396
+ if (child === undefined)
397
+ throw new Error(`no path to the ${target.type} at ${target.start}`);
398
+ path.push((node = child));
399
+ }
400
+ return path;
401
+ }
402
+ /** The first child of `node` whose range holds the UTF-16 range from `start` to `end`, if any. */
403
+ function childContaining(node, start, end) {
404
+ for (const key of visitorKeys[node.type] ?? []) {
405
+ const value = node[key];
406
+ for (const child of Array.isArray(value) ? value : [value]) {
407
+ if (isNode(child) && child.start <= start && end <= child.end)
408
+ return child;
409
+ }
410
+ }
411
+ return undefined;
412
+ }
413
+ /** Visits every node under `root`, in tree order. The walk keeps its own stack, so no depth of nesting overflows it. */
414
+ function walk(root, visit) {
415
+ const stack = [root];
416
+ for (let node = stack.pop(); node !== undefined; node = stack.pop()) {
417
+ visit(node);
418
+ const keys = visitorKeys[node.type] ?? [];
419
+ for (let k = keys.length - 1; k >= 0; k--) {
420
+ const value = node[keys[k]];
421
+ if (Array.isArray(value)) {
422
+ for (let i = value.length - 1; i >= 0; i--)
423
+ if (isNode(value[i]))
424
+ stack.push(value[i]);
425
+ }
426
+ else if (isNode(value))
427
+ stack.push(value);
428
+ }
429
+ }
430
+ }
431
+ /** The kind of worker function a callee path names, as `new` or a call reaches it, without its global name. */
432
+ function workerKind(constructs, path) {
433
+ const spelt = path.join('.');
434
+ if (constructs)
435
+ return spelt === 'Worker' ? 'worker' : spelt === 'SharedWorker' ? 'shared-worker' : undefined;
436
+ if (spelt === 'navigator.serviceWorker.register')
437
+ return 'service-worker';
438
+ return spelt === 'importScripts' ? 'import-scripts' : undefined;
439
+ }
440
+ /**
441
+ * The source type of a worker from its options (Web IDL `WorkerOptions`): the `type` of an object literal, `"classic"`
442
+ * or `"module"`. A classic script, the default, when there are none, when they are `undefined` (`isUndefined`) or
443
+ * `null`, a regular-expression literal, or a `SharedWorker`'s name (any other primitive literal), or when the object
444
+ * literal sets no `type` and nothing in it could, or sets it to `undefined`. Undefined when the call throws or rejects
445
+ * before any worker starts, so it loads nothing (Principle 1): `Worker` or `register` given a primitive literal other
446
+ * than `null`, which isn't a dictionary, or a `type` literal whose string isn't `"classic"` or `"module"` (Web IDL
447
+ * `WorkerType`; `null`, `5` and `true` give `"null"`, `"5"` and `"true"`). Null when the options can't be read
448
+ * statically: they are another expression, or the object literal's `type` is, or could come from a spread, a computed
449
+ * key, an accessor or a `__proto__` (§2: the script's syntax then decides).
450
+ */
451
+ function workerType(kind, options, isUndefined) {
452
+ if (options === undefined || isUndefined(options) || isNull(options))
453
+ return 'script';
454
+ if (isPrimitiveLiteral(options))
455
+ return kind === 'shared-worker' ? 'script' : undefined;
456
+ if (options.type === 'Literal')
457
+ return 'script';
458
+ if (options.type !== 'ObjectExpression')
459
+ return null;
460
+ const type = typeOption(options['properties'], isUndefined);
461
+ if (type === null)
462
+ return null;
463
+ return type === undefined || type === 'classic' ? 'script' : type === 'module' ? 'module' : undefined;
464
+ }
465
+ /** Whether a node is a literal whose value is a primitive other than `null`: not a regular expression. */
466
+ function isPrimitiveLiteral(node) {
467
+ if (node.type === 'TemplateLiteral')
468
+ return true;
469
+ return node.type === 'Literal' && node['regex'] === undefined && !isNull(node);
470
+ }
471
+ /**
472
+ * The `type` an object literal's properties give it, as a dictionary member reads it: the string the last property
473
+ * that can set it gives (another literal's as ToString spells it), undefined when none can or it is `undefined`, or
474
+ * null when it can't be read statically.
475
+ */
476
+ function typeOption(properties, isUndefined) {
477
+ let type;
478
+ // A prototype the literal sets supplies the `type` it doesn't set itself. An own `type`, even `undefined`, hides it.
479
+ let prototype = false;
480
+ let own = false;
481
+ for (const property of properties) {
482
+ // A spread may copy a `type` or not.
483
+ if (property.type !== 'Property') {
484
+ type = null;
485
+ continue;
486
+ }
487
+ const keyNode = property['key'];
488
+ const name = property['computed'] === true ? computedKey(keyNode) : key(keyNode);
489
+ if (name === null)
490
+ type = null;
491
+ else if (setsPrototype(property, name))
492
+ prototype = !isNull(property['value']);
493
+ else if (name === 'type') {
494
+ type = typeValue(property, isUndefined);
495
+ own = true;
496
+ }
497
+ }
498
+ return type === undefined && prototype && !own ? null : type;
499
+ }
500
+ /**
501
+ * The `type` a property of that name gives: the string of a string literal, or of another literal as ToString spells
502
+ * it (`null`, `5`, `true`, `/x/`), none of which is a valid type; undefined for `undefined`; or null when its value
503
+ * can't be read statically, as an accessor's or a method's can't.
504
+ */
505
+ function typeValue(property, isUndefined) {
506
+ if (property['kind'] !== 'init' || property['method'] === true)
507
+ return null;
508
+ const value = property['value'];
509
+ const text = literal(value);
510
+ if (text !== null)
511
+ return text;
512
+ if (isUndefined(value))
513
+ return undefined;
514
+ return value.type === 'Literal' ? literalText(value) : null;
515
+ }
516
+ /** A literal's value as ECMAScript ToString spells it: `null`, `5`, `true`, `1` for `1n`, `/x/g`. */
517
+ function literalText(node) {
518
+ if (typeof node['bigint'] === 'string')
519
+ return node['bigint'];
520
+ const regex = node['regex'];
521
+ return regex === null || regex === undefined ? String(node['value']) : `/${regex.pattern}/${regex.flags}`;
522
+ }
523
+ /** Whether an object literal's property sets its prototype: `__proto__: value`, not computed, shorthand or a method. */
524
+ function setsPrototype(property, name) {
525
+ const plain = property['kind'] === 'init' && property['method'] !== true && property['shorthand'] !== true;
526
+ return name === '__proto__' && property['computed'] !== true && plain;
527
+ }
528
+ /** The name a computed key spells when it is a literal, or null when it can't be read statically. */
529
+ function computedKey(node) {
530
+ if (node.type === 'Literal')
531
+ return String(node['value']);
532
+ return literal(node);
533
+ }
534
+ /** Whether a node is the literal `null`. */
535
+ function isNull(node) {
536
+ return node.type === 'Literal' && node['value'] === null && node['raw'] === 'null';
537
+ }
538
+ /**
539
+ * The names a callee spells, as a path of identifiers and literal keys: `navigator.serviceWorker.register`. A chain
540
+ * (`a?.b`) and a comma sequence (`(0, self.importScripts)`) call their last expression. Anything else spells nothing.
541
+ */
542
+ function calleePath(node) {
543
+ switch (node.type) {
544
+ case 'Identifier':
545
+ return [node['name']];
546
+ case 'ChainExpression':
547
+ return calleePath(node['expression']);
548
+ case 'SequenceExpression':
549
+ return calleePath(node['expressions'].at(-1));
550
+ case 'MemberExpression': {
551
+ const property = node['property'];
552
+ const name = node['computed'] === true
553
+ ? literal(property)
554
+ : property.type === 'Identifier'
555
+ ? property['name']
556
+ : null;
557
+ const object = calleePath(node['object']);
558
+ return name === null || object === undefined ? undefined : [...object, name];
559
+ }
560
+ default:
561
+ return undefined;
562
+ }
563
+ }
564
+ /** The name an identifier or string literal key spells. */
565
+ function key(node) {
566
+ if (node.type === 'Identifier')
567
+ return node['name'];
568
+ if (node.type === 'Literal' && typeof node['value'] === 'string')
569
+ return node['value'];
570
+ return undefined;
571
+ }
572
+ /** The string a literal names: a string literal, or a template literal with no substitutions. */
573
+ function literal(node) {
574
+ if (node.type === 'Literal' && typeof node['value'] === 'string')
575
+ return node['value'];
576
+ if (node.type === 'TemplateLiteral' && node['expressions'].length === 0) {
577
+ return node['quasis'][0]['value'].cooked;
578
+ }
579
+ return null;
580
+ }
581
+ /**
582
+ * The node of `type` that starts at `start`, found by descending from `root` through the children that contain it,
583
+ * without walking the rest of the tree.
584
+ */
585
+ function nodeAt(root, start, type) {
586
+ let node = root;
587
+ while (node !== undefined && (node.type !== type || node.start !== start)) {
588
+ node = childContaining(node, start, start + 1);
589
+ }
590
+ return node;
591
+ }
@@ -0,0 +1,6 @@
1
+ import type { Node } from './syntax.ts';
2
+ /**
3
+ * A check of whether a declaration of a name is in scope at a node of one program, given `path`, the chain of nodes
4
+ * from the program down to it, each the parent of the next. The names each scope declares are gathered once.
5
+ */
6
+ export declare function scopeCheck(sourceType: 'script' | 'module'): (path: readonly Node[], name: string) => boolean;