bahlint 28.58.693401 → 28.58.69340002

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
@@ -1,13 +1,13 @@
1
- [![npm version](https://img.shields.io/npm/v/bahlint.svg)](https://www.npmjs.com/package/bahlint)
2
- [![Downloads](https://img.shields.io/npm/dm/bahlint.svg)](https://www.npmjs.com/package/bahlint)
3
- [![Build Status](https://github.com/bahlint/bahlint/workflows/CI/badge.svg)](https://github.com/bahlint/bahlint/actions)
1
+ [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint)
2
+ [![Downloads](https://img.shields.io/npm/dm/eslint.svg)](https://www.npmjs.com/package/eslint)
3
+ [![Build Status](https://github.com/eslint/eslint/workflows/CI/badge.svg)](https://github.com/eslint/eslint/actions)
4
4
  <br>
5
5
  [![Open Collective Backers](https://img.shields.io/opencollective/backers/eslint)](https://opencollective.com/eslint)
6
6
  [![Open Collective Sponsors](https://img.shields.io/opencollective/sponsors/eslint)](https://opencollective.com/eslint)
7
7
 
8
- # Bahlint
8
+ # ESLint
9
9
 
10
- [Website](https://bahlint.vercel.app/) |
10
+ [Website](https://eslint.org) |
11
11
  [Configure ESLint](https://eslint.org/docs/latest/use/configure) |
12
12
  [Rules](https://eslint.org/docs/rules/) |
13
13
  [Contribute to ESLint](https://eslint.org/docs/latest/contribute) |
@@ -18,17 +18,11 @@
18
18
  [Mastodon](https://fosstodon.org/@eslint) |
19
19
  [Bluesky](https://bsky.app/profile/eslint.org)
20
20
 
21
- Bahlint is a zero-config fork of ESLint for JavaScript/TypeScript projects. It reuses the ESLint engine but gives you:
21
+ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. In many ways, it is similar to JSLint and JSHint with a few exceptions:
22
22
 
23
- - A built-in flat config so you can just run `npx bahlint .` with no config file.
24
- - Support for `bahlint.config.*` (preferred) and `eslint.config.*` flat config files.
25
- - The same rules, parser, and plugin ecosystem as ESLint.
26
-
27
- Under the hood, Bahlint still behaves like ESLint:
28
-
29
- - It uses [Espree](https://github.com/eslint/js/tree/main/packages/espree) for JavaScript parsing.
30
- - It uses an AST to evaluate patterns in code.
31
- - It is completely pluggable, every single rule is a plugin and you can add more at runtime.
23
+ - ESLint uses [Espree](https://github.com/eslint/js/tree/main/packages/espree) for JavaScript parsing.
24
+ - ESLint uses an AST to evaluate patterns in code.
25
+ - ESLint is completely pluggable, every single rule is a plugin and you can add more at runtime.
32
26
 
33
27
  ## Table of Contents
34
28
 
@@ -50,31 +44,21 @@ Under the hood, Bahlint still behaves like ESLint:
50
44
 
51
45
  Prerequisites: [Node.js](https://nodejs.org/) (`^20.19.0`, `^22.13.0`, or `>=24`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
52
46
 
53
- Install Bahlint into your project:
54
-
55
- ```shell
56
- npm install --save-dev bahlint
57
- ```
58
-
59
- Then run it from your project root:
47
+ You can install and configure ESLint using this command:
60
48
 
61
49
  ```shell
62
- npx bahlint .
50
+ npm init @eslint/config@latest
63
51
  ```
64
52
 
65
- Bahlint will lint your JavaScript/TypeScript files using its built-in flat config even if you don't have any config file.
66
-
67
- If you want to generate a custom flat config using the original ESLint wizard, you can still run:
53
+ After that, you can run ESLint on any file or directory like this:
68
54
 
69
55
  ```shell
70
- npm init @eslint/config@latest
56
+ npx eslint yourfile.js
71
57
  ```
72
58
 
73
- and then reuse the generated config with Bahlint.
74
-
75
59
  ### pnpm Installation
76
60
 
77
- To use Bahlint (and the underlying ESLint engine) with pnpm, we recommend setting up a `.npmrc` file with at least the following settings:
61
+ To use ESLint with pnpm, we recommend setting up a `.npmrc` file with at least the following settings:
78
62
 
79
63
  ```text
80
64
  auto-install-peers=true
@@ -85,7 +69,7 @@ This ensures that pnpm installs dependencies in a way that is more compatible wi
85
69
 
86
70
  ## Configuration
87
71
 
88
- You can configure rules in your `bahlint.config.js` (or `eslint.config.js`) file as in this example:
72
+ You can configure rules in your `eslint.config.js` files as in this example:
89
73
 
90
74
  ```js
91
75
  import { defineConfig } from "eslint/config";
@@ -101,7 +85,7 @@ export default defineConfig([
101
85
  ]);
102
86
  ```
103
87
 
104
- The names `"prefer-const"` and `"no-constant-binary-expression"` are the names of [rules](https://eslint.org/docs/rules) in ESLint (and therefore in Bahlint). The first value is the error level of the rule and can be one of these values:
88
+ The names `"prefer-const"` and `"no-constant-binary-expression"` are the names of [rules](https://eslint.org/docs/rules) in ESLint. The first value is the error level of the rule and can be one of these values:
105
89
 
106
90
  - `"off"` or `0` - turn the rule off
107
91
  - `"warn"` or `1` - turn the rule on as a warning (doesn't affect exit code)
@@ -166,7 +150,7 @@ Refer to the [Quick Start Guide](https://eslint.org/docs/latest/use/getting-star
166
150
 
167
151
  ### Where to ask for help?
168
152
 
169
- Open a [discussion](https://github.com/bahlint/bahlint/discussions) or stop by our [Discord server](https://eslint.org/chat).
153
+ Open a [discussion](https://github.com/eslint/eslint/discussions) or stop by our [Discord server](https://eslint.org/chat).
170
154
 
171
155
  ### Why doesn't ESLint lock dependency versions?
172
156
 
@@ -180,35 +164,35 @@ The Twilio blog has a [deeper dive](https://www.twilio.com/blog/lockfiles-nodejs
180
164
 
181
165
  ## Releases
182
166
 
183
- We have scheduled releases every two weeks on Friday or Saturday. You can follow a [release issue](https://github.com/bahlint/bahlint/issues?q=is%3Aopen+is%3Aissue+label%3Arelease) for updates about the scheduling of any particular release.
167
+ We have scheduled releases every two weeks on Friday or Saturday. You can follow a [release issue](https://github.com/eslint/eslint/issues?q=is%3Aopen+is%3Aissue+label%3Arelease) for updates about the scheduling of any particular release.
184
168
 
185
169
  ## Security Policy
186
170
 
187
- Bahlint takes security seriously. We work hard to ensure that Bahlint is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/bahlint/.github/blob/master/SECURITY.md).
171
+ ESLint takes security seriously. We work hard to ensure that ESLint is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/eslint/.github/blob/master/SECURITY.md).
188
172
 
189
173
  ## Semantic Versioning Policy
190
174
 
191
- Bahlint follows [semantic versioning](https://semver.org). However, due to the nature of Bahlint as a code quality tool, it's not always clear when a minor or major version bump occurs. To help clarify this for everyone, we've defined the following semantic versioning policy for Bahlint:
175
+ ESLint follows [semantic versioning](https://semver.org). However, due to the nature of ESLint as a code quality tool, it's not always clear when a minor or major version bump occurs. To help clarify this for everyone, we've defined the following semantic versioning policy for ESLint:
192
176
 
193
177
  - Patch release (intended to not break your lint build)
194
- - A bug fix in a rule that results in Bahlint reporting fewer linting errors.
178
+ - A bug fix in a rule that results in ESLint reporting fewer linting errors.
195
179
  - A bug fix to the CLI or core (including formatters).
196
180
  - Improvements to documentation.
197
181
  - Non-user-facing changes such as refactoring code, adding, deleting, or modifying tests, and increasing test coverage.
198
182
  - Re-releasing after a failed release (i.e., publishing a release that doesn't work for anyone).
199
183
  - Minor release (might break your lint build)
200
- - A bug fix in a rule that results in Bahlint reporting more linting errors.
184
+ - A bug fix in a rule that results in ESLint reporting more linting errors.
201
185
  - A new rule is created.
202
- - A new option to an existing rule that does not result in Bahlint reporting more linting errors by default.
203
- - A new addition to an existing rule to support a newly-added language feature (within the last 12 months) that will result in Bahlint reporting more linting errors by default.
186
+ - A new option to an existing rule that does not result in ESLint reporting more linting errors by default.
187
+ - A new addition to an existing rule to support a newly-added language feature (within the last 12 months) that will result in ESLint reporting more linting errors by default.
204
188
  - An existing rule is deprecated.
205
189
  - A new CLI capability is created.
206
190
  - New capabilities to the public API are added (new classes, new methods, new arguments to existing methods, etc.).
207
191
  - A new formatter is created.
208
- - `bahlint:recommended` is updated and will result in strictly fewer linting errors (e.g., rule removals).
192
+ - `eslint:recommended` is updated and will result in strictly fewer linting errors (e.g., rule removals).
209
193
  - Major release (likely to break your lint build)
210
- - `bahlint:recommended` is updated and may result in new linting errors (e.g., rule additions, most rule option updates).
211
- - A new option to an existing rule that results in Bahlint reporting more linting errors by default.
194
+ - `eslint:recommended` is updated and may result in new linting errors (e.g., rule additions, most rule option updates).
195
+ - A new option to an existing rule that results in ESLint reporting more linting errors by default.
212
196
  - An existing formatter is removed.
213
197
  - Part of the public API is removed or changed in an incompatible way. The public API includes:
214
198
  - Rule schemas
@@ -217,7 +201,7 @@ Bahlint follows [semantic versioning](https://semver.org). However, due to the n
217
201
  - Node.js API
218
202
  - Rule, formatter, parser, plugin APIs
219
203
 
220
- According to our policy, any minor update may report more linting errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"bahlint": "~3.1.0"` to guarantee the results of your builds.
204
+ According to our policy, any minor update may report more linting errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"eslint": "~3.1.0"` to guarantee the results of your builds.
221
205
 
222
206
  ## License
223
207
 
@@ -253,7 +237,7 @@ These folks keep the project moving and are resources for help.
253
237
 
254
238
  ### Technical Steering Committee (TSC)
255
239
 
256
- The people who manage releases, review feature requests, and meet regularly to ensure Bahlint is properly maintained.
240
+ The people who manage releases, review feature requests, and meet regularly to ensure ESLint is properly maintained.
257
241
 
258
242
  <table><tbody><tr><td align="center" valign="top" width="11%">
259
243
  <a href="https://github.com/nzakas">
@@ -352,14 +336,14 @@ Percy Ma
352
336
 
353
337
  ## Sponsors
354
338
 
355
- The following companies, organizations, and individuals support Bahlint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
339
+ The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
356
340
  to get your logo on our READMEs and [website](https://eslint.org/sponsors).
357
341
 
358
342
  <h3>Platinum Sponsors</h3>
359
343
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
360
344
  <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
361
345
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
362
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
346
+ <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
363
347
  <h3>Technology Sponsors</h3>
364
348
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
365
349
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -13,10 +13,6 @@
13
13
  },
14
14
  {
15
15
  "name": "stylish",
16
- "description": "Human-readable output format."
17
- },
18
- {
19
- "name": "gasoline",
20
- "description": "BAHLINT gasoline-themed human-readable"
16
+ "description": "Human-readable output format. This is the default formatter."
21
17
  }
22
18
  ]
package/lib/cli.js CHANGED
@@ -26,7 +26,6 @@ const fs = require("node:fs"),
26
26
  translateOptions = require("./shared/translate-cli-options");
27
27
  const { getCacheFile } = require("./eslint/eslint-helpers");
28
28
  const { SuppressionsService } = require("./services/suppressions-service");
29
- const { startTime, endTime } = require("./shared/stats");
30
29
  const debug = require("debug")("eslint:cli");
31
30
 
32
31
  //------------------------------------------------------------------------------
@@ -60,23 +59,6 @@ function countErrors(results) {
60
59
  return { errorCount, fatalErrorCount, warningCount };
61
60
  }
62
61
 
63
- /**
64
- * Count fixable problems.
65
- * @param {LintResult[]} results The lint results.
66
- * @returns {{fixableErrorCount:number;fixableWarningCount:number}} The number of fixable problems.
67
- */
68
- function countFixableProblems(results) {
69
- let fixableErrorCount = 0;
70
- let fixableWarningCount = 0;
71
-
72
- for (const result of results) {
73
- fixableErrorCount += result.fixableErrorCount;
74
- fixableWarningCount += result.fixableWarningCount;
75
- }
76
-
77
- return { fixableErrorCount, fixableWarningCount };
78
- }
79
-
80
62
  /**
81
63
  * Creates an options module from the provided CLI options and encodes it as a data URL.
82
64
  * @param {ParsedCLIOptions} options The CLI options.
@@ -384,8 +366,6 @@ const cli = {
384
366
  return 2;
385
367
  }
386
368
 
387
- const autoFix = !useStdin && !options.fix && !options.fixDryRun;
388
-
389
369
  /** @type {ESLint} */
390
370
  let engine;
391
371
 
@@ -398,8 +378,6 @@ const cli = {
398
378
  }
399
379
  let results;
400
380
 
401
- const lintStartTime = startTime();
402
-
403
381
  if (useStdin) {
404
382
  results = await engine.lintText(text, {
405
383
  filePath: options.stdinFilename,
@@ -408,9 +386,7 @@ const cli = {
408
386
  results = await engine.lintFiles(files);
409
387
  }
410
388
 
411
- const didApplyFixes = options.fix || autoFix;
412
-
413
- if (didApplyFixes) {
389
+ if (options.fix) {
414
390
  debug("Fix mode enabled - applying fixes");
415
391
  await ESLint.outputFixes(results);
416
392
  }
@@ -497,20 +473,6 @@ const cli = {
497
473
  };
498
474
  }
499
475
 
500
- const totalProblems =
501
- resultCounts.errorCount + resultCounts.warningCount;
502
- const { fixableErrorCount, fixableWarningCount } =
503
- countFixableProblems(results);
504
- const totalFixableProblems =
505
- fixableErrorCount + fixableWarningCount;
506
- const elapsedMs = endTime(lintStartTime);
507
- const elapsedSeconds = (elapsedMs / 1000).toFixed(2);
508
- const shouldShowBahlintSummary =
509
- options.format === "gasoline" &&
510
- !options.quiet &&
511
- !useStdin &&
512
- totalProblems > 0;
513
-
514
476
  if (
515
477
  await printResults(
516
478
  engine,
@@ -520,54 +482,6 @@ const cli = {
520
482
  resultsMeta,
521
483
  )
522
484
  ) {
523
- if (shouldShowBahlintSummary) {
524
- const versionText = RuntimeInfo.version();
525
- const problemsWord =
526
- totalProblems === 1 ? "problem" : "problems";
527
- const autoFixedCount = didApplyFixes
528
- ? totalFixableProblems
529
- : 0;
530
- const autoFixedWord =
531
- autoFixedCount === 1 ? "problem" : "problems";
532
- const filesScanned = results.length;
533
- const filesWord =
534
- filesScanned === 1 ? "file" : "files";
535
-
536
- const util = require("node:util");
537
-
538
- let styleText;
539
-
540
- if (options.color === void 0) {
541
- styleText = (format, text) =>
542
- util.styleText(format, text, {
543
- validateStream: true,
544
- });
545
- } else if (options.color) {
546
- styleText = (format, text) =>
547
- util.styleText(format, text, {
548
- validateStream: false,
549
- });
550
- } else {
551
- styleText = (_, text) => text;
552
- }
553
-
554
- const headerLine = `🔥 Bahlint ${versionText} - Burning your code...`;
555
- const problemsLine = `⚠ ${totalProblems} ${problemsWord} found`;
556
- const autoFixedLine = `✓ ${autoFixedCount} ${autoFixedWord} auto-fixed`;
557
- const filesLine = `✓ ${filesScanned} ${filesWord} scanned in ${elapsedSeconds}s`;
558
-
559
- // eslint-disable-next-line no-console -- Bahlint summary output
560
- console.log(
561
- styleText("red", styleText("bold", headerLine)),
562
- );
563
- // eslint-disable-next-line no-console -- Bahlint summary output
564
- console.log(styleText("yellow", problemsLine));
565
- // eslint-disable-next-line no-console -- Bahlint summary output
566
- console.log(styleText("green", autoFixedLine));
567
- // eslint-disable-next-line no-console -- Bahlint summary output
568
- console.log(styleText("dim", filesLine));
569
- }
570
-
571
485
  // Errors and warnings from the original unfiltered results should determine the exit code
572
486
  const shouldExitForFatalErrors =
573
487
  options.exitOnFatalError && resultCounts.fatalErrorCount > 0;
@@ -16,7 +16,6 @@ const { pathToFileURL } = require("node:url");
16
16
  const debug = require("debug")("eslint:config-loader");
17
17
  const { FlatConfigArray } = require("./flat-config-array");
18
18
  const { WarningService } = require("../services/warning-service");
19
- const { defaultConfig } = require("./default-config");
20
19
 
21
20
  //-----------------------------------------------------------------------------
22
21
  // Types
@@ -42,12 +41,6 @@ const { defaultConfig } = require("./default-config");
42
41
  //------------------------------------------------------------------------------
43
42
 
44
43
  const FLAT_CONFIG_FILENAMES = [
45
- "bahlint.config.js",
46
- "bahlint.config.mjs",
47
- "bahlint.config.cjs",
48
- "bahlint.config.ts",
49
- "bahlint.config.mts",
50
- "bahlint.config.cts",
51
44
  "eslint.config.js",
52
45
  "eslint.config.mjs",
53
46
  "eslint.config.cjs",
@@ -83,12 +76,7 @@ function assertValidFilePath(filePath) {
83
76
  function assertConfigurationExists(configFilePath, loaderOptions) {
84
77
  const { configFile: useConfigFile } = loaderOptions;
85
78
 
86
- /*
87
- * For bahlint we allow running without any config file present and fall
88
- * back to the built-in default configuration. We only throw when the
89
- * user explicitly requested a config file that cannot be found.
90
- */
91
- if (!configFilePath && typeof useConfigFile === "string") {
79
+ if (!configFilePath && useConfigFile !== false) {
92
80
  const error = new Error("Could not find config file.");
93
81
 
94
82
  error.messageTemplate = "config-file-missing";
@@ -646,9 +634,6 @@ class ConfigLoader {
646
634
  if (emptyConfig) {
647
635
  warningService.emitEmptyConfigWarning(configFilePath);
648
636
  }
649
- } else {
650
- // When no config file is found, fall back to the built-in default configuration.
651
- configs.push(...defaultConfig);
652
637
  }
653
638
 
654
639
  // add in any configured defaults
package/lib/options.js CHANGED
@@ -222,7 +222,7 @@ module.exports = function () {
222
222
  option: "format",
223
223
  alias: "f",
224
224
  type: "String",
225
- default: "gasoline",
225
+ default: "stylish",
226
226
  description: "Use a specific output format",
227
227
  },
228
228
  {
@@ -11,6 +11,7 @@
11
11
  //------------------------------------------------------------------------------
12
12
 
13
13
  const assert = require("node:assert"),
14
+ { existsSync, readFileSync } = require("node:fs"),
14
15
  util = require("node:util"),
15
16
  path = require("node:path"),
16
17
  equal = require("fast-deep-equal"),
@@ -666,6 +667,177 @@ function getInvocationLocation(relative = getInvocationLocation) {
666
667
  return stack.split("\n")[1].replace(/.*?\(/u, "").replace(/\)$/u, "");
667
668
  }
668
669
 
670
+ /**
671
+ * Estimates the location of the test case in the source file.
672
+ * @param {Function} invoker The method that runs the tests.
673
+ * @returns {(key: string) => string} The lazy resolver for the estimated location of the test case.
674
+ */
675
+ function buildLazyTestLocationEstimator(invoker) {
676
+ const invocationLocation = getInvocationLocation(invoker);
677
+ let testLocations = null;
678
+ return key => {
679
+ if (testLocations === null) {
680
+ let [sourceFile, sourceLine = "1", sourceColumn = "1"] =
681
+ invocationLocation
682
+ .replace(/:\\/u, "\\\\") // Windows workaround for C:\\
683
+ .split(":");
684
+ sourceFile = sourceFile.replace(/\\\\/u, ":\\");
685
+ sourceLine = Number(sourceLine);
686
+ sourceColumn = Number(sourceColumn);
687
+ testLocations = { root: invocationLocation };
688
+
689
+ if (existsSync(sourceFile)) {
690
+ let content = readFileSync(sourceFile, "utf8")
691
+ .split("\n")
692
+ .slice(sourceLine - 1);
693
+ content[0] = content[0].slice(Math.max(0, sourceColumn - 1));
694
+ content = content.map(
695
+ l =>
696
+ l
697
+ .trim() // Remove whitespace
698
+ .replace(/\s*\/\/.*$(?<!,)/u, ""), // and trailing in-line comments that aren't part of the test `code`
699
+ );
700
+
701
+ // Roots
702
+ const validStartIndex = content.findIndex(line =>
703
+ /\bvalid:/u.test(line),
704
+ );
705
+ const invalidStartIndex = content.findIndex(line =>
706
+ /\binvalid:/u.test(line),
707
+ );
708
+
709
+ testLocations.valid = `${sourceFile}:${
710
+ sourceLine + validStartIndex
711
+ }`;
712
+ testLocations.invalid = `${sourceFile}:${
713
+ sourceLine + invalidStartIndex
714
+ }`;
715
+
716
+ // Scenario basics
717
+ const validEndIndex =
718
+ validStartIndex < invalidStartIndex
719
+ ? invalidStartIndex
720
+ : content.length;
721
+ const invalidEndIndex =
722
+ validStartIndex < invalidStartIndex
723
+ ? content.length
724
+ : validStartIndex;
725
+
726
+ const validLines = content.slice(
727
+ validStartIndex,
728
+ validEndIndex,
729
+ );
730
+ const invalidLines = content.slice(
731
+ invalidStartIndex,
732
+ invalidEndIndex,
733
+ );
734
+
735
+ let objectDepth = 0;
736
+ const validLineIndexes = validLines
737
+ .map((l, i) => {
738
+ // matches `key: {` and `{`
739
+ if (/^(?:\w+\s*:\s*)?\{/u.test(l)) {
740
+ objectDepth++;
741
+ }
742
+
743
+ if (objectDepth > 0) {
744
+ if (l.endsWith("}") || l.endsWith("},")) {
745
+ objectDepth--;
746
+ }
747
+
748
+ return objectDepth <= 1 && l.includes("code:")
749
+ ? i
750
+ : null;
751
+ }
752
+
753
+ return l.endsWith(",") ? i : null;
754
+ })
755
+ .filter(Boolean);
756
+ const invalidLineIndexes = invalidLines
757
+ .map((l, i) =>
758
+ l.trimStart().startsWith("errors:") ? i : null,
759
+ )
760
+ .filter(Boolean);
761
+
762
+ Object.assign(
763
+ testLocations,
764
+ {
765
+ [`valid[0]`]: `${sourceFile}:${
766
+ sourceLine + validStartIndex
767
+ }`,
768
+ },
769
+ Object.fromEntries(
770
+ validLineIndexes.map((location, validIndex) => [
771
+ `valid[${validIndex}]`,
772
+ `${sourceFile}:${
773
+ sourceLine + validStartIndex + location
774
+ }`,
775
+ ]),
776
+ ),
777
+ Object.fromEntries(
778
+ invalidLineIndexes.map((location, invalidIndex) => [
779
+ `invalid[${invalidIndex}]`,
780
+ `${sourceFile}:${
781
+ sourceLine + invalidStartIndex + location
782
+ }`,
783
+ ]),
784
+ ),
785
+ );
786
+
787
+ // Indexes for errors inside each invalid test case
788
+ invalidLineIndexes.push(invalidLines.length);
789
+
790
+ for (let i = 0; i < invalidLineIndexes.length - 1; i++) {
791
+ const start = invalidLineIndexes[i];
792
+ const end = invalidLineIndexes[i + 1];
793
+ const errorLines = invalidLines.slice(start, end);
794
+ let errorObjectDepth = 0;
795
+ const errorLineIndexes = errorLines
796
+ .map((l, j) => {
797
+ if (l.startsWith("{") || l.endsWith("{")) {
798
+ errorObjectDepth++;
799
+
800
+ if (l.endsWith("}") || l.endsWith("},")) {
801
+ errorObjectDepth--;
802
+ }
803
+
804
+ return errorObjectDepth <= 1 ? j : null;
805
+ }
806
+
807
+ if (errorObjectDepth > 0) {
808
+ if (l.endsWith("}") || l.endsWith("},")) {
809
+ errorObjectDepth--;
810
+ }
811
+
812
+ return null;
813
+ }
814
+
815
+ return l.endsWith(",") ? j : null;
816
+ })
817
+ .filter(Boolean);
818
+
819
+ Object.assign(
820
+ testLocations,
821
+ Object.fromEntries(
822
+ errorLineIndexes.map((line, errorIndex) => [
823
+ `invalid[${i}].errors[${errorIndex}]`,
824
+ `${sourceFile}:${
825
+ sourceLine +
826
+ invalidStartIndex +
827
+ start +
828
+ line
829
+ }`,
830
+ ]),
831
+ ),
832
+ );
833
+ }
834
+ }
835
+ }
836
+
837
+ return testLocations[key] || "unknown source";
838
+ };
839
+ }
840
+
669
841
  //------------------------------------------------------------------------------
670
842
  // Public Interface
671
843
  //------------------------------------------------------------------------------
@@ -866,7 +1038,7 @@ class RuleTester {
866
1038
  assertRule(rule, ruleName);
867
1039
  assertTest(test, ruleName);
868
1040
 
869
- const invocationLocation = getInvocationLocation(this.run);
1041
+ const estimateTestLocation = buildLazyTestLocationEstimator(this.run);
870
1042
 
871
1043
  const baseConfig = [
872
1044
  {
@@ -1744,9 +1916,9 @@ class RuleTester {
1744
1916
  error.stack = error.stack.replace(
1745
1917
  /^ +at /mu,
1746
1918
  [
1747
- ` at RuleTester.run.valid[${index}]`,
1748
- ` at RuleTester.run.valid`,
1749
- ` at RuleTester.run (${invocationLocation})`,
1919
+ ` roughly at RuleTester.run.valid[${index}] (${estimateTestLocation(`valid[${index}]`)})`,
1920
+ ` roughly at RuleTester.run.valid (${estimateTestLocation("valid")})`,
1921
+ ` at RuleTester.run (${estimateTestLocation("root")})`,
1750
1922
  " at ",
1751
1923
  ].join("\n"),
1752
1924
  );
@@ -1789,12 +1961,12 @@ class RuleTester {
1789
1961
  ...(typeof errorIndex ===
1790
1962
  "number"
1791
1963
  ? [
1792
- ` at RuleTester.run.invalid[${index}].error[${errorIndex}]`,
1964
+ ` roughly at RuleTester.run.invalid[${index}].error[${errorIndex}] (${estimateTestLocation(`invalid[${index}].errors[${errorIndex}]`)})`,
1793
1965
  ]
1794
1966
  : []),
1795
- ` at RuleTester.run.invalid[${index}]`,
1796
- ` at RuleTester.run.invalid`,
1797
- ` at RuleTester.run (${invocationLocation})`,
1967
+ ` roughly at RuleTester.run.invalid[${index}] (${estimateTestLocation(`invalid[${index}]`)})`,
1968
+ ` roughly at RuleTester.run.invalid (${estimateTestLocation("invalid")})`,
1969
+ ` at RuleTester.run (${estimateTestLocation("root")})`,
1798
1970
  " at ",
1799
1971
  ].join("\n"),
1800
1972
  );
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "bahlint",
3
- "version": "28.58.693401",
3
+ "version": "28.58.69340002",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
7
7
  "bin": {
8
- "bahlint": "./bin/eslint.js"
8
+ "eslint": "./bin/eslint.js"
9
9
  },
10
10
  "main": "./lib/api.js",
11
11
  "types": "./lib/types/index.d.ts",
@@ -103,7 +103,7 @@
103
103
  ],
104
104
  "repository": "eslint/eslint",
105
105
  "funding": "https://eslint.org/donate",
106
- "homepage": "https://bahlint.vercel.app/",
106
+ "homepage": "https://eslint.org",
107
107
  "bugs": "https://github.com/eslint/eslint/issues/",
108
108
  "dependencies": {
109
109
  "@eslint-community/eslint-utils": "^4.8.0",
@@ -1,168 +0,0 @@
1
- /**
2
- * @fileoverview Gasoline-themed reporter for Bahlint.
3
- * Inspired by the default stylish formatter, but with a fuel-oriented twist.
4
- * @author ESLint
5
- */
6
- "use strict";
7
-
8
- const util = require("node:util"),
9
- table = require("../../shared/text-table");
10
-
11
- //------------------------------------------------------------------------------
12
- // Helpers
13
- //------------------------------------------------------------------------------
14
-
15
- /**
16
- * Returns a styling function based on the color option.
17
- * @param {boolean|undefined} color Indicates whether to use colors.
18
- * @returns {Function} A function that styles text.
19
- */
20
- function getStyleText(color) {
21
- if (typeof color === "undefined") {
22
- return (format, text) =>
23
- util.styleText(format, text, { validateStream: true });
24
- }
25
- if (color) {
26
- return (format, text) =>
27
- util.styleText(format, text, { validateStream: false });
28
- }
29
- return (_, text) => text;
30
- }
31
-
32
- /**
33
- * Given a word and a count, append an s if count is not one.
34
- * @param {string} word A word in its singular form.
35
- * @param {number} count A number controlling whether word should be pluralized.
36
- * @returns {string} The original word with an s on the end if count is not one.
37
- */
38
- function pluralize(word, count) {
39
- return count === 1 ? word : `${word}s`;
40
- }
41
-
42
- //------------------------------------------------------------------------------
43
- // Public Interface
44
- //------------------------------------------------------------------------------
45
-
46
- module.exports = function (results, data) {
47
- const styleText = getStyleText(data?.color);
48
-
49
- let output = "\n",
50
- errorCount = 0,
51
- warningCount = 0,
52
- fixableErrorCount = 0,
53
- fixableWarningCount = 0;
54
-
55
- results.forEach(result => {
56
- const messages = result.messages;
57
-
58
- if (messages.length === 0) {
59
- return;
60
- }
61
-
62
- errorCount += result.errorCount;
63
- warningCount += result.warningCount;
64
- fixableErrorCount += result.fixableErrorCount;
65
- fixableWarningCount += result.fixableWarningCount;
66
-
67
- // Highlight file path like a fuel line.
68
- output += `${styleText(
69
- "underline",
70
- styleText("yellow", result.filePath),
71
- )}\n`;
72
-
73
- output += `${table(
74
- messages.map(message => {
75
- const line = String(message.line || 0);
76
- const column = String(message.column || 0);
77
- const isError = message.fatal || message.severity === 2;
78
-
79
- const messageType = styleText(
80
- isError ? "red" : "yellow",
81
- isError ? "ERR" : "WARN",
82
- );
83
-
84
- return [
85
- "",
86
- line,
87
- column,
88
- messageType,
89
- message.message.replace(/([^ ])\.$/u, "$1"),
90
- message.ruleId ? styleText("dim", message.ruleId) : "",
91
- ];
92
- }),
93
- {
94
- align: ["", "r", "l"],
95
- stringLength(str) {
96
- return util.stripVTControlCharacters(str).length;
97
- },
98
- },
99
- )
100
- .split("\n")
101
- .map(el =>
102
- el.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) =>
103
- styleText("dim", `${p1}:${p2}`),
104
- ),
105
- )
106
- .join("\n")}\n\n`;
107
- });
108
-
109
- const total = errorCount + warningCount;
110
-
111
- /*
112
- * We can't use a single `styleText` call like `styleText([summaryColor, "bold"], text)` here.
113
- * This is a bug in `util.styleText` in Node.js versions earlier than v22.15.0 (https://github.com/nodejs/node/issues/56717).
114
- * As a workaround, we use nested `styleText` calls.
115
- */
116
- if (total > 0) {
117
- let summaryColor;
118
- let statusLabel;
119
-
120
- if (errorCount > 0) {
121
- summaryColor = "red";
122
- statusLabel = "ENGINE KNOCK";
123
- } else {
124
- summaryColor = "yellow";
125
- statusLabel = "LOW OCTANE";
126
- }
127
-
128
- const summaryText = [
129
- "[BAHLINT] FUEL STATUS: ",
130
- statusLabel,
131
- " - ",
132
- total,
133
- pluralize(" issue", total),
134
- " (",
135
- errorCount,
136
- pluralize(" error", errorCount),
137
- ", ",
138
- warningCount,
139
- pluralize(" warning", warningCount),
140
- ")",
141
- ].join("");
142
-
143
- output += `${styleText(
144
- summaryColor,
145
- styleText("bold", summaryText),
146
- )}\n`;
147
-
148
- if (fixableErrorCount > 0 || fixableWarningCount > 0) {
149
- const fixSummaryText = [
150
- "[BAHLINT] ",
151
- fixableErrorCount,
152
- pluralize(" error", fixableErrorCount),
153
- " and ",
154
- fixableWarningCount,
155
- pluralize(" warning", fixableWarningCount),
156
- " potentially fixable with the `--fix` pit stop.",
157
- ].join("");
158
-
159
- output += `${styleText(
160
- summaryColor,
161
- styleText("bold", fixSummaryText),
162
- )}\n`;
163
- }
164
- }
165
-
166
- // Resets output color, to prevent changes on the top level
167
- return total > 0 ? styleText("reset", output) : "";
168
- };