es-check 9.1.3 → 9.1.5-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.
- package/README.md +29 -3
- package/constants.js +208 -53
- package/detectFeatures.js +11 -39
- package/index.js +19 -3
- package/package.json +5 -5
- package/polyfillDetector.js +27 -57
- package/utils.js +12 -2
package/README.md
CHANGED
|
@@ -24,10 +24,19 @@ Ensuring that JavaScript files can pass ES Check is important in a [modular and
|
|
|
24
24
|
|
|
25
25
|
**ES Check** version 9 is a major release update that can enforce more ES version specific features checks, implements initial browserslist integration, basic (naive) polyfill detection, and supports an allowlist. To enable ecmaVersion specific checks, pass the `--checkFeatures` flag. To enable browserslist integration, pass the `--checkBrowser` flag. To enable polyfill detection, pass the `--checkForPolyfills` flag. There is also more config file support. Besides this, there are other feature updates based on user feedback. This version should not break any existing scripts but, as significant changes/features have been added and it's know that es-check supports protecting against breaking errors going to production, a major version bump feels appropriate. Please report any issues!
|
|
26
26
|
|
|
27
|
+
|
|
28
|
+
### `--checkFeatures`
|
|
29
|
+
|
|
27
30
|
```sh
|
|
28
31
|
es-check es6 './dist/**/*.js' --checkFeatures
|
|
29
32
|
```
|
|
30
33
|
|
|
34
|
+
### `checkBrowser --browserslistQuery='<broswerslist query>'`
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
es-check checkBrowser ./dist/**/*.js --browserslistQuery="last 2 versions"
|
|
38
|
+
```
|
|
39
|
+
|
|
31
40
|
---
|
|
32
41
|
|
|
33
42
|
<p align="center">
|
|
@@ -82,10 +91,12 @@ ES Check checks syntax out of the box—to protect against breaking errors going
|
|
|
82
91
|
|
|
83
92
|
The images below demonstrate command line scripts and their corresponding logged results.
|
|
84
93
|
|
|
85
|
-
Pass
|
|
94
|
+
### Pass
|
|
95
|
+
|
|
86
96
|

|
|
87
97
|
|
|
88
|
-
Fail
|
|
98
|
+
### Fail
|
|
99
|
+
|
|
89
100
|

|
|
90
101
|
|
|
91
102
|
**ES Check** is run above with node commands. It can also be run within npm scripts, ci tools, or testing suites.
|
|
@@ -248,6 +259,18 @@ es-check --checkBrowser --browserslistQuery="last 2 versions" ./dist/**/*.js
|
|
|
248
259
|
es-check --checkBrowser --browserslistQuery=">0.5%, not dead" --checkFeatures ./dist/**/*.js
|
|
249
260
|
```
|
|
250
261
|
|
|
262
|
+
**Using browserlist just like an es version**
|
|
263
|
+
|
|
264
|
+
```sh
|
|
265
|
+
es-check checkBrowser ./dist/**/*.js --browserslistQuery=">0.5%, not dead"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Using browserlist with a pre-defined browserlist**
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
es-check checkBrowser ./dist/**/*.js
|
|
272
|
+
```
|
|
273
|
+
|
|
251
274
|
---
|
|
252
275
|
|
|
253
276
|
## Usage
|
|
@@ -442,6 +465,9 @@ es-check --checkBrowser --browserslistQuery="last 2 versions" ./dist/**/*.js
|
|
|
442
465
|
|
|
443
466
|
# Using 'checkBrowser' as the ES version argument
|
|
444
467
|
es-check checkBrowser --browserslistQuery="last 2 versions" ./dist/**/*.js
|
|
468
|
+
|
|
469
|
+
# Using a pre-defined browserslist configuration
|
|
470
|
+
es-check checkBrowser ./dist/**/*.js
|
|
445
471
|
```
|
|
446
472
|
|
|
447
473
|
This will read your browserslist configuration (from `.browserslistrc`, `package.json`, etc.) and determine the appropriate ES version based on your targeted browsers.
|
|
@@ -466,7 +492,7 @@ es-check --checkBrowser --browserslistEnv="production" ./dist/**/*.js
|
|
|
466
492
|
es-check --checkBrowser --checkFeatures ./dist/**/*.js
|
|
467
493
|
```
|
|
468
494
|
|
|
469
|
-
⚠️ **NOTE:** When using `--checkBrowser`, you must also provide a `--browserslistQuery` or have a valid browserslist configuration in your project.
|
|
495
|
+
⚠️ **NOTE:** When using `--checkBrowser`, you must also provide a `--browserslistQuery` or have a valid browserslist configuration in your project. You cannot have a files directly after your `--checkBrowser` option; it will read as
|
|
470
496
|
|
|
471
497
|
---
|
|
472
498
|
|
package/constants.js
CHANGED
|
@@ -502,6 +502,100 @@ const ES_FEATURES = {
|
|
|
502
502
|
property: 'with',
|
|
503
503
|
},
|
|
504
504
|
},
|
|
505
|
+
|
|
506
|
+
// ----------------------------------------------------------
|
|
507
|
+
// ES14 / ES2023 (continued)
|
|
508
|
+
// ----------------------------------------------------------
|
|
509
|
+
ArrayFindLast: {
|
|
510
|
+
minVersion: 14,
|
|
511
|
+
example: 'arr.findLast(x => x > 5)',
|
|
512
|
+
astInfo: {
|
|
513
|
+
nodeType: 'CallExpression',
|
|
514
|
+
property: 'findLast',
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
ArrayFindLastIndex: {
|
|
518
|
+
minVersion: 14,
|
|
519
|
+
example: 'arr.findLastIndex(x => x > 5)',
|
|
520
|
+
astInfo: {
|
|
521
|
+
nodeType: 'CallExpression',
|
|
522
|
+
property: 'findLastIndex',
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
|
|
526
|
+
// ----------------------------------------------------------
|
|
527
|
+
// ES15 / ES2024
|
|
528
|
+
// ----------------------------------------------------------
|
|
529
|
+
ObjectHasOwn: {
|
|
530
|
+
minVersion: 13,
|
|
531
|
+
example: 'Object.hasOwn(obj, "prop")',
|
|
532
|
+
astInfo: {
|
|
533
|
+
nodeType: 'CallExpression',
|
|
534
|
+
object: 'Object',
|
|
535
|
+
property: 'hasOwn',
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
StringIsWellFormed: {
|
|
539
|
+
minVersion: 15,
|
|
540
|
+
example: 'str.isWellFormed()',
|
|
541
|
+
astInfo: {
|
|
542
|
+
nodeType: 'CallExpression',
|
|
543
|
+
property: 'isWellFormed',
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
StringToWellFormed: {
|
|
547
|
+
minVersion: 15,
|
|
548
|
+
example: 'str.toWellFormed()',
|
|
549
|
+
astInfo: {
|
|
550
|
+
nodeType: 'CallExpression',
|
|
551
|
+
property: 'toWellFormed',
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
RegExpUnicodeSetFlag: {
|
|
555
|
+
minVersion: 15,
|
|
556
|
+
example: '/pattern/v',
|
|
557
|
+
astInfo: {
|
|
558
|
+
nodeType: 'RegExpLiteral',
|
|
559
|
+
flags: 'v',
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
// ----------------------------------------------------------
|
|
564
|
+
// ES16 / ES2025
|
|
565
|
+
// ----------------------------------------------------------
|
|
566
|
+
ArrayGroup: {
|
|
567
|
+
minVersion: 16,
|
|
568
|
+
example: 'arr.group(x => x.category)',
|
|
569
|
+
astInfo: {
|
|
570
|
+
nodeType: 'CallExpression',
|
|
571
|
+
property: 'group',
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
ArrayGroupToMap: {
|
|
575
|
+
minVersion: 16,
|
|
576
|
+
example: 'arr.groupToMap(x => x.category)',
|
|
577
|
+
astInfo: {
|
|
578
|
+
nodeType: 'CallExpression',
|
|
579
|
+
property: 'groupToMap',
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
PromiseTry: {
|
|
583
|
+
minVersion: 16,
|
|
584
|
+
example: 'Promise.try(() => syncOrAsyncFunction())',
|
|
585
|
+
astInfo: {
|
|
586
|
+
nodeType: 'CallExpression',
|
|
587
|
+
object: 'Promise',
|
|
588
|
+
property: 'try',
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
DuplicateNamedCaptureGroups: {
|
|
592
|
+
minVersion: 16,
|
|
593
|
+
example: '/(?<name>a)|(?<name>b)/',
|
|
594
|
+
astInfo: {
|
|
595
|
+
nodeType: 'RegExpLiteral',
|
|
596
|
+
duplicateNamedGroups: true,
|
|
597
|
+
},
|
|
598
|
+
},
|
|
505
599
|
};
|
|
506
600
|
|
|
507
601
|
const NODE_TYPES = {
|
|
@@ -512,6 +606,45 @@ const NODE_TYPES = {
|
|
|
512
606
|
NEW_EXPRESSION: 'NewExpression',
|
|
513
607
|
};
|
|
514
608
|
|
|
609
|
+
const POLYFILL_PATTERNS = [
|
|
610
|
+
// Array methods
|
|
611
|
+
{ pattern: /Array\.prototype\.toSorted/, feature: 'ArrayToSorted' },
|
|
612
|
+
{ pattern: /Array\.prototype\.findLast/, feature: 'ArrayFindLast' },
|
|
613
|
+
{ pattern: /Array\.prototype\.findLastIndex/, feature: 'ArrayFindLastIndex' },
|
|
614
|
+
{ pattern: /Array\.prototype\.at/, feature: 'ArrayAt' },
|
|
615
|
+
|
|
616
|
+
// String methods
|
|
617
|
+
{ pattern: /String\.prototype\.replaceAll/, feature: 'StringReplaceAll' },
|
|
618
|
+
{ pattern: /String\.prototype\.matchAll/, feature: 'StringMatchAll' },
|
|
619
|
+
{ pattern: /String\.prototype\.at/, feature: 'StringAt' },
|
|
620
|
+
|
|
621
|
+
// Object methods
|
|
622
|
+
{ pattern: /Object\.hasOwn/, feature: 'ObjectHasOwn' },
|
|
623
|
+
|
|
624
|
+
// Promise methods
|
|
625
|
+
{ pattern: /Promise\.any/, feature: 'PromiseAny' },
|
|
626
|
+
|
|
627
|
+
// RegExp methods
|
|
628
|
+
{ pattern: /RegExp\.prototype\.exec/, feature: 'RegExpExec' },
|
|
629
|
+
|
|
630
|
+
// Global methods
|
|
631
|
+
{ pattern: /globalThis/, feature: 'GlobalThis' },
|
|
632
|
+
];
|
|
633
|
+
|
|
634
|
+
const IMPORT_PATTERNS = [
|
|
635
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.to-sorted['"]/, feature: 'ArrayToSorted' },
|
|
636
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.find-last['"]/, feature: 'ArrayFindLast' },
|
|
637
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.find-last-index['"]/, feature: 'ArrayFindLastIndex' },
|
|
638
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.at['"]/, feature: 'ArrayAt' },
|
|
639
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.string\.replace-all['"]/, feature: 'StringReplaceAll' },
|
|
640
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.string\.match-all['"]/, feature: 'StringMatchAll' },
|
|
641
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.string\.at['"]/, feature: 'StringAt' },
|
|
642
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.object\.has-own['"]/, feature: 'ObjectHasOwn' },
|
|
643
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.promise\.any['"]/, feature: 'PromiseAny' },
|
|
644
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.regexp\.exec['"]/, feature: 'RegExpExec' },
|
|
645
|
+
{ pattern: /from\s+['"]core-js\/modules\/es\.global-this['"]/, feature: 'GlobalThis' },
|
|
646
|
+
];
|
|
647
|
+
|
|
515
648
|
const BROWSER_TO_ES_VERSION = {
|
|
516
649
|
ie: {
|
|
517
650
|
'11': 5
|
|
@@ -574,71 +707,93 @@ const BROWSER_TO_ES_VERSION = {
|
|
|
574
707
|
}
|
|
575
708
|
};
|
|
576
709
|
|
|
710
|
+
const JS_VERSIONS = [
|
|
711
|
+
'es3', 'es4', 'es5', 'es6', 'es2015',
|
|
712
|
+
'es7', 'es2016', 'es8', 'es2017', 'es9', 'es2018',
|
|
713
|
+
'es10', 'es2019', 'es11', 'es2020', 'es12', 'es2021',
|
|
714
|
+
'es13', 'es2022', 'es14', 'es2023', 'es15', 'es2024', 'es16', 'es2025', 'checkBrowser'
|
|
715
|
+
];
|
|
716
|
+
|
|
577
717
|
/**
|
|
578
|
-
* Maps feature names from ES_FEATURES to their polyfill patterns
|
|
579
|
-
* This
|
|
718
|
+
* Maps feature names from ES_FEATURES to their polyfill patterns.
|
|
719
|
+
* This version uses standardized keys and robust regex for both manual and module polyfills.
|
|
580
720
|
*/
|
|
581
721
|
const FEATURE_TO_POLYFILL_MAP = {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
/
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
/
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
/
|
|
597
|
-
/from\s+['"]core-js\/modules\/es\.array\.at['"]/
|
|
598
|
-
],
|
|
722
|
+
// ES2015 (ES6)
|
|
723
|
+
'Array.from': [/\bArray\.from\s*=/, /['"]core-js\/modules\/es\.array\.from['"]/],
|
|
724
|
+
'Array.of': [/\bArray\.of\s*=/, /['"]core-js\/modules\/es\.array\.of['"]/],
|
|
725
|
+
'Array.prototype.find': [/\bArray\.prototype\.find\s*=/, /['"]core-js\/modules\/es\.array\.find['"]/],
|
|
726
|
+
'Array.prototype.findIndex': [/\bArray\.prototype\.findIndex\s*=/, /['"]core-js\/modules\/es\.array\.find-index['"]/],
|
|
727
|
+
'Object.assign': [/\bObject\.assign\s*=/, /['"]core-js\/modules\/es\.object\.assign['"]/, /object-assign/],
|
|
728
|
+
'Promise': [/\bPromise\s*=/, /['"]core-js\/modules\/es\.promise['"]/, /es6-promise/],
|
|
729
|
+
'String.prototype.startsWith': [/\bString\.prototype\.startsWith\s*=/, /['"]core-js\/modules\/es\.string\.starts-with['"]/],
|
|
730
|
+
'String.prototype.endsWith': [/\bString\.prototype\.endsWith\s*=/, /['"]core-js\/modules\/es\.string\.ends-with['"]/],
|
|
731
|
+
'String.prototype.includes': [/\bString\.prototype\.includes\s*=/, /['"]core-js\/modules\/es\.string\.includes['"]/],
|
|
732
|
+
'Symbol': [/\bSymbol\s*=/, /['"]core-js\/modules\/es\.symbol['"]/],
|
|
733
|
+
'Map': [/\bMap\s*=/, /['"]core-js\/modules\/es\.map['"]/],
|
|
734
|
+
'Set': [/\bSet\s*=/, /['"]core-js\/modules\/es\.set['"]/],
|
|
735
|
+
'WeakMap': [/\bWeakMap\s*=/, /['"]core-js\/modules\/es\.weak-map['"]/],
|
|
736
|
+
'WeakSet': [/\bWeakSet\s*=/, /['"]core-js\/modules\/es\.weak-set['"]/],
|
|
599
737
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
/String\.prototype\.replaceAll/,
|
|
603
|
-
/from\s+['"]core-js\/modules\/es\.string\.replace-all['"]/
|
|
604
|
-
],
|
|
605
|
-
'StringMatchAll': [
|
|
606
|
-
/String\.prototype\.matchAll/,
|
|
607
|
-
/from\s+['"]core-js\/modules\/es\.string\.match-all['"]/
|
|
608
|
-
],
|
|
609
|
-
'StringAt': [
|
|
610
|
-
/String\.prototype\.at/,
|
|
611
|
-
/from\s+['"]core-js\/modules\/es\.string\.at['"]/
|
|
612
|
-
],
|
|
738
|
+
// ES2016
|
|
739
|
+
'Array.prototype.includes': [/\bArray\.prototype\.includes\s*=/, /['"]core-js\/modules\/es\.array\.includes['"]/],
|
|
613
740
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
741
|
+
// ES2017
|
|
742
|
+
'Object.values': [/\bObject\.values\s*=/, /['"]core-js\/modules\/es\.object\.values['"]/],
|
|
743
|
+
'Object.entries': [/\bObject\.entries\s*=/, /['"]core-js\/modules\/es\.object\.entries['"]/],
|
|
744
|
+
'Object.getOwnPropertyDescriptors': [/\bObject\.getOwnPropertyDescriptors\s*=/, /['"]core-js\/modules\/es\.object\.get-own-property-descriptors['"]/],
|
|
745
|
+
'String.prototype.padStart': [/\bString\.prototype\.padStart\s*=/, /['"]core-js\/modules\/es\.string\.pad-start['"]/],
|
|
746
|
+
'String.prototype.padEnd': [/\bString\.prototype\.padEnd\s*=/, /['"]core-js\/modules\/es\.string\.pad-end['"]/],
|
|
619
747
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
/Promise\.any/,
|
|
623
|
-
/from\s+['"]core-js\/modules\/es\.promise\.any['"]/
|
|
624
|
-
],
|
|
748
|
+
// ES2018
|
|
749
|
+
'Promise.prototype.finally': [/\bPromise\.prototype\.finally\s*=/, /['"]core-js\/modules\/es\.promise\.finally['"]/],
|
|
625
750
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
751
|
+
// ES2019
|
|
752
|
+
'Array.prototype.flat': [/\bArray\.prototype\.flat\s*=/, /['"]core-js\/modules\/es\.array\.flat['"]/],
|
|
753
|
+
'Array.prototype.flatMap': [/\bArray\.prototype\.flatMap\s*=/, /['"]core-js\/modules\/es\.array\.flat-map['"]/],
|
|
754
|
+
'Object.fromEntries': [/\bObject\.fromEntries\s*=/, /['"]core-js\/modules\/es\.object\.from-entries['"]/],
|
|
755
|
+
'String.prototype.trimStart': [/\bString\.prototype\.trimStart\s*=/, /['"]core-js\/modules\/es\.string\.trim-start['"]/],
|
|
756
|
+
'String.prototype.trimEnd': [/\bString\.prototype\.trimEnd\s*=/, /['"]core-js\/modules\/es\.string\.trim-end['"]/],
|
|
631
757
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
/
|
|
635
|
-
/
|
|
636
|
-
|
|
758
|
+
// ES2020
|
|
759
|
+
'Promise.allSettled': [/\bPromise\.allSettled\s*=/, /['"]core-js\/modules\/es\.promise\.all-settled['"]/],
|
|
760
|
+
'String.prototype.matchAll': [/\bString\.prototype\.matchAll\s*=/, /['"]core-js\/modules\/es\.string\.match-all['"]/],
|
|
761
|
+
'globalThis': [/globalThis\s*=/, /['"]core-js\/modules\/es\.global-this['"]/],
|
|
762
|
+
'BigInt': [/\bBigInt\s*=/, /['"]core-js\/modules\/es\.bigint['"]/],
|
|
763
|
+
|
|
764
|
+
// ES2021
|
|
765
|
+
'Promise.any': [/\bPromise\.any\s*=/, /['"]core-js\/modules\/es\.promise\.any['"]/],
|
|
766
|
+
'String.prototype.replaceAll': [/\bString\.prototype\.replaceAll\s*=/, /['"]core-js\/modules\/es\.string\.replace-all['"]/],
|
|
767
|
+
|
|
768
|
+
// ES2022
|
|
769
|
+
'Array.prototype.at': [/\bArray\.prototype\.at\s*=/, /['"]core-js\/modules\/es\.array\.at['"]/],
|
|
770
|
+
'String.prototype.at': [/\bString\.prototype\.at\s*=/, /['"]core-js\/modules\/es\.string\.at['"]/],
|
|
771
|
+
|
|
772
|
+
// ES2023
|
|
773
|
+
'Array.prototype.findLast': [/\bArray\.prototype\.findLast\s*=/, /['"]core-js\/modules\/es\.array\.find-last['"]/],
|
|
774
|
+
'Array.prototype.findLastIndex': [/\bArray\.prototype\.findLastIndex\s*=/, /['"]core-js\/modules\/es\.array\.find-last-index['"]/],
|
|
775
|
+
'Array.prototype.toReversed': [/\bArray\.prototype\.toReversed\s*=/, /['"]core-js\/modules\/es\.array\.to-reversed['"]/],
|
|
776
|
+
'Array.prototype.toSorted': [/\bArray\.prototype\.toSorted\s*=/, /['"]core-js\/modules\/es\.array\.to-sorted['"]/],
|
|
777
|
+
'Array.prototype.toSpliced': [/\bArray\.prototype\.toSpliced\s*=/, /['"]core-js\/modules\/es\.array\.to-spliced['"]/],
|
|
778
|
+
'Array.prototype.with': [/\bArray\.prototype\.with\s*=/, /['"]core-js\/modules\/es\.array\.with['"]/],
|
|
779
|
+
|
|
780
|
+
// ES2024
|
|
781
|
+
'Object.hasOwn': [/\bObject\.hasOwn\s*=/, /['"]core-js\/modules\/es\.object\.has-own['"]/],
|
|
782
|
+
'String.prototype.isWellFormed': [/\bString\.prototype\.isWellFormed\s*=/, /['"]core-js\/modules\/es\.string\.is-well-formed['"]/],
|
|
783
|
+
'String.prototype.toWellFormed': [/\bString\.prototype\.toWellFormed\s*=/, /['"]core-js\/modules\/es\.string\.to-well-formed['"]/],
|
|
784
|
+
|
|
785
|
+
// ES2025
|
|
786
|
+
'Array.prototype.group': [/\bArray\.prototype\.group\s*=/, /['"]core-js\/modules\/es\.array\.group['"]/],
|
|
787
|
+
'Array.prototype.groupToMap': [/\bArray\.prototype\.groupToMap\s*=/, /['"]core-js\/modules\/es\.array\.group-to-map['"]/],
|
|
788
|
+
'Promise.try': [/\bPromise\.try\s*=/, /['"]core-js\/modules\/es\.promise\.try['"]/],
|
|
637
789
|
};
|
|
638
790
|
|
|
639
791
|
module.exports = {
|
|
640
792
|
ES_FEATURES,
|
|
641
793
|
NODE_TYPES,
|
|
794
|
+
POLYFILL_PATTERNS,
|
|
795
|
+
IMPORT_PATTERNS,
|
|
642
796
|
BROWSER_TO_ES_VERSION,
|
|
643
|
-
FEATURE_TO_POLYFILL_MAP
|
|
797
|
+
FEATURE_TO_POLYFILL_MAP,
|
|
798
|
+
JS_VERSIONS,
|
|
644
799
|
};
|
package/detectFeatures.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const acorn = require('acorn');
|
|
2
2
|
const walk = require('acorn-walk');
|
|
3
|
-
const { ES_FEATURES } = require('./constants');
|
|
3
|
+
const { ES_FEATURES, POLYFILL_PATTERNS, IMPORT_PATTERNS } = require('./constants');
|
|
4
4
|
const { checkMap } = require('./utils');
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -8,47 +8,21 @@ const { checkMap } = require('./utils');
|
|
|
8
8
|
* @param {string} code - The source code to check
|
|
9
9
|
* @param {Set} polyfills - Set to store detected polyfills
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
const detectPolyfills = (
|
|
12
|
+
code,
|
|
13
|
+
polyfills,
|
|
14
|
+
{
|
|
15
|
+
polyfillPatterns = POLYFILL_PATTERNS,
|
|
16
|
+
importPatterns = IMPORT_PATTERNS
|
|
17
|
+
} = {}) => {
|
|
12
18
|
if (code.includes('core-js') || code.includes('polyfill')) {
|
|
13
|
-
const polyfillPatterns = [
|
|
14
|
-
{ pattern: /Array\.prototype\.toSorted/, feature: 'ArrayToSorted' },
|
|
15
|
-
{ pattern: /Array\.prototype\.findLast/, feature: 'ArrayFindLast' },
|
|
16
|
-
{ pattern: /Array\.prototype\.findLastIndex/, feature: 'ArrayFindLastIndex' },
|
|
17
|
-
{ pattern: /Array\.prototype\.at/, feature: 'ArrayAt' },
|
|
18
|
-
{ pattern: /String\.prototype\.replaceAll/, feature: 'StringReplaceAll' },
|
|
19
|
-
{ pattern: /String\.prototype\.matchAll/, feature: 'StringMatchAll' },
|
|
20
|
-
{ pattern: /String\.prototype\.at/, feature: 'StringAt' },
|
|
21
|
-
{ pattern: /Object\.hasOwn/, feature: 'ObjectHasOwn' },
|
|
22
|
-
{ pattern: /Promise\.any/, feature: 'PromiseAny' },
|
|
23
|
-
{ pattern: /RegExp\.prototype\.exec/, feature: 'RegExpExec' },
|
|
24
|
-
{ pattern: /globalThis/, feature: 'GlobalThis' },
|
|
25
|
-
];
|
|
26
|
-
|
|
27
19
|
for (const { pattern, feature } of polyfillPatterns) {
|
|
28
|
-
if (pattern.test(code))
|
|
29
|
-
polyfills.add(feature);
|
|
30
|
-
}
|
|
20
|
+
if (pattern.test(code)) polyfills.add(feature);
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
if (code.includes('import') && code.includes('core-js')) {
|
|
34
|
-
const importPatterns = [
|
|
35
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.to-sorted['"]/, feature: 'ArrayToSorted' },
|
|
36
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.find-last['"]/, feature: 'ArrayFindLast' },
|
|
37
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.find-last-index['"]/, feature: 'ArrayFindLastIndex' },
|
|
38
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.array\.at['"]/, feature: 'ArrayAt' },
|
|
39
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.string\.replace-all['"]/, feature: 'StringReplaceAll' },
|
|
40
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.string\.match-all['"]/, feature: 'StringMatchAll' },
|
|
41
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.string\.at['"]/, feature: 'StringAt' },
|
|
42
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.object\.has-own['"]/, feature: 'ObjectHasOwn' },
|
|
43
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.promise\.any['"]/, feature: 'PromiseAny' },
|
|
44
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.regexp\.exec['"]/, feature: 'RegExpExec' },
|
|
45
|
-
{ pattern: /from\s+['"]core-js\/modules\/es\.global-this['"]/, feature: 'GlobalThis' },
|
|
46
|
-
];
|
|
47
|
-
|
|
48
24
|
for (const { pattern, feature } of importPatterns) {
|
|
49
|
-
if (pattern.test(code))
|
|
50
|
-
polyfills.add(feature);
|
|
51
|
-
}
|
|
25
|
+
if (pattern.test(code)) polyfills.add(feature);
|
|
52
26
|
}
|
|
53
27
|
}
|
|
54
28
|
}
|
|
@@ -58,9 +32,7 @@ const detectFeatures = (code, ecmaVersion, sourceType, ignoreList = new Set(), o
|
|
|
58
32
|
const { checkForPolyfills } = options;
|
|
59
33
|
|
|
60
34
|
const polyfills = new Set();
|
|
61
|
-
if (checkForPolyfills)
|
|
62
|
-
detectPolyfills(code, polyfills);
|
|
63
|
-
}
|
|
35
|
+
if (checkForPolyfills) detectPolyfills(code, polyfills);
|
|
64
36
|
|
|
65
37
|
const ast = acorn.parse(code, {
|
|
66
38
|
ecmaVersion: 'latest',
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const detectFeatures = require('./detectFeatures')
|
|
|
10
10
|
let polyfillDetector = null;
|
|
11
11
|
const pkg = require('./package.json')
|
|
12
12
|
const { lilconfig } = require('lilconfig');
|
|
13
|
+
const { JS_VERSIONS } = require('./constants');
|
|
13
14
|
const { parseIgnoreList, createLogger, generateBashCompletion, generateZshCompletion } = require('./utils');
|
|
14
15
|
|
|
15
16
|
program.configureOutput({
|
|
@@ -69,7 +70,7 @@ program
|
|
|
69
70
|
.version(pkg.version)
|
|
70
71
|
.argument(
|
|
71
72
|
'[ecmaVersion]',
|
|
72
|
-
'ecmaVersion to check files against. Can be: es3, es4, es5, es6/es2015, es7/es2016, es8/es2017, es9/es2018, es10/es2019, es11/es2020, es12/es2021, es13/es2022, es14/es2023, checkBrowser',
|
|
73
|
+
'ecmaVersion to check files against. Can be: es3, es4, es5, es6/es2015, es7/es2016, es8/es2017, es9/es2018, es10/es2019, es11/es2020, es12/es2021, es13/es2022, es14/es2023, es15/es2024, es16/es2025, checkBrowser',
|
|
73
74
|
)
|
|
74
75
|
.argument('[files...]', 'a glob of files to to test the EcmaScript version against')
|
|
75
76
|
.option('--module', 'use ES modules')
|
|
@@ -92,7 +93,7 @@ program
|
|
|
92
93
|
.addOption(new Option('--ignore-file <path>', 'path to JSON file containing features to ignore').hideHelp())
|
|
93
94
|
.option('--ignoreFile <path>', 'path to JSON file containing features to ignore')
|
|
94
95
|
.option('--allowList <features>', 'comma-separated list of features to allow even in lower ES versions, e.g., "const,let"')
|
|
95
|
-
.addOption(new Option('--checkBrowser', 'use browserslist configuration to determine ES version, use checkBrowser argument instead of ecmaVersion').hideHelp())
|
|
96
|
+
.addOption(new Option('--checkBrowser', 'use browserslist configuration to determine ES version, use checkBrowser argument instead of ecmaVersion', false).hideHelp())
|
|
96
97
|
.option('--browserslistQuery <query>', 'browserslist query')
|
|
97
98
|
.option('--browserslistPath <path>', 'path to custom browserslist configuration')
|
|
98
99
|
.option('--browserslistEnv <env>', 'browserslist environment to use')
|
|
@@ -145,6 +146,13 @@ program
|
|
|
145
146
|
process.exit(1)
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
const validEcmaVersionValues = new Set(JS_VERSIONS);
|
|
150
|
+
|
|
151
|
+
if (options.checkBrowser && ecmaVersionArg && !validEcmaVersionValues.has(ecmaVersionArg)) {
|
|
152
|
+
filesArg.unshift(ecmaVersionArg);
|
|
153
|
+
ecmaVersionArg = 'checkBrowser';
|
|
154
|
+
}
|
|
155
|
+
|
|
148
156
|
const configs = await loadConfig(options.config);
|
|
149
157
|
const baseConfig = configs[0] || {};
|
|
150
158
|
|
|
@@ -257,7 +265,7 @@ async function runChecks(configs, logger) {
|
|
|
257
265
|
|
|
258
266
|
let ecmaVersion
|
|
259
267
|
|
|
260
|
-
const isBrowserslistCheck = Boolean(expectedEcmaVersion === 'checkBrowser' || checkBrowser);
|
|
268
|
+
const isBrowserslistCheck = Boolean(expectedEcmaVersion === 'checkBrowser' || checkBrowser !== undefined);
|
|
261
269
|
if (isBrowserslistCheck) {
|
|
262
270
|
const browserslistQuery = config.browserslistQuery;
|
|
263
271
|
try {
|
|
@@ -324,6 +332,14 @@ async function runChecks(configs, logger) {
|
|
|
324
332
|
case 'es2023':
|
|
325
333
|
ecmaVersion = '14'
|
|
326
334
|
break
|
|
335
|
+
case 'es15':
|
|
336
|
+
case 'es2024':
|
|
337
|
+
ecmaVersion = '15'
|
|
338
|
+
break
|
|
339
|
+
case 'es16':
|
|
340
|
+
case 'es2025':
|
|
341
|
+
ecmaVersion = '16'
|
|
342
|
+
break
|
|
327
343
|
default:
|
|
328
344
|
logger.error('Invalid ecmaScript version, please pass a valid version, use --help for help')
|
|
329
345
|
process.exit(1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "es-check",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.5-0",
|
|
4
4
|
"description": "Checks the ECMAScript version of .js glob against a specified version of ECMAScript with a shell command",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,19 +52,19 @@
|
|
|
52
52
|
"codependence": "^0.3.1",
|
|
53
53
|
"commitizen": "4.3.1",
|
|
54
54
|
"conventional-changelog-cli": "^5.0.0",
|
|
55
|
-
"eslint": "9.
|
|
55
|
+
"eslint": "9.30.0",
|
|
56
56
|
"eslint-config-prettier": "10.1.5",
|
|
57
57
|
"eslint-plugin-es5": "^1.5.0",
|
|
58
58
|
"husky": "9.1.7",
|
|
59
59
|
"is-ci": "^3.0.1",
|
|
60
|
-
"mocha": "11.
|
|
60
|
+
"mocha": "11.7.1",
|
|
61
61
|
"nyc": "^17.1.0",
|
|
62
62
|
"path-exists-cli": "^2.0.0",
|
|
63
|
-
"prettier": "3.
|
|
63
|
+
"prettier": "3.6.2",
|
|
64
64
|
"release-it": "19.0.3"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"acorn": "8.
|
|
67
|
+
"acorn": "8.15.0",
|
|
68
68
|
"acorn-walk": "^8.3.4",
|
|
69
69
|
"browserslist": "^4.23.3",
|
|
70
70
|
"commander": "14.0.0",
|
package/polyfillDetector.js
CHANGED
|
@@ -1,77 +1,47 @@
|
|
|
1
1
|
const { FEATURE_TO_POLYFILL_MAP } = require('./constants');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Detects polyfills in the code and returns a set of polyfilled feature names
|
|
5
|
-
*
|
|
6
|
-
* @param {
|
|
7
|
-
* @
|
|
4
|
+
* Detects polyfills in the code and returns a set of polyfilled feature names.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} code - The source code to check.
|
|
7
|
+
* @param {object} logger - A required logger instance.
|
|
8
|
+
* @param {object} [featureMap=FEATURE_TO_POLYFILL_MAP] - The map of features.
|
|
9
|
+
* @returns {Set<string>} - A set of polyfilled feature names.
|
|
8
10
|
*/
|
|
9
|
-
function detectPolyfills(code, logger) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (code.includes('core-js/modules/es.array.to-sorted')) {
|
|
18
|
-
polyfills.add('ArrayToSorted');
|
|
19
|
-
}
|
|
20
|
-
if (code.includes('core-js/modules/es.object.has-own')) {
|
|
21
|
-
polyfills.add('ObjectHasOwn');
|
|
22
|
-
}
|
|
23
|
-
if (code.includes('core-js/modules/es.string.replace-all')) {
|
|
24
|
-
polyfills.add('StringReplaceAll');
|
|
11
|
+
function detectPolyfills(code, logger, featureMap = FEATURE_TO_POLYFILL_MAP) {
|
|
12
|
+
const polyfills = new Set();
|
|
13
|
+
if (!code || !featureMap) return polyfills;
|
|
14
|
+
|
|
15
|
+
// Since logger is required, we can use it without checking for its existence.
|
|
16
|
+
// The optional chaining (?.) is still good practice for properties like isLevelEnabled.
|
|
17
|
+
if (logger?.isLevelEnabled?.('debug')) {
|
|
18
|
+
// We can log at the beginning if needed, or at the end.
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
const polyfillFeatures = Object.entries(featureMap);
|
|
22
|
+
polyfillFeatures.forEach(([feature, patterns]) => {
|
|
23
|
+
const isPolyfilled = patterns.some(pattern => pattern.test(code));
|
|
24
|
+
if (isPolyfilled) polyfills.add(feature);
|
|
25
|
+
});
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
27
|
+
if (logger?.isLevelEnabled?.('debug')) {
|
|
28
|
+
const hasPolyfills = polyfills.size > 0;
|
|
29
|
+
if (hasPolyfills) logger.debug(`ES-Check: Detected polyfills: ${Array.from(polyfills).join(', ')}`);
|
|
30
|
+
else logger.debug('ES-Check: No polyfills detected.');
|
|
38
31
|
}
|
|
39
|
-
}
|
|
40
32
|
|
|
41
|
-
if (polyfills.size === 0 && (code.includes('polyfill') || code.includes('Array.prototype') || code.includes('Object.') || code.includes('String.prototype'))) {
|
|
42
|
-
} else if (polyfills.size > 0) {
|
|
43
33
|
return polyfills;
|
|
44
|
-
} else if (!code.includes('core-js') && !code.includes('polyfill') && !code.includes('Array.prototype')) {
|
|
45
|
-
return polyfills;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
for (const [featureName, patterns] of Object.entries(FEATURE_TO_POLYFILL_MAP)) {
|
|
49
|
-
for (const pattern of patterns) {
|
|
50
|
-
if (pattern.test(code)) {
|
|
51
|
-
polyfills.add(featureName);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (logger?.isLevelEnabled?.('debug') && polyfills.size > 0) {
|
|
58
|
-
logger.debug(`ES-Check: Detected polyfills: ${Array.from(polyfills).join(', ')}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return polyfills;
|
|
62
34
|
}
|
|
63
35
|
|
|
64
36
|
/**
|
|
65
|
-
* Filters unsupported features by removing those that have been polyfilled
|
|
37
|
+
* Filters unsupported features by removing those that have been polyfilled.
|
|
66
38
|
* @param {Array<string>} unsupportedFeatures - List of unsupported features
|
|
67
39
|
* @param {Set<string>} polyfills - Set of polyfilled feature names
|
|
68
40
|
* @returns {Array<string>} - Filtered list of unsupported features
|
|
69
41
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
42
|
+
const filterPolyfilled = (unsupportedFeatures, polyfills) => {
|
|
43
|
+
const hasPolyfills = polyfills && polyfills.size > 0;
|
|
44
|
+
if (!hasPolyfills) return unsupportedFeatures;
|
|
75
45
|
return unsupportedFeatures.filter(feature => !polyfills.has(feature));
|
|
76
46
|
}
|
|
77
47
|
|
package/utils.js
CHANGED
|
@@ -113,6 +113,12 @@ const checkMap = {
|
|
|
113
113
|
node.callee.property.type === 'Identifier' &&
|
|
114
114
|
node.callee.property.name === astInfo.property
|
|
115
115
|
);
|
|
116
|
+
} else if (astInfo.property) {
|
|
117
|
+
// Check for method calls with any object when only property is specified
|
|
118
|
+
return (
|
|
119
|
+
node.callee.property.type === 'Identifier' &&
|
|
120
|
+
node.callee.property.name === astInfo.property
|
|
121
|
+
);
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
return false;
|
|
@@ -180,7 +186,7 @@ function createLogger(options = {}) {
|
|
|
180
186
|
level,
|
|
181
187
|
stderrLevels: ['error', 'warn'],
|
|
182
188
|
format: winston.format.combine(
|
|
183
|
-
...(supportsColor.stdout
|
|
189
|
+
...(supportsColor.stdout && !noColor ? [winston.format.colorize()] : []),
|
|
184
190
|
winston.format.simple(),
|
|
185
191
|
),
|
|
186
192
|
})
|
|
@@ -217,7 +223,7 @@ _${cmdName.replace(/-/g, '_')}_completion() {
|
|
|
217
223
|
opts="${optsStr}"
|
|
218
224
|
|
|
219
225
|
# ES versions
|
|
220
|
-
es_versions="es3 es5 es6 es2015 es7 es2016 es8 es2017 es9 es2018 es10 es2019 es11 es2020 es12 es2021 es13 es2022 es14 es2023"
|
|
226
|
+
es_versions="es3 es5 es6 es2015 es7 es2016 es8 es2017 es9 es2018 es10 es2019 es11 es2020 es12 es2021 es13 es2022 es14 es2023 es15 es2024 es16 es2025"
|
|
221
227
|
|
|
222
228
|
# Handle special cases based on previous argument
|
|
223
229
|
case "\$prev" in
|
|
@@ -288,6 +294,10 @@ _${cmdName.replace(/-/g, '_')}() {
|
|
|
288
294
|
"es2022:ECMAScript 2022"
|
|
289
295
|
"es14:ECMAScript 2023"
|
|
290
296
|
"es2023:ECMAScript 2023"
|
|
297
|
+
"es15:ECMAScript 2024"
|
|
298
|
+
"es2024:ECMAScript 2024"
|
|
299
|
+
"es16:ECMAScript 2025"
|
|
300
|
+
"es2025:ECMAScript 2025"
|
|
291
301
|
)
|
|
292
302
|
|
|
293
303
|
# Commands
|