es-check 9.1.4 → 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.
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
@@ -578,74 +711,88 @@ const JS_VERSIONS = [
578
711
  'es3', 'es4', 'es5', 'es6', 'es2015',
579
712
  'es7', 'es2016', 'es8', 'es2017', 'es9', 'es2018',
580
713
  'es10', 'es2019', 'es11', 'es2020', 'es12', 'es2021',
581
- 'es13', 'es2022', 'es14', 'es2023', 'checkBrowser'
714
+ 'es13', 'es2022', 'es14', 'es2023', 'es15', 'es2024', 'es16', 'es2025', 'checkBrowser'
582
715
  ];
583
716
 
584
717
  /**
585
- * Maps feature names from ES_FEATURES to their polyfill patterns
586
- * 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.
587
720
  */
588
721
  const FEATURE_TO_POLYFILL_MAP = {
589
- // Array methods
590
- 'ArrayToSorted': [
591
- /Array\.prototype\.toSorted/,
592
- /from\s+['"]core-js\/modules\/es\.array\.to-sorted['"]/
593
- ],
594
- 'ArrayFindLast': [
595
- /Array\.prototype\.findLast/,
596
- /from\s+['"]core-js\/modules\/es\.array\.find-last['"]/
597
- ],
598
- 'ArrayFindLastIndex': [
599
- /Array\.prototype\.findLastIndex/,
600
- /from\s+['"]core-js\/modules\/es\.array\.find-last-index['"]/
601
- ],
602
- 'ArrayAt': [
603
- /Array\.prototype\.at/,
604
- /from\s+['"]core-js\/modules\/es\.array\.at['"]/
605
- ],
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['"]/],
606
737
 
607
- // String methods
608
- 'StringReplaceAll': [
609
- /String\.prototype\.replaceAll/,
610
- /from\s+['"]core-js\/modules\/es\.string\.replace-all['"]/
611
- ],
612
- 'StringMatchAll': [
613
- /String\.prototype\.matchAll/,
614
- /from\s+['"]core-js\/modules\/es\.string\.match-all['"]/
615
- ],
616
- 'StringAt': [
617
- /String\.prototype\.at/,
618
- /from\s+['"]core-js\/modules\/es\.string\.at['"]/
619
- ],
738
+ // ES2016
739
+ 'Array.prototype.includes': [/\bArray\.prototype\.includes\s*=/, /['"]core-js\/modules\/es\.array\.includes['"]/],
620
740
 
621
- // Object methods
622
- 'ObjectHasOwn': [
623
- /Object\.hasOwn/,
624
- /from\s+['"]core-js\/modules\/es\.object\.has-own['"]/
625
- ],
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['"]/],
626
747
 
627
- // Promise methods
628
- 'PromiseAny': [
629
- /Promise\.any/,
630
- /from\s+['"]core-js\/modules\/es\.promise\.any['"]/
631
- ],
748
+ // ES2018
749
+ 'Promise.prototype.finally': [/\bPromise\.prototype\.finally\s*=/, /['"]core-js\/modules\/es\.promise\.finally['"]/],
632
750
 
633
- // RegExp methods
634
- 'RegExpExec': [
635
- /RegExp\.prototype\.exec/,
636
- /from\s+['"]core-js\/modules\/es\.regexp\.exec['"]/
637
- ],
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['"]/],
638
757
 
639
- // Global methods
640
- 'GlobalThis': [
641
- /globalThis/,
642
- /from\s+['"]core-js\/modules\/es\.global-this['"]/
643
- ],
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['"]/],
644
789
  };
645
790
 
646
791
  module.exports = {
647
792
  ES_FEATURES,
648
793
  NODE_TYPES,
794
+ POLYFILL_PATTERNS,
795
+ IMPORT_PATTERNS,
649
796
  BROWSER_TO_ES_VERSION,
650
797
  FEATURE_TO_POLYFILL_MAP,
651
798
  JS_VERSIONS,
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
@@ -70,7 +70,7 @@ program
70
70
  .version(pkg.version)
71
71
  .argument(
72
72
  '[ecmaVersion]',
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, 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',
74
74
  )
75
75
  .argument('[files...]', 'a glob of files to to test the EcmaScript version against')
76
76
  .option('--module', 'use ES modules')
@@ -332,6 +332,14 @@ async function runChecks(configs, logger) {
332
332
  case 'es2023':
333
333
  ecmaVersion = '14'
334
334
  break
335
+ case 'es15':
336
+ case 'es2024':
337
+ ecmaVersion = '15'
338
+ break
339
+ case 'es16':
340
+ case 'es2025':
341
+ ecmaVersion = '16'
342
+ break
335
343
  default:
336
344
  logger.error('Invalid ecmaScript version, please pass a valid version, use --help for help')
337
345
  process.exit(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-check",
3
- "version": "9.1.4",
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