es-check 9.1.5-0 → 9.2.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
@@ -99,16 +99,12 @@ The images below demonstrate command line scripts and their corresponding logged
99
99
 
100
100
  ![fail](https://user-images.githubusercontent.com/1074042/31471486-d65c3a80-ae9d-11e7-94fd-68b7acdb2d89.jpg)
101
101
 
102
- **ES Check** is run above with node commands. It can also be run within npm scripts, ci tools, or testing suites.
102
+ **ES Check** is run above with node commands. It can also be run within npm scripts, ci tools, or testing suites. It also provide minimal support for use in node apps.
103
103
 
104
104
  ---
105
105
 
106
106
  ## API
107
107
 
108
- **ES Check** provides the necessities. It accepts its place as a JavaScript matcher/tester.
109
-
110
- ### General Information
111
-
112
108
  ```sh
113
109
 
114
110
  # USAGE
@@ -156,6 +152,7 @@ Here's a comprehensive list of all available options:
156
152
  | `--browserslistPath <path>` | Path to custom browserslist configuration (default: uses standard browserslist config resolution) |
157
153
  | `--browserslistEnv <env>` | Browserslist environment to use (default: production) |
158
154
  | `--config <path>` | Path to custom .escheckrc config file |
155
+ | `--batchSize <number>` | Number of files to process concurrently (0 for unlimited, default: 0) |
159
156
  | `-h, --help` | Display help for command |
160
157
 
161
158
  ### Shell Completion
@@ -275,7 +272,7 @@ es-check checkBrowser ./dist/**/*.js
275
272
 
276
273
  ## Usage
277
274
 
278
- ES Check is a shell command CLI. It is run in [shell tool](http://linuxcommand.org/lc3_learning_the_shell.php) like Terminal, ITerm, or Hyper. It takes in two arguments: an [ECMAScript version](https://www.w3schools.com/js/js_versions.asp) (`<ECMAScript version>`) and files (`[files]`) in [globs](http://searchsecurity.techtarget.com/definition/globbing).
275
+ ES Check is mainly a shell command CLI. It is run in [shell tool](http://linuxcommand.org/lc3_learning_the_shell.php) like Terminal, ITerm, or Hyper. It takes in two arguments: an [ECMAScript version](https://www.w3schools.com/js/js_versions.asp) (`<ECMAScript version>`) and files (`[files]`) in [globs](http://searchsecurity.techtarget.com/definition/globbing).
279
276
 
280
277
  Here are some example of **es check** scripts that could be run:
281
278
 
@@ -287,6 +284,36 @@ es-check es6 ./js/*.js
287
284
  es-check es6 ./js/*.js ./dist/*.js
288
285
  ```
289
286
 
287
+ ### Using ES Check in Node
288
+
289
+ In addition to its CLI utility, ES Check can be used programmatically in Node.js applications:
290
+
291
+ ```javascript
292
+ const { runChecks, loadConfig } = require('es-check');
293
+
294
+ async function checkMyFiles() {
295
+ const config = {
296
+ ecmaVersion: 'es5',
297
+ files: ['dist/**/*.js'],
298
+ module: false,
299
+ checkFeatures: true
300
+ };
301
+
302
+ try {
303
+ await runChecks([config], logger);
304
+ console.log('All files passed ES5 check!');
305
+ } catch (error) {
306
+ console.error('Some files failed the ES check');
307
+ process.exit(1);
308
+ }
309
+ }
310
+
311
+ async function checkWithConfig() {
312
+ const configs = await loadConfig('./.escheckrc');
313
+ await runChecks(configs, logger);
314
+ }
315
+ ```
316
+
290
317
  ---
291
318
 
292
319
  ## Configuration
@@ -496,6 +523,40 @@ es-check --checkBrowser --checkFeatures ./dist/**/*.js
496
523
 
497
524
  ---
498
525
 
526
+ ## Performance Optimization
527
+
528
+ ES Check provides the `--batchSize` option to optimize performance for different scenarios:
529
+
530
+ ```sh
531
+ # Process all files in parallel (default)
532
+ es-check es5 './dist/**/*.js' --batchSize 0
533
+
534
+ # Process 10 files at a time (memory-constrained environments)
535
+ es-check es5 './dist/**/*.js' --batchSize 10
536
+
537
+ # Process 50 files at a time (balanced approach)
538
+ es-check es5 './dist/**/*.js' --batchSize 50
539
+ ```
540
+
541
+ ### Performance Guidelines
542
+
543
+ | Scenario | Recommended `--batchSize` | Reason |
544
+ |----------|---------------------------|---------|
545
+ | Small codebases (< 100 files) | `0` (unlimited) | Maximum parallelism for fastest results |
546
+ | Medium codebases (100-500 files) | `0` or `50` | Balance between speed and memory |
547
+ | Large codebases (> 500 files) | `50-100` | Prevent memory spikes |
548
+ | CI/CD with limited memory | `10-20` | Conservative memory usage |
549
+ | Local development | `0` (default) | Utilize available hardware |
550
+
551
+ ### Recent Performance Improvements
552
+
553
+ As of August 2025, ES Check has been optimized with:
554
+ - **Single-parse optimization**: Files are parsed once and the AST is reused
555
+ - **Async file processing**: Non-blocking I/O for better performance
556
+ - **Configurable batch processing**: Fine-tune based on your needs
557
+
558
+ ---
559
+
499
560
  ## Checking node_modules Dependencies
500
561
 
501
562
  To check node_modules dependencies for ES compatibility:
@@ -514,13 +575,13 @@ A simple example script is available in `examples/check-node-modules.js`.
514
575
 
515
576
  ## Acknowledgements
516
577
 
517
- ES Check is a small utility using powerful tools that [Isaac Z. Schlueter](https://github.com/isaacs), [Marijn Haverbeke](https://github.com/marijnh), and [Matthias Etienne](https://github.com/mattallty) built. [ES Checker](https://github.com/ruanyf/es-checker) by [Ruan YiFeng](https://github.com/ruanyf) checks the JavaScript version supported within a [browser](http://ruanyf.github.io/es-checker/) at run time. ES Check offers similar feedback to ES Checker but at build time and is specific to the product that is using it. ES Check was started after reading this [post](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) about [deploying es2015 code to production today] by [Philip Walton](https://github.com/philipwalton).
578
+ ES Check is a small utility using powerful tools that [Isaac Z. Schlueter](https://github.com/isaacs), [Marijn Haverbeke](https://github.com/marijnh), and [Matthias Etienne](https://github.com/mattallty) built. [ES Checker](https://github.com/ruanyf/es-checker) by [Ruan YiFeng](https://github.com/ruanyf) checks the JavaScript version supported within a [browser](http://ruanyf.github.io/es-checker/) at run time. ES Check offers similar feedback to ES Checker but at build time and is specific to the product that is using it. ES Check was started after reading [Philip Walton](https://github.com/philipwalton)'s post about [deploying es2015 code to production today](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/).
518
579
 
519
580
  ---
520
581
 
521
582
  ## Contributing
522
583
 
523
- ES Check has 7 dependencies: [acorn and acorn-walk](https://github.com/ternjs/acorn/), [fast-glob](https://github.com/mrmlnc/fast-glob), [supports-color](github.com/chalk/supports-color), [winston](https://github.com/winstonjs/winston), [browserslist](https://github.com/browserslist/browserslist), and [commander](https://github.com/tj/commander). To contribute, file an [issue](https://github.com/yowainwright/es-check/issues) or submit a pull request.
584
+ ES Check has 7 dependencies: [acorn and acorn-walk](https://github.com/ternjs/acorn/), [fast-glob](https://github.com/mrmlnc/fast-glob), [supports-color](https://github.com/chalk/supports-color), [winston](https://github.com/winstonjs/winston), [browserslist](https://github.com/browserslist/browserslist), and [commander](https://github.com/tj/commander). To contribute, file an [issue](https://github.com/yowainwright/es-check/issues) or submit a pull request.
524
585
 
525
586
  To update es versions, check out these lines of code [here](https://github.com/yowainwright/es-check/blob/main/index.js#L92-L153) and [here (in acorn.js)](https://github.com/acornjs/acorn/blob/3221fa54f9dea30338228b97210c4f1fd332652d/acorn/src/acorn.d.ts#L586).
526
587
 
package/detectFeatures.js CHANGED
@@ -29,12 +29,12 @@ const detectPolyfills = (
29
29
  }
30
30
 
31
31
  const detectFeatures = (code, ecmaVersion, sourceType, ignoreList = new Set(), options = {}) => {
32
- const { checkForPolyfills } = options;
32
+ const { checkForPolyfills, ast: providedAst } = options;
33
33
 
34
34
  const polyfills = new Set();
35
35
  if (checkForPolyfills) detectPolyfills(code, polyfills);
36
36
 
37
- const ast = acorn.parse(code, {
37
+ const ast = providedAst || acorn.parse(code, {
38
38
  ecmaVersion: 'latest',
39
39
  sourceType,
40
40
  });
package/index.js CHANGED
@@ -11,7 +11,7 @@ let polyfillDetector = null;
11
11
  const pkg = require('./package.json')
12
12
  const { lilconfig } = require('lilconfig');
13
13
  const { JS_VERSIONS } = require('./constants');
14
- const { parseIgnoreList, createLogger, generateBashCompletion, generateZshCompletion } = require('./utils');
14
+ const { parseIgnoreList, createLogger, generateBashCompletion, generateZshCompletion, processBatchedFiles, readFileAsync, parseCode } = require('./utils');
15
15
 
16
16
  program.configureOutput({
17
17
  writeOut: (str) => process.stdout.write(str),
@@ -98,6 +98,7 @@ program
98
98
  .option('--browserslistPath <path>', 'path to custom browserslist configuration')
99
99
  .option('--browserslistEnv <env>', 'browserslist environment to use')
100
100
  .option('--config <path>', 'path to custom .escheckrc config file')
101
+ .option('--batchSize <number>', 'number of files to process concurrently (0 for unlimited)', '0')
101
102
 
102
103
  async function loadConfig(customConfigPath) {
103
104
  const logger = createLogger();
@@ -178,6 +179,7 @@ program
178
179
  browserslistQuery: options.browserslistQuery !== undefined ? options.browserslistQuery : baseConfig.browserslistQuery,
179
180
  browserslistPath: options.browserslistPath !== undefined ? options.browserslistPath : baseConfig.browserslistPath,
180
181
  browserslistEnv: options.browserslistEnv !== undefined ? options.browserslistEnv : baseConfig.browserslistEnv,
182
+ batchSize: options.batchSize !== undefined ? options.batchSize : baseConfig.batchSize,
181
183
  };
182
184
 
183
185
  if (ecmaVersionArg !== undefined) {
@@ -367,20 +369,22 @@ async function runChecks(configs, logger) {
367
369
  }
368
370
  }
369
371
 
370
- const expandedPathsToIgnore = pathsToIgnore.reduce((result, path) =>
371
- path.includes('*') ? result.concat(glob.sync(path, globOpts)) : result.concat(path)
372
- , [])
373
-
374
- const filterForIgnore = (globbedFiles) => {
375
- if (expandedPathsToIgnore && expandedPathsToIgnore.length > 0) {
376
- return globbedFiles.filter(
377
- (filePath) => !expandedPathsToIgnore.some((ignoreValue) => filePath.includes(ignoreValue))
378
- );
379
- }
380
- return globbedFiles;
372
+ let expandedPathsToIgnore = [];
373
+ if (pathsToIgnore.length > 0) {
374
+ expandedPathsToIgnore = pathsToIgnore.reduce((result, path) => {
375
+ if (path.includes('*')) {
376
+ return result.concat(glob.sync(path, globOpts));
377
+ }
378
+ return result.concat(path);
379
+ }, []);
381
380
  }
382
381
 
383
- const filteredFiles = filterForIgnore(allMatchedFiles)
382
+ let filteredFiles = allMatchedFiles;
383
+ if (expandedPathsToIgnore.length > 0) {
384
+ filteredFiles = allMatchedFiles.filter((filePath) => {
385
+ return !expandedPathsToIgnore.some((ignoreValue) => filePath.includes(ignoreValue));
386
+ });
387
+ }
384
388
 
385
389
  const ignoreList = parseIgnoreList(config);
386
390
 
@@ -388,27 +392,27 @@ async function runChecks(configs, logger) {
388
392
  logger.debug('ES-Check: ignoring features:', Array.from(ignoreList).join(', '));
389
393
  }
390
394
 
391
- filteredFiles.forEach((file) => {
392
- const code = fs.readFileSync(file, 'utf8')
395
+ const batchSize = parseInt(config.batchSize || '0', 10);
396
+
397
+ const processFile = async (file) => {
398
+ const { content: code, error: readError } = await readFileAsync(file, fs);
399
+ if (readError) {
400
+ return readError;
401
+ }
402
+
393
403
  if (logger.isLevelEnabled('debug')) {
394
404
  logger.debug(`ES-Check: checking ${file}`)
395
405
  }
396
- try {
397
- acorn.parse(code, acornOpts)
398
- } catch (err) {
406
+
407
+ const { ast, error: parseError } = parseCode(code, acornOpts, acorn, file);
408
+ if (parseError) {
399
409
  if (logger.isLevelEnabled('debug')) {
400
- logger.debug(`ES-Check: failed to parse file: ${file} \n - error: ${err}`)
401
- }
402
- const errorObj = {
403
- err,
404
- stack: err.stack,
405
- file,
410
+ logger.debug(`ES-Check: failed to parse file: ${file} \n - error: ${parseError.err}`)
406
411
  }
407
- errArray.push(errorObj);
408
- return;
412
+ return parseError;
409
413
  }
410
414
 
411
- if (!checkFeatures) return;
415
+ if (!checkFeatures) return null;
412
416
  const parseSourceType = acornOpts.sourceType || 'script';
413
417
  const esVersion = parseInt(ecmaVersion, 10);
414
418
 
@@ -416,7 +420,8 @@ async function runChecks(configs, logger) {
416
420
  code,
417
421
  esVersion,
418
422
  parseSourceType,
419
- ignoreList
423
+ ignoreList,
424
+ { ast, checkForPolyfills }
420
425
  );
421
426
 
422
427
  if (logger.isLevelEnabled('debug')) {
@@ -442,13 +447,18 @@ async function runChecks(configs, logger) {
442
447
  const isSupported = filteredUnsupportedFeatures.length === 0;
443
448
  if (!isSupported) {
444
449
  const error = new Error(`Unsupported features used: ${filteredUnsupportedFeatures.join(', ')} but your target is ES${ecmaVersion}.`);
445
- errArray.push({
450
+ return {
446
451
  err: error,
447
452
  file,
448
453
  stack: error.stack
449
- });
454
+ };
450
455
  }
451
- })
456
+ return null;
457
+ };
458
+
459
+ const results = await processBatchedFiles(filteredFiles, processFile, batchSize);
460
+ const errors = results.filter(result => result !== null);
461
+ errArray.push(...errors);
452
462
 
453
463
  if (errArray.length > 0) {
454
464
  logger.error(`ES-Check: there were ${errArray.length} ES version matching errors.`)
@@ -475,4 +485,11 @@ async function runChecks(configs, logger) {
475
485
  }
476
486
  }
477
487
 
478
- program.parse()
488
+ if (require.main === module) {
489
+ program.parse()
490
+ }
491
+
492
+ module.exports = {
493
+ runChecks,
494
+ loadConfig
495
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-check",
3
- "version": "9.1.5-0",
3
+ "version": "9.2.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",
@@ -18,6 +18,7 @@
18
18
  "scripts": {
19
19
  "commit": "git-cz",
20
20
  "commit-msg": "commitlint --edit $1",
21
+ "dev": "pnpm site:dev",
21
22
  "lint": "eslint index.js --fix",
22
23
  "lint:ci": "eslint index.js utils.js detectFeatures.js constants.js browserslist.js polyfillDetector.js",
23
24
  "pre-commit": "pnpm lint && pnpm test",
@@ -28,7 +29,11 @@
28
29
  "setup": "pnpm install --reporter=silent",
29
30
  "test": "nyc mocha test.js utils.test.js browserslist.test.js polyfillDetector.test.js detectFeatures.test.js --timeout 10s",
30
31
  "update": "codependence --update",
31
- "benchmark": "./benchmarks/run-benchmarks.sh"
32
+ "benchmark": "./benchmarks/run-benchmarks.sh",
33
+ "site:dev": "pnpm --filter es-check-docs run dev",
34
+ "site:build": "pnpm --filter es-check-docs run build",
35
+ "site:preview": "pnpm --filter es-check-docs run preview",
36
+ "site:install": "pnpm --filter es-check-docs install"
32
37
  },
33
38
  "repository": {
34
39
  "type": "git",
@@ -52,8 +57,8 @@
52
57
  "codependence": "^0.3.1",
53
58
  "commitizen": "4.3.1",
54
59
  "conventional-changelog-cli": "^5.0.0",
55
- "eslint": "9.30.0",
56
- "eslint-config-prettier": "10.1.5",
60
+ "eslint": "9.32.0",
61
+ "eslint-config-prettier": "10.1.8",
57
62
  "eslint-plugin-es5": "^1.5.0",
58
63
  "husky": "9.1.7",
59
64
  "is-ci": "^3.0.1",
@@ -61,7 +66,7 @@
61
66
  "nyc": "^17.1.0",
62
67
  "path-exists-cli": "^2.0.0",
63
68
  "prettier": "3.6.2",
64
- "release-it": "19.0.3"
69
+ "release-it": "19.0.4"
65
70
  },
66
71
  "dependencies": {
67
72
  "acorn": "8.15.0",
package/utils.js CHANGED
@@ -338,6 +338,75 @@ _${cmdName.replace(/-/g, '_')}
338
338
  `;
339
339
  }
340
340
 
341
+ /**
342
+ * Process files in batches for better performance
343
+ * @param {string[]} files - Array of file paths to process
344
+ * @param {Function} processor - Async function to process each file
345
+ * @param {number} batchSize - Number of files to process concurrently (0 for unlimited)
346
+ * @returns {Promise<Array>} Array of results from processing all files
347
+ */
348
+ async function processBatchedFiles(files, processor, batchSize = 0) {
349
+ if (batchSize <= 0) {
350
+ return Promise.all(files.map(processor));
351
+ }
352
+
353
+ const results = [];
354
+
355
+ for (let i = 0; i < files.length; i += batchSize) {
356
+ const batch = files.slice(i, i + batchSize);
357
+ const batchResults = await Promise.all(batch.map(processor));
358
+ results.push(...batchResults);
359
+ }
360
+
361
+ return results;
362
+ }
363
+
364
+ /**
365
+ * Read file asynchronously with error handling
366
+ * @param {string} file - File path to read
367
+ * @param {Object} fs - File system module
368
+ * @returns {Promise<{content: string, error: null} | {content: null, error: Object}>}
369
+ */
370
+ async function readFileAsync(file, fs) {
371
+ try {
372
+ const content = await fs.promises.readFile(file, 'utf8');
373
+ return { content, error: null };
374
+ } catch (err) {
375
+ return {
376
+ content: null,
377
+ error: {
378
+ err,
379
+ file,
380
+ stack: err.stack
381
+ }
382
+ };
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Parse code with acorn and handle errors
388
+ * @param {string} code - Code to parse
389
+ * @param {Object} acornOpts - Acorn parsing options
390
+ * @param {Object} acorn - Acorn module
391
+ * @param {string} file - File path for error reporting
392
+ * @returns {{ast: Object, error: null} | {ast: null, error: Object}}
393
+ */
394
+ function parseCode(code, acornOpts, acorn, file) {
395
+ try {
396
+ const ast = acorn.parse(code, acornOpts);
397
+ return { ast, error: null };
398
+ } catch (err) {
399
+ return {
400
+ ast: null,
401
+ error: {
402
+ err,
403
+ stack: err.stack,
404
+ file
405
+ }
406
+ };
407
+ }
408
+ }
409
+
341
410
  module.exports = {
342
411
  parseIgnoreList,
343
412
  checkVarKindMatch,
@@ -347,5 +416,8 @@ module.exports = {
347
416
  checkMap,
348
417
  createLogger,
349
418
  generateBashCompletion,
350
- generateZshCompletion
419
+ generateZshCompletion,
420
+ processBatchedFiles,
421
+ readFileAsync,
422
+ parseCode
351
423
  };