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 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
  ![pass](https://user-images.githubusercontent.com/1074042/31471487-d7be22ee-ae9d-11e7-86e2-2c0f71cfffe6.jpg)
87
97
 
88
- Fail
98
+ ### Fail
99
+
89
100
  ![fail](https://user-images.githubusercontent.com/1074042/31471486-d65c3a80-ae9d-11e7-94fd-68b7acdb2d89.jpg)
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 helps us identify which features are being polyfilled
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
- // Array methods
583
- 'ArrayToSorted': [
584
- /Array\.prototype\.toSorted/,
585
- /from\s+['"]core-js\/modules\/es\.array\.to-sorted['"]/
586
- ],
587
- 'ArrayFindLast': [
588
- /Array\.prototype\.findLast/,
589
- /from\s+['"]core-js\/modules\/es\.array\.find-last['"]/
590
- ],
591
- 'ArrayFindLastIndex': [
592
- /Array\.prototype\.findLastIndex/,
593
- /from\s+['"]core-js\/modules\/es\.array\.find-last-index['"]/
594
- ],
595
- 'ArrayAt': [
596
- /Array\.prototype\.at/,
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
- // String methods
601
- 'StringReplaceAll': [
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
- // Object methods
615
- 'ObjectHasOwn': [
616
- /Object\.hasOwn/,
617
- /from\s+['"]core-js\/modules\/es\.object\.has-own['"]/
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
- // Promise methods
621
- 'PromiseAny': [
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
- // RegExp methods
627
- 'RegExpExec': [
628
- /RegExp\.prototype\.exec/,
629
- /from\s+['"]core-js\/modules\/es\.regexp\.exec['"]/
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
- // Global methods
633
- 'GlobalThis': [
634
- /globalThis/,
635
- /from\s+['"]core-js\/modules\/es\.global-this['"]/
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
- function detectPolyfills(code, polyfills) {
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",
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.28.0",
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.5.0",
60
+ "mocha": "11.7.1",
61
61
  "nyc": "^17.1.0",
62
62
  "path-exists-cli": "^2.0.0",
63
- "prettier": "3.5.3",
63
+ "prettier": "3.6.2",
64
64
  "release-it": "19.0.3"
65
65
  },
66
66
  "dependencies": {
67
- "acorn": "8.14.1",
67
+ "acorn": "8.15.0",
68
68
  "acorn-walk": "^8.3.4",
69
69
  "browserslist": "^4.23.3",
70
70
  "commander": "14.0.0",
@@ -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
- * @param {string} code - The source code to check
6
- * @param {Object} logger - Winston logger instance for debug output
7
- * @returns {Set<string>} - Set of polyfilled feature names
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
- const polyfills = new Set();
11
-
12
- if (!code) {
13
- return polyfills;
14
- }
15
-
16
- if (code.includes('import') && code.includes('core-js')) {
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
- if (polyfills.size > 0) {
28
- return polyfills;
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
- for (const [featureName, patterns] of Object.entries(FEATURE_TO_POLYFILL_MAP)) {
32
- for (const pattern of patterns) {
33
- if (pattern.test(code)) {
34
- polyfills.add(featureName);
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
- function filterPolyfilled(unsupportedFeatures, polyfills) {
71
- if (!polyfills || polyfills.size === 0) {
72
- return unsupportedFeatures;
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 || !noColor ? [winston.format.colorize()] : []),
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