es-check 9.5.4 → 9.6.0-1

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
@@ -132,6 +132,7 @@ Here's a comprehensive list of all available options:
132
132
  | `-V, --version` | Output the version number |
133
133
  | `--module` | Use ES modules (default: false) |
134
134
  | `--allowHashBang` | If the code starts with #! treat it as a comment (default: false) |
135
+ | `--typescript, --ts` | Enable TypeScript file support (default: false) |
135
136
  | `--files <files>` | A glob of files to test the ECMAScript version against (alias for [files...]) |
136
137
  | `--not <files>` | Folder or file names to skip |
137
138
  | `--noColor` | Disable use of colors in output (default: false) |
@@ -167,6 +168,14 @@ es-check es6 './dist/**/*.js' --module
167
168
  es-check es6 './tests/*.js' --allowHashBang
168
169
  ```
169
170
 
171
+ **Checking TypeScript files:**
172
+
173
+ ```sh
174
+ es-check es5 './src/**/*.{js,ts}' --typescript
175
+ ```
176
+
177
+ This is experimental functionality that strips TypeScript type annotations and tests the resulting JavaScript against the specified ES version.
178
+
170
179
  **Skipping specific files or directories:**
171
180
 
172
181
  ```sh
@@ -1,12 +1,6 @@
1
1
  const browserslist = require("browserslist");
2
2
  const { BROWSER_TO_ES_VERSION } = require("./constants/versions");
3
3
 
4
- /**
5
- * Determines the ES version supported by a specific browser and version
6
- * @param {string} browser - Browser name
7
- * @param {string} version - Browser version
8
- * @returns {number} - ES version (5, 6, etc.)
9
- */
10
4
  function getESVersionForBrowser(browser, version) {
11
5
  const defaultVersion = 5;
12
6
 
@@ -43,14 +37,6 @@ function getESVersionForBrowser(browser, version) {
43
37
  return BROWSER_TO_ES_VERSION[browser][matchedVersion];
44
38
  }
45
39
 
46
- /**
47
- * Determines the minimum ES version required to support all browsers in the browserslist
48
- * @param {Object} options - Options object
49
- * @param {string} [options.browserslistQuery]
50
- * @param {string} [options.browserslistPath] - Optional custom path to browserslist config
51
- * @param {string} [options.browserslistEnv] - Optional browserslist environment to use
52
- * @returns {number} - The ES version to use (e.g., 5, 6, etc.)
53
- */
54
40
  function getESVersionFromBrowserslist(options = {}) {
55
41
  const { browserslistPath, browserslistEnv, browserslistQuery } = options;
56
42
 
@@ -1,6 +1,10 @@
1
1
  const fs = require("fs");
2
2
  const { parseIgnoreList, processBatchedFiles } = require("../helpers");
3
3
  const { determineInvocationType } = require("../cli/utils");
4
+ const { LATEST_PARSER_VERSION } = require("../constants/versions");
5
+ const {
6
+ getMinimumParserVersion,
7
+ } = require("../constants/featureParserMapping");
4
8
  const {
5
9
  parseFilePatterns,
6
10
  validateConfig,
@@ -81,9 +85,17 @@ function processConfig(config, context) {
81
85
 
82
86
  const { ecmaVersion } = versionResult;
83
87
  const useLatestForParsing = config.checkFeatures;
88
+ const targetEsVersion = parseInt(ecmaVersion, 10);
89
+
90
+ const enableFeatureDetection = config.checkBrowser || config.checkFeatures;
91
+ const minRequiredParser = getMinimumParserVersion(
92
+ targetEsVersion,
93
+ enableFeatureDetection,
94
+ );
95
+
84
96
  const parserEcmaVersion = useLatestForParsing
85
- ? 2025
86
- : parseInt(ecmaVersion, 10);
97
+ ? LATEST_PARSER_VERSION
98
+ : minRequiredParser;
87
99
  const acornOpts = { ecmaVersion: parserEcmaVersion, silent: true };
88
100
 
89
101
  if (isDebug) {
@@ -301,6 +301,7 @@ function processFullAST(
301
301
  parserOptions,
302
302
  acorn,
303
303
  file,
304
+ config,
304
305
  );
305
306
  const hasParseError = parseError !== null;
306
307
  const shouldDebugError = hasParseError && isDebug;
@@ -367,21 +368,39 @@ function processFullAST(
367
368
  logger || { debug: () => {}, isLevelEnabled: () => false },
368
369
  );
369
370
 
370
- const filteredUnsupportedFeatures = polyfillDetector.filterPolyfilled(
371
+ let filteredUnsupportedFeatures = polyfillDetector.filterPolyfilled(
371
372
  unsupportedFeatures,
372
373
  polyfills,
373
374
  );
374
375
 
375
- const hasReduction =
376
+ const hasPolyfillReduction =
376
377
  isDebug &&
377
378
  filteredUnsupportedFeatures.length !== unsupportedFeatures.length;
378
379
 
379
- if (hasReduction) {
380
+ if (hasPolyfillReduction) {
380
381
  logger.debug(
381
382
  `ES-Check: Polyfills reduced unsupported features from ${unsupportedFeatures.length} to ${filteredUnsupportedFeatures.length}`,
382
383
  );
383
384
  }
384
385
 
386
+ if (config.ignorePolyfillable) {
387
+ const beforeIgnorePolyfillable = filteredUnsupportedFeatures.length;
388
+ filteredUnsupportedFeatures = polyfillDetector.filterPolyfillableFeatures(
389
+ filteredUnsupportedFeatures,
390
+ config.ignorePolyfillable,
391
+ );
392
+
393
+ const hasPolyfillableReduction =
394
+ isDebug &&
395
+ filteredUnsupportedFeatures.length !== beforeIgnorePolyfillable;
396
+
397
+ if (hasPolyfillableReduction) {
398
+ logger.debug(
399
+ `ES-Check: Ignoring polyfillable features reduced unsupported features from ${beforeIgnorePolyfillable} to ${filteredUnsupportedFeatures.length}`,
400
+ );
401
+ }
402
+ }
403
+
385
404
  const isSupported = filteredUnsupportedFeatures.length === 0;
386
405
 
387
406
  if (!isSupported) {
@@ -133,6 +133,11 @@ const CLI_OPTIONS = [
133
133
  "lightweight mode: faster checking with pattern matching only (skips full AST parsing)",
134
134
  default: false,
135
135
  },
136
+ {
137
+ flags: "--typescript, --ts",
138
+ description: "enable TypeScript file support",
139
+ default: false,
140
+ },
136
141
  ];
137
142
 
138
143
  const SUPPORTED_SHELLS = ["bash", "zsh"];
@@ -162,6 +167,8 @@ const COMPLETION_OPTIONS = [
162
167
  "config",
163
168
  "batchSize",
164
169
  "noCache",
170
+ "typescript",
171
+ "ts",
165
172
  ];
166
173
 
167
174
  module.exports = {
@@ -16,8 +16,10 @@ function buildConfig(ecmaVersionArg, filesArg, options, baseConfig) {
16
16
  ...baseConfig,
17
17
  module: options.module,
18
18
  allowHashBang: options.allowHashBang || options["allow-hash-bang"],
19
+ typescript: options.typescript || options.ts,
19
20
  checkFeatures: options.checkFeatures,
20
21
  checkForPolyfills: options.checkForPolyfills,
22
+ ignorePolyfillable: options.ignorePolyfillable,
21
23
  ignore: options.ignore !== undefined ? options.ignore : baseConfig.ignore,
22
24
  ignoreFile:
23
25
  ignoreFilePath !== undefined ? ignoreFilePath : baseConfig.ignoreFile,
package/lib/cli/parser.js CHANGED
@@ -2,6 +2,8 @@ const BOOLEAN_FLAGS = new Set([
2
2
  "module",
3
3
  "allowHashBang",
4
4
  "allow-hash-bang",
5
+ "typescript",
6
+ "ts",
5
7
  "noColor",
6
8
  "no-color",
7
9
  "verbose",
@@ -104,6 +106,7 @@ OPTIONS:
104
106
  --module Use ES modules
105
107
  --light Fast checking using pattern matching only
106
108
  --allowHashBang Treat #! as comment
109
+ --typescript, -ts Enable TypeScript file support
107
110
  --files <files> Files to check (comma-separated)
108
111
  --not <files> Files/folders to skip (comma-separated)
109
112
  --noColor Disable colors
@@ -113,6 +116,7 @@ OPTIONS:
113
116
  --looseGlobMatching Don't fail if no files found
114
117
  --checkFeatures Check for ES version specific features
115
118
  --checkForPolyfills Consider polyfills when checking features
119
+ --ignorePolyfillable [library] Ignore polyfillable features (optionally specify library: core-js)
116
120
  --ignore <features> Features to ignore (comma-separated)
117
121
  --ignoreFile <path> JSON file with features to ignore
118
122
  --allowList <features> Features to allow (comma-separated)
@@ -128,8 +132,11 @@ EXAMPLES:
128
132
  es-check es5 './dist/**/*.js'
129
133
  es-check es6 './src/*.js' --module
130
134
  es-check es2020 './build/**/*.js' --checkFeatures
135
+ es-check es2017 './dist/*.js' --checkFeatures --ignorePolyfillable=core-js
131
136
  es-check checkBrowser './dist/*.js' --browserslistQuery="last 2 versions"
132
137
  es-check es5 './dist/*.js' --not=./dist/vendor,./dist/legacy
138
+ es-check es5 './src/**/*.{js,ts}' --typescript
139
+ es-check es2020 './src/**/*.ts' --module -ts
133
140
 
134
141
  COMPLETION:
135
142
  es-check completion Generate bash completion
@@ -46,6 +46,7 @@ const ES13_FEATURES = {
46
46
  astInfo: {
47
47
  nodeType: "CallExpression",
48
48
  property: "at",
49
+ excludeCallerTypes: ["ObjectExpression"],
49
50
  },
50
51
  },
51
52
  ObjectHasOwn: {
@@ -0,0 +1,47 @@
1
+ const ES17_FEATURES = {
2
+ ArrayFromAsync: {
3
+ minVersion: 17,
4
+ example: "Array.fromAsync(asyncIterable)",
5
+ astInfo: {
6
+ nodeType: "CallExpression",
7
+ object: "Array",
8
+ property: "fromAsync",
9
+ },
10
+ },
11
+ ErrorIsError: {
12
+ minVersion: 17,
13
+ example: "Error.isError(value)",
14
+ astInfo: {
15
+ nodeType: "CallExpression",
16
+ object: "Error",
17
+ property: "isError",
18
+ },
19
+ },
20
+ ArrayBufferTransfer: {
21
+ minVersion: 17,
22
+ example: "buffer.transfer(newByteLength)",
23
+ astInfo: {
24
+ nodeType: "CallExpression",
25
+ property: "transfer",
26
+ },
27
+ },
28
+ ArrayBufferTransferToFixedLength: {
29
+ minVersion: 17,
30
+ example: "buffer.transferToFixedLength(newByteLength)",
31
+ astInfo: {
32
+ nodeType: "CallExpression",
33
+ property: "transferToFixedLength",
34
+ },
35
+ },
36
+ IntlDurationFormat: {
37
+ minVersion: 17,
38
+ example: "new Intl.DurationFormat('en')",
39
+ astInfo: {
40
+ nodeType: "NewExpression",
41
+ object: "Intl",
42
+ property: "DurationFormat",
43
+ },
44
+ },
45
+ };
46
+
47
+ module.exports = { ES17_FEATURES };
@@ -9,6 +9,7 @@ const { ES13_FEATURES } = require("./13");
9
9
  const { ES14_FEATURES } = require("./14");
10
10
  const { ES15_FEATURES } = require("./15");
11
11
  const { ES16_FEATURES } = require("./16");
12
+ const { ES17_FEATURES } = require("./17");
12
13
  const {
13
14
  POLYFILL_PATTERNS,
14
15
  IMPORT_PATTERNS,
@@ -27,6 +28,7 @@ const ES_FEATURES = {
27
28
  ...ES14_FEATURES,
28
29
  ...ES15_FEATURES,
29
30
  ...ES16_FEATURES,
31
+ ...ES17_FEATURES,
30
32
  };
31
33
 
32
34
  module.exports = {
@@ -1,86 +1,86 @@
1
1
  const POLYFILL_PATTERNS = [
2
- // Array methods
3
2
  { pattern: /Array\.prototype\.toSorted/, feature: "ArrayToSorted" },
4
3
  { pattern: /Array\.prototype\.findLast/, feature: "ArrayFindLast" },
5
4
  { pattern: /Array\.prototype\.findLastIndex/, feature: "ArrayFindLastIndex" },
6
5
  { pattern: /Array\.prototype\.at/, feature: "ArrayAt" },
7
-
8
- // String methods
9
6
  { pattern: /String\.prototype\.replaceAll/, feature: "StringReplaceAll" },
10
7
  { pattern: /String\.prototype\.matchAll/, feature: "StringMatchAll" },
11
8
  { pattern: /String\.prototype\.at/, feature: "StringAt" },
12
-
13
- // Object methods
14
9
  { pattern: /Object\.hasOwn/, feature: "ObjectHasOwn" },
15
-
16
- // Promise methods
17
10
  { pattern: /Promise\.any/, feature: "PromiseAny" },
18
-
19
- // RegExp methods
20
11
  { pattern: /RegExp\.prototype\.exec/, feature: "RegExpExec" },
21
-
22
- // Global methods
23
12
  { pattern: /globalThis/, feature: "GlobalThis" },
24
13
  ];
25
14
 
26
15
  const IMPORT_PATTERNS = [
27
16
  {
28
- pattern: /from\s+['"]core-js\/modules\/es\.array\.to-sorted['"]/,
17
+ pattern:
18
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.array\.to-sorted['"]/,
29
19
  feature: "ArrayToSorted",
30
20
  },
31
21
  {
32
- pattern: /from\s+['"]core-js\/modules\/es\.array\.find-last['"]/,
22
+ pattern:
23
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.array\.find-last['"]/,
33
24
  feature: "ArrayFindLast",
34
25
  },
35
26
  {
36
- pattern: /from\s+['"]core-js\/modules\/es\.array\.find-last-index['"]/,
27
+ pattern:
28
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.array\.find-last-index['"]/,
37
29
  feature: "ArrayFindLastIndex",
38
30
  },
39
31
  {
40
- pattern: /from\s+['"]core-js\/modules\/es\.array\.at['"]/,
32
+ pattern:
33
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.array\.at['"]/,
41
34
  feature: "ArrayAt",
42
35
  },
43
36
  {
44
- pattern: /from\s+['"]core-js\/modules\/es\.string\.replace-all['"]/,
37
+ pattern:
38
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.string\.replace-all['"]/,
45
39
  feature: "StringReplaceAll",
46
40
  },
47
41
  {
48
- pattern: /from\s+['"]core-js\/modules\/es\.string\.match-all['"]/,
42
+ pattern:
43
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.string\.match-all['"]/,
49
44
  feature: "StringMatchAll",
50
45
  },
51
46
  {
52
- pattern: /from\s+['"]core-js\/modules\/es\.string\.at['"]/,
47
+ pattern:
48
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.string\.at['"]/,
53
49
  feature: "StringAt",
54
50
  },
55
51
  {
56
- pattern: /from\s+['"]core-js\/modules\/es\.object\.has-own['"]/,
52
+ pattern:
53
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.object\.has-own['"]/,
57
54
  feature: "ObjectHasOwn",
58
55
  },
59
56
  {
60
- pattern: /from\s+['"]core-js\/modules\/es\.promise\.any['"]/,
57
+ pattern:
58
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.promise\.any['"]/,
61
59
  feature: "PromiseAny",
62
60
  },
63
61
  {
64
- pattern: /from\s+['"]core-js\/modules\/es\.regexp\.exec['"]/,
62
+ pattern:
63
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.regexp\.exec['"]/,
65
64
  feature: "RegExpExec",
66
65
  },
67
66
  {
68
- pattern: /from\s+['"]core-js\/modules\/es\.global-this['"]/,
67
+ pattern:
68
+ /(?:from\s+|require\s*\(\s*)['"]core-js\/modules\/es\.global-this['"]/,
69
69
  feature: "GlobalThis",
70
70
  },
71
71
  ];
72
72
 
73
- /**
74
- * Maps feature names from ES_FEATURES to their polyfill patterns.
75
- * This version uses standardized keys and robust regex for both manual and module polyfills.
76
- */
77
73
  const FEATURE_TO_POLYFILL_MAP = {
78
- // ES2015 (ES6)
79
74
  "Array.from": [
80
75
  /\bArray\.from\s*=/,
81
76
  /['"]core-js\/modules\/es\.array\.from['"]/,
77
+ /['"]core-js\/actual\/array\/from['"]/,
78
+ ],
79
+ "Array.of": [
80
+ /\bArray\.of\s*=/,
81
+ /['"]core-js\/modules\/es\.array\.of['"]/,
82
+ /['"]core-js\/actual\/array\/of['"]/,
82
83
  ],
83
- "Array.of": [/\bArray\.of\s*=/, /['"]core-js\/modules\/es\.array\.of['"]/],
84
84
  "Array.prototype.find": [
85
85
  /\bArray\.prototype\.find\s*=/,
86
86
  /['"]core-js\/modules\/es\.array\.find['"]/,
@@ -92,7 +92,7 @@ const FEATURE_TO_POLYFILL_MAP = {
92
92
  "Object.assign": [
93
93
  /\bObject\.assign\s*=/,
94
94
  /['"]core-js\/modules\/es\.object\.assign['"]/,
95
- /object-assign/,
95
+ /\bobject-assign\b/,
96
96
  ],
97
97
  Promise: [
98
98
  /\bPromise\s*=/,
@@ -117,13 +117,11 @@ const FEATURE_TO_POLYFILL_MAP = {
117
117
  WeakMap: [/\bWeakMap\s*=/, /['"]core-js\/modules\/es\.weak-map['"]/],
118
118
  WeakSet: [/\bWeakSet\s*=/, /['"]core-js\/modules\/es\.weak-set['"]/],
119
119
 
120
- // ES2016
121
120
  "Array.prototype.includes": [
122
121
  /\bArray\.prototype\.includes\s*=/,
123
122
  /['"]core-js\/modules\/es\.array\.includes['"]/,
124
123
  ],
125
124
 
126
- // ES2017
127
125
  "Object.values": [
128
126
  /\bObject\.values\s*=/,
129
127
  /['"]core-js\/modules\/es\.object\.values['"]/,
@@ -145,13 +143,11 @@ const FEATURE_TO_POLYFILL_MAP = {
145
143
  /['"]core-js\/modules\/es\.string\.pad-end['"]/,
146
144
  ],
147
145
 
148
- // ES2018
149
146
  "Promise.prototype.finally": [
150
147
  /\bPromise\.prototype\.finally\s*=/,
151
148
  /['"]core-js\/modules\/es\.promise\.finally['"]/,
152
149
  ],
153
150
 
154
- // ES2019
155
151
  "Array.prototype.flat": [
156
152
  /\bArray\.prototype\.flat\s*=/,
157
153
  /['"]core-js\/modules\/es\.array\.flat['"]/,
@@ -173,7 +169,6 @@ const FEATURE_TO_POLYFILL_MAP = {
173
169
  /['"]core-js\/modules\/es\.string\.trim-end['"]/,
174
170
  ],
175
171
 
176
- // ES2020
177
172
  "Promise.allSettled": [
178
173
  /\bPromise\.allSettled\s*=/,
179
174
  /['"]core-js\/modules\/es\.promise\.all-settled['"]/,
@@ -182,10 +177,9 @@ const FEATURE_TO_POLYFILL_MAP = {
182
177
  /\bString\.prototype\.matchAll\s*=/,
183
178
  /['"]core-js\/modules\/es\.string\.match-all['"]/,
184
179
  ],
185
- globalThis: [/globalThis\s*=/, /['"]core-js\/modules\/es\.global-this['"]/],
180
+ globalThis: [/\bglobalThis\s*=/, /['"]core-js\/modules\/es\.global-this['"]/],
186
181
  BigInt: [/\bBigInt\s*=/, /['"]core-js\/modules\/es\.bigint['"]/],
187
182
 
188
- // ES2021
189
183
  "Promise.any": [
190
184
  /\bPromise\.any\s*=/,
191
185
  /['"]core-js\/modules\/es\.promise\.any['"]/,
@@ -195,17 +189,17 @@ const FEATURE_TO_POLYFILL_MAP = {
195
189
  /['"]core-js\/modules\/es\.string\.replace-all['"]/,
196
190
  ],
197
191
 
198
- // ES2022
199
192
  "Array.prototype.at": [
200
193
  /\bArray\.prototype\.at\s*=/,
201
194
  /['"]core-js\/modules\/es\.array\.at['"]/,
195
+ /['"]core-js\/actual\/array\/at['"]/,
202
196
  ],
203
197
  "String.prototype.at": [
204
198
  /\bString\.prototype\.at\s*=/,
205
199
  /['"]core-js\/modules\/es\.string\.at['"]/,
200
+ /['"]core-js\/actual\/string\/at['"]/,
206
201
  ],
207
202
 
208
- // ES2023
209
203
  "Array.prototype.findLast": [
210
204
  /\bArray\.prototype\.findLast\s*=/,
211
205
  /['"]core-js\/modules\/es\.array\.find-last['"]/,
@@ -217,21 +211,33 @@ const FEATURE_TO_POLYFILL_MAP = {
217
211
  "Array.prototype.toReversed": [
218
212
  /\bArray\.prototype\.toReversed\s*=/,
219
213
  /['"]core-js\/modules\/es\.array\.to-reversed['"]/,
214
+ /['"]core-js\/actual\/array\/to-reversed['"]/,
215
+ /['"]core-js\/stable\/array\/to-reversed['"]/,
216
+ /(?:__webpack_require__\([^)]*core-js[^)]*to-reversed|core[-_]js[^/\n]*toReversed)/,
217
+ /\bcore[-_]js[-_]modules[-_][^/\n]*to[-_]reversed/,
220
218
  ],
221
219
  "Array.prototype.toSorted": [
222
220
  /\bArray\.prototype\.toSorted\s*=/,
223
221
  /['"]core-js\/modules\/es\.array\.to-sorted['"]/,
222
+ /['"]core-js\/actual\/array\/to-sorted['"]/,
223
+ /['"]core-js\/stable\/array\/to-sorted['"]/,
224
+ /__webpack_require__\([^)]*core-js\/modules\/es\.array\.to-sorted[^)]*\)/,
225
+ /\bcore[-_]js[-_]modules[-_][^/\n]*to[-_]sorted/,
224
226
  ],
225
227
  "Array.prototype.toSpliced": [
226
228
  /\bArray\.prototype\.toSpliced\s*=/,
227
229
  /['"]core-js\/modules\/es\.array\.to-spliced['"]/,
230
+ /['"]core-js\/actual\/array\/to-spliced['"]/,
228
231
  ],
229
232
  "Array.prototype.with": [
230
233
  /\bArray\.prototype\.with\s*=/,
231
234
  /['"]core-js\/modules\/es\.array\.with['"]/,
235
+ /['"]core-js\/actual\/array\/with['"]/,
236
+ /['"]core-js\/stable\/array\/with['"]/,
237
+ /(?:__webpack_require__\([^)]*core-js[^)]*array[^)]*with|core[-_]js[^/\n]*array[-_]with)/,
238
+ /\bcore[-_]js[-_]modules[-_][^/\n]*array[-_]with/,
232
239
  ],
233
240
 
234
- // ES2024
235
241
  "Object.hasOwn": [
236
242
  /\bObject\.hasOwn\s*=/,
237
243
  /['"]core-js\/modules\/es\.object\.has-own['"]/,
@@ -245,7 +251,6 @@ const FEATURE_TO_POLYFILL_MAP = {
245
251
  /['"]core-js\/modules\/es\.string\.to-well-formed['"]/,
246
252
  ],
247
253
 
248
- // ES2025
249
254
  "Array.prototype.group": [
250
255
  /\bArray\.prototype\.group\s*=/,
251
256
  /['"]core-js\/modules\/es\.array\.group['"]/,
@@ -258,6 +263,47 @@ const FEATURE_TO_POLYFILL_MAP = {
258
263
  /\bPromise\.try\s*=/,
259
264
  /['"]core-js\/modules\/es\.promise\.try['"]/,
260
265
  ],
266
+
267
+ "Array.fromAsync": [
268
+ /\bArray\.fromAsync\s*=/,
269
+ /['"]core-js\/modules\/es\.array\.from-async['"]/,
270
+ /['"]core-js\/actual\/array\/from-async['"]/,
271
+ ],
272
+ "Error.isError": [
273
+ /\bError\.isError\s*=/,
274
+ /['"]core-js\/modules\/es\.error\.is-error['"]/,
275
+ /['"]core-js\/actual\/error\/is-error['"]/,
276
+ ],
277
+ "Intl.DurationFormat": [
278
+ /\bIntl\.DurationFormat\s*=/,
279
+ /['"]core-js\/modules\/es\.intl\.duration-format['"]/,
280
+ /['"]@formatjs\/intl-durationformat['"]/,
281
+ ],
282
+
283
+ ArrayToSorted: [
284
+ /\bArray\.prototype\.toSorted\s*=/,
285
+ /['"]core-js\/modules\/es\.array\.to-sorted['"]/,
286
+ /['"]core-js\/actual\/array\/to-sorted['"]/,
287
+ /['"]core-js\/stable\/array\/to-sorted['"]/,
288
+ /__webpack_require__\([^)]*core-js\/modules\/es\.array\.to-sorted[^)]*\)/,
289
+ /\bcore[-_]js[-_]modules[-_]es[-_]array[-_]toSorted\b/,
290
+ ],
291
+ ArrayToReversed: [
292
+ /\bArray\.prototype\.toReversed\s*=/,
293
+ /['"]core-js\/modules\/es\.array\.to-reversed['"]/,
294
+ /['"]core-js\/actual\/array\/to-reversed['"]/,
295
+ /['"]core-js\/stable\/array\/to-reversed['"]/,
296
+ /(?:__webpack_require__\([^)]*core-js[^)]*to-reversed|core[-_]js[^/\n]*toReversed)/,
297
+ /\bcore[-_]js[-_]modules[-_][^/\n]*to[-_]reversed/,
298
+ ],
299
+ ArrayWith: [
300
+ /\bArray\.prototype\.with\s*=/,
301
+ /['"]core-js\/modules\/es\.array\.with['"]/,
302
+ /['"]core-js\/actual\/array\/with['"]/,
303
+ /['"]core-js\/stable\/array\/with['"]/,
304
+ /(?:__webpack_require__\([^)]*core-js[^)]*array[^)]*with|core[-_]js[-_]array[-_]with)/,
305
+ /\bcore[-_]js[-_]modules[-_][^/\n]*array[-_]with/,
306
+ ],
261
307
  };
262
308
 
263
309
  module.exports = {
@@ -0,0 +1,50 @@
1
+ const FEATURE_PARSER_REQUIREMENTS = {
2
+ SpreadInArrays: 6,
3
+ ObjectSpread: 9,
4
+ PrivateFields: 9,
5
+ BigInt: 9,
6
+ NumericSeparators: 10,
7
+ OptionalChaining: 11,
8
+ NullishCoalescing: 11,
9
+ LogicalAssignment: 12,
10
+ LogicalAssignmentOr: 12,
11
+ LogicalAssignmentNullish: 12,
12
+ TopLevelAwait: 12,
13
+ StaticInitializationBlocks: 13,
14
+ };
15
+
16
+ function getParserVersionForFeature(featureName, targetEsVersion) {
17
+ const requiredParser = FEATURE_PARSER_REQUIREMENTS[featureName];
18
+ if (!requiredParser) {
19
+ return targetEsVersion;
20
+ }
21
+
22
+ return Math.max(targetEsVersion, requiredParser);
23
+ }
24
+
25
+ function getMinimumParserVersion(
26
+ targetEsVersion,
27
+ enableFeatureDetection = false,
28
+ ) {
29
+ if (!enableFeatureDetection) {
30
+ return targetEsVersion;
31
+ }
32
+
33
+ let minParserVersion = targetEsVersion;
34
+
35
+ for (const [_feature, requiredParser] of Object.entries(
36
+ FEATURE_PARSER_REQUIREMENTS,
37
+ )) {
38
+ if (targetEsVersion >= requiredParser) {
39
+ minParserVersion = Math.max(minParserVersion, requiredParser);
40
+ }
41
+ }
42
+
43
+ return minParserVersion;
44
+ }
45
+
46
+ module.exports = {
47
+ FEATURE_PARSER_REQUIREMENTS,
48
+ getParserVersionForFeature,
49
+ getMinimumParserVersion,
50
+ };
@@ -0,0 +1,133 @@
1
+ const POLYFILLABLE_FEATURES = new Set([
2
+ "Array.from",
3
+ "Array.of",
4
+ "Array.prototype.find",
5
+ "Array.prototype.findIndex",
6
+ "Array.prototype.includes",
7
+ "Array.prototype.at",
8
+ "Array.prototype.findLast",
9
+ "Array.prototype.findLastIndex",
10
+ "Array.prototype.toSorted",
11
+ "Array.prototype.toReversed",
12
+ "Array.prototype.with",
13
+ "Array.prototype.flatMap",
14
+ "Array.prototype.flat",
15
+ "ArrayToSorted",
16
+ "ArrayToReversed",
17
+ "ArrayWith",
18
+ "ArrayFindLast",
19
+ "ArrayFindLastIndex",
20
+ "ArrayAt",
21
+ "String.prototype.includes",
22
+ "String.prototype.startsWith",
23
+ "String.prototype.endsWith",
24
+ "String.prototype.repeat",
25
+ "String.prototype.padStart",
26
+ "String.prototype.padEnd",
27
+ "String.prototype.trimStart",
28
+ "String.prototype.trimEnd",
29
+ "String.prototype.replaceAll",
30
+ "String.prototype.matchAll",
31
+ "String.prototype.at",
32
+ "StringReplaceAll",
33
+ "StringMatchAll",
34
+ "StringAt",
35
+ "Object.assign",
36
+ "Object.keys",
37
+ "Object.values",
38
+ "Object.entries",
39
+ "Object.fromEntries",
40
+ "Object.hasOwn",
41
+ "ObjectHasOwn",
42
+ "Promise.resolve",
43
+ "Promise.reject",
44
+ "Promise.all",
45
+ "Promise.race",
46
+ "Promise.allSettled",
47
+ "Promise.any",
48
+ "PromiseAny",
49
+ "PromiseAllSettled",
50
+ "Map",
51
+ "Set",
52
+ "WeakMap",
53
+ "WeakSet",
54
+ "Symbol",
55
+ "Symbol.iterator",
56
+ "Symbol.asyncIterator",
57
+ "globalThis",
58
+ "GlobalThis",
59
+ "Number.isNaN",
60
+ "Number.isFinite",
61
+ "Number.isInteger",
62
+ "Number.isSafeInteger",
63
+ "Number.parseFloat",
64
+ "Number.parseInt",
65
+ "Math.trunc",
66
+ "Math.sign",
67
+ "Math.cbrt",
68
+ "Math.hypot",
69
+ "RegExp.prototype.flags",
70
+ ]);
71
+
72
+ const CORE_JS_POLYFILLABLE = new Set([
73
+ "Array.from",
74
+ "Array.of",
75
+ "Array.prototype.find",
76
+ "Array.prototype.findIndex",
77
+ "Array.prototype.includes",
78
+ "Array.prototype.at",
79
+ "Array.prototype.findLast",
80
+ "Array.prototype.findLastIndex",
81
+ "Array.prototype.toSorted",
82
+ "Array.prototype.toReversed",
83
+ "Array.prototype.with",
84
+ "ArrayToSorted",
85
+ "ArrayToReversed",
86
+ "ArrayWith",
87
+ "ArrayFindLast",
88
+ "ArrayFindLastIndex",
89
+ "ArrayAt",
90
+ "String.prototype.includes",
91
+ "String.prototype.startsWith",
92
+ "String.prototype.endsWith",
93
+ "String.prototype.repeat",
94
+ "String.prototype.padStart",
95
+ "String.prototype.padEnd",
96
+ "String.prototype.trimStart",
97
+ "String.prototype.trimEnd",
98
+ "String.prototype.replaceAll",
99
+ "String.prototype.matchAll",
100
+ "String.prototype.at",
101
+ "StringReplaceAll",
102
+ "StringMatchAll",
103
+ "StringAt",
104
+ "Object.assign",
105
+ "Object.keys",
106
+ "Object.values",
107
+ "Object.entries",
108
+ "Object.fromEntries",
109
+ "Object.hasOwn",
110
+ "ObjectHasOwn",
111
+ "Promise.resolve",
112
+ "Promise.reject",
113
+ "Promise.all",
114
+ "Promise.race",
115
+ "Promise.allSettled",
116
+ "Promise.any",
117
+ "PromiseAny",
118
+ "PromiseAllSettled",
119
+ "Map",
120
+ "Set",
121
+ "WeakMap",
122
+ "WeakSet",
123
+ "Symbol",
124
+ "Symbol.iterator",
125
+ "Symbol.asyncIterator",
126
+ "globalThis",
127
+ "GlobalThis",
128
+ ]);
129
+
130
+ module.exports = {
131
+ POLYFILLABLE_FEATURES,
132
+ CORE_JS_POLYFILLABLE,
133
+ };
@@ -13,6 +13,7 @@ const ECMA_VERSION_MAP = {
13
13
  14: "es2023",
14
14
  15: "es2024",
15
15
  16: "es2025",
16
+ 17: "es2026",
16
17
  2015: "es2015",
17
18
  2016: "es2016",
18
19
  2017: "es2017",
@@ -24,6 +25,7 @@ const ECMA_VERSION_MAP = {
24
25
  2023: "es2023",
25
26
  2024: "es2024",
26
27
  2025: "es2025",
28
+ 2026: "es2026",
27
29
  es3: "es5",
28
30
  es4: "es5",
29
31
  es5: "es5",
@@ -38,6 +40,7 @@ const ECMA_VERSION_MAP = {
38
40
  es14: "es2023",
39
41
  es15: "es2024",
40
42
  es16: "es2025",
43
+ es17: "es2026",
41
44
  es2015: "es2015",
42
45
  es2016: "es2016",
43
46
  es2017: "es2017",
@@ -49,6 +52,7 @@ const ECMA_VERSION_MAP = {
49
52
  es2023: "es2023",
50
53
  es2024: "es2024",
51
54
  es2025: "es2025",
55
+ es2026: "es2026",
52
56
  };
53
57
 
54
58
  const ECMA_VERSION_TO_NUMBER = {
@@ -64,6 +68,7 @@ const ECMA_VERSION_TO_NUMBER = {
64
68
  es2023: 14,
65
69
  es2024: 15,
66
70
  es2025: 16,
71
+ es2026: 17,
67
72
  };
68
73
 
69
74
  const BROWSER_TO_ES_VERSION = {
@@ -82,6 +87,7 @@ const BROWSER_TO_ES_VERSION = {
82
87
  97: 14,
83
88
  117: 15,
84
89
  136: 16,
90
+ 150: 17,
85
91
  },
86
92
  firefox: {
87
93
  52: 6,
@@ -95,6 +101,7 @@ const BROWSER_TO_ES_VERSION = {
95
101
  104: 14,
96
102
  119: 15,
97
103
  134: 16,
104
+ 145: 17,
98
105
  },
99
106
  chrome: {
100
107
  51: 6,
@@ -108,6 +115,7 @@ const BROWSER_TO_ES_VERSION = {
108
115
  97: 14,
109
116
  117: 15,
110
117
  136: 16,
118
+ 150: 17,
111
119
  },
112
120
  safari: {
113
121
  10: 6,
@@ -121,6 +129,7 @@ const BROWSER_TO_ES_VERSION = {
121
129
  15.4: 14,
122
130
  17: 15,
123
131
  18.2: 16,
132
+ 19.1: 17,
124
133
  },
125
134
  opera: {
126
135
  38: 6,
@@ -134,6 +143,7 @@ const BROWSER_TO_ES_VERSION = {
134
143
  83: 14,
135
144
  103: 15,
136
145
  121: 16,
146
+ 135: 17,
137
147
  },
138
148
  ios_saf: {
139
149
  10: 6,
@@ -147,6 +157,7 @@ const BROWSER_TO_ES_VERSION = {
147
157
  15.4: 14,
148
158
  17: 15,
149
159
  18.2: 16,
160
+ 19.1: 17,
150
161
  },
151
162
  android: {
152
163
  67: 6,
@@ -160,6 +171,7 @@ const BROWSER_TO_ES_VERSION = {
160
171
  97: 14,
161
172
  117: 15,
162
173
  136: 16,
174
+ 150: 17,
163
175
  },
164
176
  };
165
177
 
@@ -189,6 +201,8 @@ const JS_VERSIONS = [
189
201
  "es2024",
190
202
  "es16",
191
203
  "es2025",
204
+ "es17",
205
+ "es2026",
192
206
  "checkBrowser",
193
207
  ];
194
208
 
@@ -205,12 +219,16 @@ const VERSION_ORDER = [
205
219
  "es2023",
206
220
  "es2024",
207
221
  "es2025",
222
+ "es2026",
208
223
  ];
209
224
 
225
+ const LATEST_PARSER_VERSION = 2025;
226
+
210
227
  module.exports = {
211
228
  ECMA_VERSION_MAP,
212
229
  ECMA_VERSION_TO_NUMBER,
213
230
  BROWSER_TO_ES_VERSION,
214
231
  JS_VERSIONS,
215
232
  VERSION_ORDER,
233
+ LATEST_PARSER_VERSION,
216
234
  };
@@ -4,8 +4,34 @@ const {
4
4
  IMPORT_PATTERNS,
5
5
  FEATURE_TO_POLYFILL_MAP,
6
6
  } = require("./constants");
7
+ const {
8
+ POLYFILLABLE_FEATURES,
9
+ CORE_JS_POLYFILLABLE,
10
+ } = require("./constants/polyfillableFeatures");
7
11
  const { detectFeaturesFromAST } = require("./helpers/astDetector");
8
12
 
13
+ function getPolyfillableFeatures(library) {
14
+ if (!library) {
15
+ return POLYFILLABLE_FEATURES;
16
+ }
17
+
18
+ let normalizedLibrary;
19
+ try {
20
+ normalizedLibrary =
21
+ typeof library === "string"
22
+ ? library.toLowerCase()
23
+ : String(library).toLowerCase();
24
+ } catch {
25
+ return POLYFILLABLE_FEATURES;
26
+ }
27
+
28
+ if (normalizedLibrary === "core-js") {
29
+ return CORE_JS_POLYFILLABLE;
30
+ }
31
+
32
+ return POLYFILLABLE_FEATURES;
33
+ }
34
+
9
35
  const detectPolyfills = (
10
36
  code,
11
37
  polyfills,
@@ -19,7 +45,10 @@ const detectPolyfills = (
19
45
  if (pattern.test(code)) polyfills.add(feature);
20
46
  }
21
47
 
22
- if (code.includes("import") && code.includes("core-js")) {
48
+ const hasCoreJsModuleRef =
49
+ (code.includes("import") || code.includes("require")) &&
50
+ code.includes("core-js");
51
+ if (hasCoreJsModuleRef) {
23
52
  for (const { pattern, feature } of importPatterns) {
24
53
  if (pattern.test(code)) polyfills.add(feature);
25
54
  }
@@ -82,9 +111,6 @@ function detectPolyfillsForFeatures(
82
111
  const polyfills = new Set();
83
112
  if (!code || !featureMap) return polyfills;
84
113
 
85
- if (logger?.isLevelEnabled?.("debug")) {
86
- }
87
-
88
114
  const polyfillFeatures = Object.entries(featureMap);
89
115
  polyfillFeatures.forEach(([feature, patterns]) => {
90
116
  const isPolyfilled = patterns.some((pattern) => pattern.test(code));
@@ -109,6 +135,18 @@ const filterPolyfilled = (unsupportedFeatures, polyfills) => {
109
135
  return unsupportedFeatures.filter((feature) => !polyfills.has(feature));
110
136
  };
111
137
 
138
+ const filterPolyfillableFeatures = (
139
+ unsupportedFeatures,
140
+ ignorePolyfillable,
141
+ ) => {
142
+ if (!ignorePolyfillable) return unsupportedFeatures;
143
+
144
+ const polyfillableSet = getPolyfillableFeatures(ignorePolyfillable);
145
+ return unsupportedFeatures.filter((feature) => !polyfillableSet.has(feature));
146
+ };
147
+
112
148
  module.exports = detectFeatures;
113
149
  module.exports.detectPolyfills = detectPolyfillsForFeatures;
114
150
  module.exports.filterPolyfilled = filterPolyfilled;
151
+ module.exports.filterPolyfillableFeatures = filterPolyfillableFeatures;
152
+ module.exports.getPolyfillableFeatures = getPolyfillableFeatures;
@@ -14,21 +14,25 @@ function hasOptionsCause(node) {
14
14
  }
15
15
 
16
16
  function checkMap(node, astInfo) {
17
+ const callee = node.callee;
18
+ const calleeObject = callee?.object;
19
+ const calleeProperty = callee?.property;
20
+ const calleeObjectName = calleeObject?.name;
21
+ const calleePropertyName = calleeProperty?.name;
22
+
17
23
  const hasKindMismatch = astInfo.kind && node.kind !== astInfo.kind;
18
24
  const hasOperatorMismatch =
19
25
  astInfo.operator && node.operator !== astInfo.operator;
20
26
  const hasCalleeMismatch =
21
- astInfo.callee && (!node.callee || node.callee.name !== astInfo.callee);
27
+ astInfo.callee && (!callee || callee.name !== astInfo.callee);
22
28
 
23
- const hasCalleeObject = node.callee?.object?.name === astInfo.object;
29
+ const hasCalleeObject = calleeObjectName === astInfo.object;
24
30
  const hasCalleeIdentifier =
25
- node.callee?.type === "Identifier" && node.callee?.name === astInfo.object;
31
+ callee?.type === "Identifier" && callee?.name === astInfo.object;
26
32
  const hasPropertyMismatch =
27
- astInfo.property && node.callee?.property?.name !== astInfo.property;
28
- const hasPropertyMatch = node.callee?.property?.name === astInfo.property;
29
- const shouldExclude = astInfo.excludeObjects?.includes(
30
- node.callee?.object?.name,
31
- );
33
+ astInfo.property && calleePropertyName !== astInfo.property;
34
+ const hasPropertyMatch = calleePropertyName === astInfo.property;
35
+ const shouldExclude = astInfo.excludeObjects?.includes(calleeObjectName);
32
36
 
33
37
  const needsObjectAndProperty = astInfo.object && astInfo.property;
34
38
  const hasObjectAndPropertyMismatch =
@@ -52,6 +56,10 @@ function checkMap(node, astInfo) {
52
56
  const hasOptionsCauseMismatch =
53
57
  astInfo.hasOptionsCause && !hasOptionsCause(node);
54
58
 
59
+ const callerType = calleeObject?.type;
60
+ const hasExcludedCallerType =
61
+ astInfo.excludeCallerTypes?.includes(callerType);
62
+
55
63
  const hasMismatch =
56
64
  hasKindMismatch ||
57
65
  hasOperatorMismatch ||
@@ -61,7 +69,8 @@ function checkMap(node, astInfo) {
61
69
  hasPropertyWithoutMatch ||
62
70
  hasPropertyWithExclusion ||
63
71
  hasObjectAndPropertyMismatch ||
64
- hasOptionsCauseMismatch;
72
+ hasOptionsCauseMismatch ||
73
+ hasExcludedCallerType;
65
74
 
66
75
  if (hasMismatch) return false;
67
76
 
@@ -41,6 +41,7 @@ const CHILD_KEYS = [
41
41
  "key",
42
42
  "value",
43
43
  "superClass",
44
+ "expressions",
44
45
  ];
45
46
 
46
47
  function normalizeNodeType(nodeType) {
@@ -108,6 +109,10 @@ function matchesFeature(node, astInfo, context = {}) {
108
109
  return node.left?.type === "PrivateIdentifier";
109
110
  }
110
111
 
112
+ if (astInfo.noParam) {
113
+ return node.type === "CatchClause" && node.param == null;
114
+ }
115
+
111
116
  if (node.type === "CallExpression" || node.type === "NewExpression") {
112
117
  return checkMap(node, astInfo);
113
118
  }
@@ -22,6 +22,8 @@ module.exports = Object.assign(
22
22
  polyfillDetector: {
23
23
  detectPolyfills: detectFeaturesModule.detectPolyfills,
24
24
  filterPolyfilled: detectFeaturesModule.filterPolyfilled,
25
+ filterPolyfillableFeatures:
26
+ detectFeaturesModule.filterPolyfillableFeatures,
25
27
  },
26
28
  },
27
29
  );
@@ -1,11 +1,134 @@
1
- function parseCode(code, acornOpts, acorn, file) {
1
+ function detectRuntime() {
2
+ if (typeof Deno !== "undefined") {
3
+ return "deno";
4
+ }
5
+ if (typeof Bun !== "undefined") {
6
+ return "bun";
7
+ }
8
+ return "node";
9
+ }
10
+
11
+ function stripTypesInNode(code) {
12
+ const moduleAPI = require("module");
13
+ if (typeof moduleAPI.stripTypeScriptTypes === "function") {
14
+ return moduleAPI.stripTypeScriptTypes(code, { mode: "strip" });
15
+ }
16
+ throw new Error(
17
+ "es-check execution on TypeScript files requires Node.js v22.13.0+",
18
+ );
19
+ }
20
+
21
+ function buildLineOffsets(original, stripped) {
22
+ const originalLines = original.split("\n");
23
+ const strippedLines = stripped.split("\n");
24
+ const lineCount = Math.max(originalLines.length, strippedLines.length);
25
+
26
+ const offsets = [];
27
+ for (let i = 0; i < lineCount; i++) {
28
+ const origLen = (originalLines[i] || "").length;
29
+ const stripLen = (strippedLines[i] || "").length;
30
+ offsets.push(origLen - stripLen);
31
+ }
32
+ return offsets;
33
+ }
34
+
35
+ function mapPosition(line, column, lineOffsets) {
36
+ const hasNoOffsets = !lineOffsets || lineOffsets.length === 0;
37
+ if (hasNoOffsets) return { line, column };
38
+
39
+ const lineIndex = line - 1;
40
+ const isBelowRange = lineIndex < 0;
41
+ const isAboveRange = lineIndex >= lineOffsets.length;
42
+ const isOutOfBounds = isBelowRange || isAboveRange;
43
+ if (isOutOfBounds) return { line, column };
44
+
45
+ const offset = lineOffsets[lineIndex];
46
+ const adjustedColumn = column + offset;
47
+ return { line, column: adjustedColumn };
48
+ }
49
+
50
+ function stripTypesInBun(code) {
51
+ if (typeof Bun !== "undefined" && typeof Bun.Transpiler !== "undefined") {
52
+ const transpiler = new Bun.Transpiler({ loader: "ts" });
53
+ const stripped = transpiler.transformSync(code);
54
+ const lineOffsets = buildLineOffsets(code, stripped);
55
+ return { code: stripped, lineOffsets };
56
+ }
57
+ throw new Error(
58
+ "es-check execution on TypeScript files requires Bun v1.0.0+",
59
+ );
60
+ }
61
+
62
+ function stripTypesInDeno() {
63
+ throw new Error(
64
+ "es-check execution on TypeScript files is not supported in Deno (Deno has native TypeScript support)",
65
+ );
66
+ }
67
+
68
+ function stripTypeScript(code) {
69
+ const isInvalidCode = !code || typeof code !== "string";
70
+ if (isInvalidCode) {
71
+ return { code, lineOffsets: null };
72
+ }
73
+
74
+ const runtime = detectRuntime();
75
+ const isNodeRuntime = runtime === "node";
76
+ const isBunRuntime = runtime === "bun";
77
+ const isDenoRuntime = runtime === "deno";
78
+
79
+ if (isNodeRuntime) {
80
+ return { code: stripTypesInNode(code), lineOffsets: null };
81
+ }
82
+
83
+ if (isBunRuntime) {
84
+ return stripTypesInBun(code);
85
+ }
86
+
87
+ if (isDenoRuntime) {
88
+ return stripTypesInDeno();
89
+ }
90
+
91
+ throw new Error(
92
+ "es-check execution on TypeScript files is not supported in this runtime",
93
+ );
94
+ }
95
+
96
+ function parseCode(code, acornOpts, acorn, file, options = {}) {
97
+ let processedCode = code;
98
+ let lineOffsets = null;
99
+
100
+ const hasTypeScriptOption = options.typescript || options.ts;
101
+ const hasTsExtension = file?.endsWith(".ts");
102
+ const hasTsxExtension = file?.endsWith(".tsx");
103
+ const isTypeScriptFile = hasTsExtension || hasTsxExtension;
104
+ const shouldProcessTypeScript = hasTypeScriptOption && isTypeScriptFile;
105
+
106
+ if (shouldProcessTypeScript) {
107
+ const result = stripTypeScript(code);
108
+ processedCode = result.code;
109
+ lineOffsets = result.lineOffsets;
110
+ }
111
+
2
112
  try {
3
- const ast = acorn.parse(code, acornOpts);
113
+ const ast = acorn.parse(processedCode, acornOpts);
4
114
  return { ast, error: null };
5
115
  } catch (err) {
6
- const hasLocation = err.loc && err.loc.line && err.loc.column;
7
- const line = hasLocation ? err.loc.line : null;
8
- const column = hasLocation ? err.loc.column : null;
116
+ const errorLocation = err.loc;
117
+ const hasValidLocation = errorLocation?.line && errorLocation?.column;
118
+ const hasLineOffsets = lineOffsets !== null;
119
+ const shouldMapPosition = hasValidLocation && hasLineOffsets;
120
+
121
+ const mappedPosition = shouldMapPosition
122
+ ? mapPosition(errorLocation.line, errorLocation.column, lineOffsets)
123
+ : null;
124
+
125
+ let line = null;
126
+ let column = null;
127
+
128
+ if (hasValidLocation) {
129
+ line = mappedPosition?.line ?? errorLocation.line;
130
+ column = mappedPosition?.column ?? errorLocation.column;
131
+ }
9
132
 
10
133
  return {
11
134
  ast: null,
@@ -22,4 +145,11 @@ function parseCode(code, acornOpts, acorn, file) {
22
145
 
23
146
  module.exports = {
24
147
  parseCode,
148
+ stripTypeScript,
149
+ detectRuntime,
150
+ stripTypesInNode,
151
+ stripTypesInBun,
152
+ stripTypesInDeno,
153
+ buildLineOffsets,
154
+ mapPosition,
25
155
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-check",
3
- "version": "9.5.4",
3
+ "version": "9.6.0-1",
4
4
  "description": "Checks the ECMAScript version of .js glob against a specified version of ECMAScript with a shell command",
5
5
  "main": "lib/index.js",
6
6
  "license": "MIT",
@@ -27,14 +27,18 @@
27
27
  "prepare": "node scripts/install-hooks.mjs",
28
28
  "prepublishOnly": "pnpm test",
29
29
  "release": "release-it --no-git.requireUpstream",
30
- "report:coverage": "mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info tests/unit/*.test.js tests/unit/helpers/*.test.js tests/unit/helpers/**/*.test.js tests/unit/constants/*.test.js tests/unit/cli/*.test.js tests/unit/check-runner/*.test.js --test-timeout=10000",
31
- "test": "node --test tests/unit/*.test.js tests/unit/helpers/*.test.js tests/unit/helpers/**/*.test.js tests/unit/constants/*.test.js tests/unit/cli/*.test.js tests/unit/check-runner/*.test.js --test-timeout=10000 && npm run test:e2e",
30
+ "report:coverage": "mkdir -p coverage && node tests/scripts/test-runner.js unit --coverage --reporter=lcov",
31
+ "test": "node tests/scripts/test-runner.js unit && npm run test:e2e",
32
+ "test:unit": "node tests/scripts/test-runner.js unit",
33
+ "test:unit:verbose": "node tests/scripts/test-runner.js unit --verbose",
32
34
  "test:e2e": "npm run test:e2e:cli && npm run test:e2e:esm && npm run test:e2e:package-files && npm run test:e2e:sourcemap",
35
+ "test:e2e:typescript": "tests/e2e/typescript/run-tests.sh",
33
36
  "test:e2e:cli": "node tests/e2e/test-cli.js",
34
37
  "test:e2e:esm": "node tests/e2e/test-esm-import.mjs",
35
38
  "test:e2e:package-files": "node tests/e2e/test-package-files.mjs",
36
39
  "test:e2e:sourcemap": "node --test tests/e2e/test-sourcemap.js --test-timeout=10000",
37
- "update": "codependence --update && pastoralist",
40
+ "generate-parser-requirements": "node scripts/generate-parser-requirements.js",
41
+ "update": "codependence --update && pastoralist && pnpm run generate-parser-requirements",
38
42
  "benchmark": "./tests/benchmarks/run-benchmarks.sh",
39
43
  "site:dev": "pnpm --filter es-check-docs run dev",
40
44
  "site:build": "pnpm --filter es-check-docs run build",
@@ -52,15 +56,16 @@
52
56
  "homepage": "https://github.com/yowainwright/es-check#readme",
53
57
  "website": "https://jeffry.in/es-check",
54
58
  "devDependencies": {
55
- "codependence": "1.0.0-1",
56
- "oxlint": "^1.29.0",
59
+ "@mdn/browser-compat-data": "^7.3.3",
60
+ "browserslist": "^4.28.1",
61
+ "codependence": "0.3.1",
62
+ "oxlint": "^1.49.0",
57
63
  "pastoralist": "1.10.0-1",
58
- "prettier": "3.7.4",
59
- "release-it": "19.2.3"
64
+ "prettier": "3.8.1",
65
+ "release-it": "19.2.4"
60
66
  },
61
67
  "dependencies": {
62
- "acorn": "8.15.0",
63
- "browserslist": "^4.28.0",
68
+ "acorn": "8.16.0",
64
69
  "fast-glob": "^3.3.3"
65
70
  },
66
71
  "engines": {
@@ -109,7 +114,7 @@
109
114
  "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
110
115
  }
111
116
  },
112
- "packageManager": "pnpm@10.27.0",
117
+ "packageManager": "pnpm@10.30.0",
113
118
  "pastoralist": {
114
119
  "appendix": {
115
120
  "vite@6.4.1": {
@@ -159,6 +164,50 @@
159
164
  "severity": "low",
160
165
  "url": "https://github.com/yowainwright/es-check/security/dependabot/57"
161
166
  }
167
+ },
168
+ "devalue@5.6.3": {
169
+ "dependents": {
170
+ "es-check": "devalue (transitive dependency)"
171
+ },
172
+ "ledger": {
173
+ "addedDate": "2026-02-21T00:23:12.264Z",
174
+ "reason": "Security fix: devalue `uneval`ed code can create objects with polluted prototypes when `eval`ed (low)",
175
+ "securityChecked": true,
176
+ "securityCheckDate": "2026-02-21T00:23:12.264Z",
177
+ "securityProvider": "github",
178
+ "severity": "low",
179
+ "url": "https://github.com/yowainwright/es-check/security/dependabot/66"
180
+ }
181
+ },
182
+ "lodash@4.17.23": {
183
+ "dependents": {
184
+ "es-check": "lodash (transitive dependency)"
185
+ },
186
+ "ledger": {
187
+ "addedDate": "2026-02-21T00:23:12.264Z",
188
+ "reason": "Security fix: Lodash has Prototype Pollution Vulnerability in `_.unset` and `_.omit` functions (medium)",
189
+ "securityChecked": true,
190
+ "securityCheckDate": "2026-02-21T00:23:12.264Z",
191
+ "securityProvider": "github",
192
+ "cve": "CVE-2025-13465",
193
+ "severity": "medium",
194
+ "url": "https://github.com/yowainwright/es-check/security/dependabot/63"
195
+ }
196
+ },
197
+ "diff@5.2.2": {
198
+ "dependents": {
199
+ "es-check": "diff (transitive dependency)"
200
+ },
201
+ "ledger": {
202
+ "addedDate": "2026-02-21T00:23:12.264Z",
203
+ "reason": "Security fix: jsdiff has a Denial of Service vulnerability in parsePatch and applyPatch (low)",
204
+ "securityChecked": true,
205
+ "securityCheckDate": "2026-02-21T00:23:12.264Z",
206
+ "securityProvider": "github",
207
+ "cve": "CVE-2026-24001",
208
+ "severity": "low",
209
+ "url": "https://github.com/yowainwright/es-check/security/dependabot/61"
210
+ }
162
211
  }
163
212
  },
164
213
  "depPaths": [
@@ -174,13 +223,17 @@
174
223
  "hasWorkspaceSecurityChecks": true
175
224
  }
176
225
  },
177
- "pnpm": {
178
- "overrides": {
179
- "vite": "6.4.1",
180
- "tmp": "0.2.4",
181
- "js-yaml": "4.1.1",
182
- "mdast-util-to-hast": "13.2.1",
183
- "undici": "6.23.0"
184
- }
185
- }
226
+ "overrides": {
227
+ "vite": "6.4.1",
228
+ "tmp": "0.2.4",
229
+ "js-yaml": "4.1.1",
230
+ "mdast-util-to-hast": "13.2.1",
231
+ "undici": "6.23.0",
232
+ "devalue": "5.6.3",
233
+ "lodash": "4.17.23",
234
+ "diff": "5.2.2"
235
+ },
236
+ "workspaces": [
237
+ "site"
238
+ ]
186
239
  }