Skip to content

Commit 1796661

Browse files
committed
✨ Add detection support for more core-js modules
Signed-off-by: Ferdinand Thiessen <[email protected]>
1 parent 1e75748 commit 1796661

File tree

2 files changed

+106
-160
lines changed

2 files changed

+106
-160
lines changed

src/analyze.ts

Lines changed: 90 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@
1515

1616
import { traverse, Visitor, Visitors } from 'estree-toolkit'
1717

18+
enum PolyfillType {
19+
CallWithArguments,
20+
StaticMember,
21+
GenericMethod,
22+
GenericProperty,
23+
Global
24+
}
25+
26+
const PT = PolyfillType
27+
type ModuleName = string
28+
type Arguments = any[]
29+
type ModuleEntry = [ModuleName, PolyfillType, string, ...Arguments]
30+
1831
/**
1932
* Core-Js 3.x contains some alias which will be removed with version 4.
2033
* This maps those aliases to the correct modules
@@ -23,6 +36,9 @@ const aliasModules = {
2336
'es.aggregate-error': [
2437
'es.aggregate-error.constructor'
2538
],
39+
'es.data-view': [
40+
'es.data-view.constructor'
41+
],
2642
'es.map': [
2743
'es.map.constructor'
2844
],
@@ -51,19 +67,8 @@ const aliasModules = {
5167
'es.weak-map.constructor'
5268
]
5369
}
54-
enum PolyfillType {
55-
StaticMember,
56-
GenericMethod,
57-
GenericProperty,
58-
Global
59-
}
60-
61-
const PT = PolyfillType
62-
type ModuleName = string
63-
type Arguments = string[]
64-
type ModuleEntry = [ModuleName, PolyfillType, ...Arguments]
6570

66-
const detectableModules: ModuleEntry[] = [
71+
export const detectableModules: ModuleEntry[] = [
6772
['es.aggregate-error.cause', PT.Global, 'AggregateError'],
6873
['es.aggregate-error.constructor', PT.Global, 'AggregateError'],
6974
// array buffer
@@ -88,6 +93,9 @@ const detectableModules: ModuleEntry[] = [
8893
['es.array.includes', PT.GenericMethod, 'includes'],
8994
['es.array.index-of', PT.GenericMethod, 'indexOf'],
9095
['es.array.is-array', PT.StaticMember, 'Array', 'isArray'],
96+
['es.array.iterator', PT.GenericMethod, 'entries'],
97+
['es.array.iterator', PT.GenericMethod, 'keys'],
98+
['es.array.iterator', PT.GenericMethod, 'values'],
9199
['es.array.join', PT.GenericMethod, 'join'],
92100
['es.array.last-index-of', PT.GenericMethod, 'lastIndexOf'],
93101
['es.array.map', PT.GenericMethod, 'map'],
@@ -99,6 +107,7 @@ const detectableModules: ModuleEntry[] = [
99107
['es.array.slice', PT.GenericMethod, 'slice'],
100108
['es.array.some', PT.GenericMethod, 'some'],
101109
['es.array.sort', PT.GenericMethod, 'sort'],
110+
['es.array.splice', PT.GenericMethod, 'splice'],
102111
['es.array.to-reversed', PT.GenericMethod, 'toReversed'],
103112
['es.array.to-sorted', PT.GenericMethod, 'toSorted'],
104113
['es.array.to-spliced', PT.GenericMethod, 'toSpliced'],
@@ -107,6 +116,17 @@ const detectableModules: ModuleEntry[] = [
107116
['es.array.species', PT.StaticMember, 'Symbol', 'species'],
108117
// TODO: es.array.unscopables.flat-map
109118
// TODO: es.array.unscopables.flat
119+
// data-view
120+
['es.data-view.constructor', PT.Global, 'DataView'],
121+
// error
122+
// Not possible because toString might be called implicitly (template string):
123+
// es.error.to-string
124+
// function
125+
['es.function.bind', PT.GenericMethod, 'bind'],
126+
['es.function.has-instance', PT.StaticMember, 'Symbol', 'hasInstance'],
127+
['es.function.name', PT.GenericProperty, 'name'],
128+
// globalThis
129+
['es.global-this', PT.Global, 'globalThis'],
110130
// json
111131
['es.json.stringify', PT.StaticMember, 'JSON', 'stringify'],
112132
// map
@@ -126,12 +146,12 @@ const detectableModules: ModuleEntry[] = [
126146
['es.promise.all', PT.StaticMember, 'Promise', 'all'],
127147
['es.promise.all-settled', PT.StaticMember, 'Promise', 'allSettled'],
128148
['es.promise.any', PT.StaticMember, 'Promise', 'any', 'es.promise.any'],
149+
['es.promise.catch', PT.GenericMethod, 'catch'],
129150
['es.promise.constructor', PT.Global, 'Promise'],
130151
['es.promise.race', PT.StaticMember, 'Promise', 'race'],
131152
['es.promise.reject', PT.StaticMember, 'Promise', 'reject'],
132153
['es.promise.resolve', PT.StaticMember, 'Promise', 'resolve'],
133154
['es.promise.finally', PT.GenericMethod, 'finally'],
134-
// TODO: 'es.promise.catch',
135155
// RegExp
136156
['es.regexp.constructor', PT.Global, 'RegExp'],
137157
// TODO "es.regexp.dot-all"
@@ -184,6 +204,9 @@ const detectableModules: ModuleEntry[] = [
184204
['es.typed-array.to-sorted', PT.GenericMethod, 'toSorted'],
185205
['es.typed-array.to-string', PT.GenericMethod, 'toString'],
186206
['es.typed-array.with', PT.GenericMethod, 'with'],
207+
// un-/escape
208+
['es.escape', PT.Global, 'escape'],
209+
['es.unescape', PT.Global, 'unescape'],
187210
// Weak
188211
['es.weak-map.constructor', PT.Global, 'WeakMap'],
189212
['es.weak-set.constructor', PT.Global, 'WeakSet'],
@@ -222,6 +245,7 @@ const detectableModules: ModuleEntry[] = [
222245
'es.math.expm1',
223246
'es.math.fround',
224247
'es.math.hypot',
248+
'es.math.imul',
225249
'es.math.log10',
226250
'es.math.log1p',
227251
'es.math.log2',
@@ -233,11 +257,29 @@ const detectableModules: ModuleEntry[] = [
233257
detectableModules.push([m, PT.StaticMember, 'Math', m.split('.').at(-1)])
234258
}))();
235259

260+
/**
261+
* Init detection of error
262+
*/
263+
(() => [
264+
'Error',
265+
'EvalError',
266+
'RangeError',
267+
'ReferenceError',
268+
'SyntaxError',
269+
'TypeError',
270+
'URIError',
271+
'CompileError',
272+
'LinkError',
273+
'RuntimeError'
274+
].forEach(error => {
275+
detectableModules.push(['es.error.cause', PT.CallWithArguments, error, (args: Array<unknown>) => args.length === 2])
276+
}))();
277+
236278
/**
237279
* Init detection of static Number functions
238280
*/
239281
(() => [
240-
['es.number.is-epsilon', 'EPSILON'],
282+
['es.number.epsilon', 'EPSILON'],
241283
['es.number.is-finite', 'isFinite'],
242284
['es.number.is-integer', 'isInteger'],
243285
['es.number.is-nan', 'isNaN'],
@@ -252,15 +294,16 @@ const detectableModules: ModuleEntry[] = [
252294
// TODO: es.number.to-precision
253295

254296
].forEach(([mod, fn]) => {
255-
detectableModules.push([mod[0], PT.StaticMember, 'Number', mod[1]])
256-
}))();
297+
detectableModules.push([mod, PT.StaticMember, 'Number', fn])
298+
}))();
257299

258300
class Walker {
259301
_modules: string[] = []
260302
_globals = new Map<string, string>()
261303
_methods = new Map<string, Array<string>>
262304
_members = new Map<string, readonly string[]>
263305
_properties = new Map<string, Array<string>>
306+
_calls = new Map<string, Array<any>>
264307

265308
registerModules(modules: ModuleEntry[], filterModules: string[]) {
266309
const usedModules: string[] = []
@@ -279,6 +322,9 @@ class Walker {
279322
case PolyfillType.StaticMember:
280323
this.registerMember(name, rest[0], rest[1])
281324
break
325+
case PolyfillType.CallWithArguments:
326+
this.registerCall(name, rest[0], rest[1])
327+
break
282328
}
283329
usedModules.push(name)
284330
}
@@ -308,6 +354,13 @@ class Walker {
308354
this._methods.get(method).push(module)
309355
}
310356

357+
registerCall(module: string, object: string, validator: (args: Array<unknown>) => boolean) {
358+
if (!this._calls.has(object)) {
359+
this._calls.set(object, [])
360+
}
361+
this._calls.get(object).push({ module, validator })
362+
}
363+
311364
getVisitors(): Visitors<any> {
312365
return {
313366
Program(path, state: Walker) {
@@ -318,6 +371,26 @@ class Walker {
318371
})
319372
},
320373

374+
CallExpression(path, state: Walker) {
375+
if (path.node.callee.type === 'Identifier') {
376+
if (state._calls.has(path.node.callee.name)) {
377+
state._calls.get(path.node.callee.name).forEach(v => {
378+
if (v.validator(path.node.arguments)) state._modules.push(v.module)
379+
})
380+
}
381+
}
382+
},
383+
384+
NewExpression(path, state: Walker) {
385+
if (path.node.callee.type === 'Identifier') {
386+
if (state._calls.has(path.node.callee.name)) {
387+
state._calls.get(path.node.callee.name).forEach(v => {
388+
if (v.validator(path.node.arguments)) state._modules.push(v.module)
389+
})
390+
}
391+
}
392+
},
393+
321394
MemberExpression(path, state: Walker) {
322395
if (path.node.object.type === 'Identifier') {
323396
const property = path.node.property.type === 'Identifier' ? path.node.property.name : (path.node.property.type === 'Literal' && typeof path.node.property.value === 'string' ? path.node.property.value : undefined)
@@ -346,7 +419,7 @@ class Walker {
346419
}
347420
}
348421

349-
export function filterModules(modules: string[], ast: any) {
422+
export function filterModules(modules: readonly string[], ast: any) {
350423
const realModules: string[] = []
351424
modules.forEach(m => {
352425
if (m in aliasModules) realModules.push(...aliasModules[m])
@@ -361,144 +434,3 @@ export function filterModules(modules: string[], ast: any) {
361434
}}), walker)
362435
return [...(new Set(walker._modules))]
363436
}
364-
365-
/*
366-
"es.error.cause";
367-
"es.error.to-string";
368-
"es.aggregate-error";
369-
"es.aggregate-error.cause";
370-
"es.array.at";
371-
"es.array.concat";
372-
"es.array.copy-within";
373-
"es.array.fill";
374-
"es.array.filter";
375-
"es.array.find";
376-
"es.array.find-index";
377-
"es.array.find-last";
378-
"es.array.find-last-index";
379-
"es.array.flat";
380-
"es.array.flat-map";
381-
"es.array.from";
382-
"es.array.includes";
383-
"es.array.index-of";
384-
"es.array.iterator";
385-
"es.array.last-index-of";
386-
"es.array.map";
387-
"es.array.of";
388-
"es.array.push";
389-
"es.array.reduce";
390-
"es.array.reduce-right";
391-
"es.array.slice";
392-
"es.array.sort";
393-
"es.array.species";
394-
"es.array.splice";
395-
"es.array.to-reversed";
396-
"es.array.to-sorted";
397-
"es.array.to-spliced";
398-
"es.array.unscopables.flat";
399-
"es.array.unscopables.flat-map";
400-
"es.array.unshift";
401-
"es.array.with";
402-
"es.array-buffer.is-view";
403-
"es.array-buffer.slice";
404-
"es.date.to-primitive";
405-
"es.function.has-instance";
406-
"es.global-this";
407-
"eson.stringify";
408-
"eson.to-string-tag";
409-
"es.math.to-string-tag";
410-
"es.object.define-getter";
411-
"es.object.define-properties";
412-
"es.object.define-property";
413-
"es.object.define-setter";
414-
"es.object.entries";
415-
"es.object.freeze";
416-
"es.object.from-entries";
417-
"es.object.get-own-property-descriptor";
418-
"es.object.get-own-property-descriptors";
419-
"es.object.get-own-property-names";
420-
"es.object.get-prototype-of";
421-
"es.object.has-own";
422-
"es.object.is-extensible";
423-
"es.object.is-frozen";
424-
"es.object.is-sealed";
425-
"es.object.lookup-getter";
426-
"es.object.lookup-setter";
427-
"es.object.prevent-extensions";
428-
"es.object.seal";
429-
"es.object.set-prototype-of";
430-
"es.object.to-string";
431-
"es.reflect.apply";
432-
"es.reflect.construct";
433-
"es.reflect.define-property";
434-
"es.reflect.delete-property";
435-
"es.reflect.get";
436-
"es.reflect.get-own-property-descriptor";
437-
"es.reflect.get-prototype-of";
438-
"es.reflect.has";
439-
"es.reflect.is-extensible";
440-
"es.reflect.own-keys";
441-
"es.reflect.prevent-extensions";
442-
"es.reflect.set";
443-
"es.reflect.set-prototype-of";
444-
"es.reflect.to-string-tag";
445-
"es.string.at-alternative";
446-
"es.string.code-point-at";
447-
"es.string.ends-with";
448-
"es.string.from-code-point";
449-
"es.string.includes";
450-
"es.string.iterator";
451-
"es.string.match";
452-
"es.string.match-all";
453-
"es.string.pad-end";
454-
"es.string.pad-start";
455-
"es.string.raw";
456-
"es.string.repeat";
457-
"es.string.replace";
458-
"es.string.replace-all";
459-
"es.string.search";
460-
"es.string.split";
461-
"es.string.starts-with";
462-
"es.string.trim";
463-
"es.string.trim-end";
464-
"es.string.trim-start";
465-
"es.typed-array.float32-array";
466-
"es.typed-array.float64-array";
467-
"es.typed-array.int8-array";
468-
"es.typed-array.int16-array";
469-
"es.typed-array.int32-array";
470-
"es.typed-array.uint8-array";
471-
"es.typed-array.uint8-clamped-array";
472-
"es.typed-array.uint16-array";
473-
"es.typed-array.uint32-array";
474-
"es.typed-array.at";
475-
"es.typed-array.copy-within";
476-
"es.typed-array.every";
477-
"es.typed-array.fill";
478-
"es.typed-array.filter";
479-
"es.typed-array.find";
480-
"es.typed-array.find-index";
481-
"es.typed-array.find-last";
482-
"es.typed-array.find-last-index";
483-
"es.typed-array.for-each";
484-
"es.typed-array.from";
485-
"es.typed-array.includes";
486-
"es.typed-array.index-of";
487-
"es.typed-array.iterator";
488-
"es.typed-array.join";
489-
"es.typed-array.last-index-of";
490-
"es.typed-array.map";
491-
"es.typed-array.of";
492-
"es.typed-array.reduce";
493-
"es.typed-array.reduce-right";
494-
"es.typed-array.reverse";
495-
"es.typed-array.set";
496-
"es.typed-array.slice";
497-
"es.typed-array.some";
498-
"es.typed-array.sort";
499-
"es.typed-array.to-locale-string";
500-
"es.typed-array.to-reversed";
501-
"es.typed-array.to-sorted";
502-
"es.typed-array.to-string";
503-
"es.typed-array.with.js";
504-
*/

tests/analyze.test.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,21 @@ test('filter one (un)used', () => {
3232
Math.sinh(0.1)
3333
`);
3434

35-
3635
expect(filterModules(['es.math.cosh'], ast).sort()).toEqual(['es.math.cosh'].sort())
37-
expect(filterModules(['es.math.cosh'], ast0).sort()).toEqual([].sort())
36+
expect(filterModules(['es.math.cosh'], ast0)).toEqual([])
37+
})
38+
39+
test('Can detect errors with cause', () => {
40+
const ast = parseModule(`
41+
new RangeError('foo', { cause: e});
42+
`)
43+
const ast0 = parseModule('')
44+
45+
expect(filterModules(['es.error.cause'], ast0)).toEqual([])
46+
expect(filterModules(['es.error.cause'], ast)).toEqual(['es.error.cause'])
47+
})
48+
// X-Fail as not implemented
49+
test.failing('Can detect all esnext modules', () => {
50+
const ast = parseModule('')
51+
expect(filterModules(compat.modules.filter(v => v.startsWith('esnext.')), ast)).toBe([])
3852
})

0 commit comments

Comments
 (0)