@useinsider/eslint-config 4.0.0-beta.1 → 4.0.0-beta.2
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 +100 -9
- package/dist/builders/scopeBlocks.d.ts +6 -0
- package/dist/cli.cjs +551 -166
- package/dist/cli.cjs.map +1 -1
- package/dist/main.cjs +390 -84
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +391 -85
- package/dist/main.js.map +1 -1
- package/dist/utils/ensureConfigStructure.d.ts +8 -4
- package/dist/utils/ensureOxlintConfig.d.ts +2 -1
- package/dist/utils/layoutDiagnosis.d.ts +63 -0
- package/dist/utils/offerMigration.d.ts +42 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,8 +27,8 @@ export const { useInsiderESLint, useInsiderOXC } = useInsider({
|
|
|
27
27
|
});
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
```
|
|
31
|
-
// eslint.config.
|
|
30
|
+
```ts
|
|
31
|
+
// eslint.config.ts
|
|
32
32
|
import { useInsiderESLint } from './insider.lint.ts';
|
|
33
33
|
|
|
34
34
|
export default useInsiderESLint();
|
|
@@ -52,6 +52,13 @@ Three constraints this layout is not free to break:
|
|
|
52
52
|
- **The `./insider.lint.ts` import keeps its real extension.** Neither
|
|
53
53
|
ESLint's loader nor oxlint's applies TypeScript's `.js` → `.ts` specifier
|
|
54
54
|
rewriting, so `'./insider.lint.js'` does not resolve.
|
|
55
|
+
- **The ESLint config needs a TypeScript extension too** — `eslint.config.ts`
|
|
56
|
+
or `eslint.config.mts`. ESLint routes those through jiti, which resolves the
|
|
57
|
+
TypeScript lint module on every supported Node version. A `.js`/`.mjs`/`.cjs`
|
|
58
|
+
config is loaded by Node directly, so importing `./insider.lint.ts` from one
|
|
59
|
+
works only where Node's type stripping is unflagged (Node >= 22.18) and dies
|
|
60
|
+
with `ERR_UNKNOWN_FILE_EXTENSION` on Node 20. Both CLI commands therefore
|
|
61
|
+
rename an existing JavaScript flat config and delete the original.
|
|
55
62
|
- **The oxlint config must be `oxlint.config.ts` or `oxlint.config.mts`.**
|
|
56
63
|
oxlint silently ignores `oxlint.config.{cts,js,mjs,cjs}` and falls through
|
|
57
64
|
to the nearest ancestor config. In a package with no `"type": "module"`,
|
|
@@ -59,7 +66,11 @@ Three constraints this layout is not free to break:
|
|
|
59
66
|
- **A TypeScript project needs `allowImportingTsExtensions: true`** in the
|
|
60
67
|
`tsconfig.json` that covers the layout files, or `tsc` rejects the import
|
|
61
68
|
with `TS5097`. Both CLI flows offer to set it for you, following `extends`
|
|
62
|
-
first so a shared base that already carries it is left alone.
|
|
69
|
+
first so a shared base that already carries it is left alone. TypeScript only
|
|
70
|
+
accepts the flag on a project with `noEmit`, `emitDeclarationOnly` or
|
|
71
|
+
`rewriteRelativeImportExtensions` — on an emitting project it fails with
|
|
72
|
+
`TS5096` — so a project that emits is never edited; the CLI explains what to
|
|
73
|
+
turn on instead.
|
|
63
74
|
- **`oxlint` runs with `--disable-nested-config`.** The emitted config carries
|
|
64
75
|
options oxlint accepts only in a root config, and a discovered nested config
|
|
65
76
|
carrying them aborts the whole run. Disabling nested discovery also makes
|
|
@@ -83,7 +94,9 @@ is a sign your scope coverage is incomplete — describe the extra files inside
|
|
|
83
94
|
- [Strict and spell-checker knobs](#strict-and-spell-checker-knobs)
|
|
84
95
|
- [API reference](#api-reference)
|
|
85
96
|
- [Unused disable directives](#unused-disable-directives)
|
|
97
|
+
- [Lint scripts](#lint-scripts)
|
|
86
98
|
- [Migrating to v4](#migrating-to-v4)
|
|
99
|
+
- [The guided migration offer](#the-guided-migration-offer)
|
|
87
100
|
- [Migrating from v1.x](#migrating-from-v1x)
|
|
88
101
|
- [First lint after install](#first-lint-after-install)
|
|
89
102
|
- [Troubleshooting](#troubleshooting)
|
|
@@ -105,11 +118,14 @@ npx @useinsider/eslint-config
|
|
|
105
118
|
The CLI walks an interactive prompt (project type, language, framework, test
|
|
106
119
|
runner, jQuery globals, tsconfig layout), installs every required linter and
|
|
107
120
|
plugin pinned to the versions this package was built against, writes the three
|
|
108
|
-
layout files at the project root,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
layout files at the project root, wires oxlint into the script that actually
|
|
122
|
+
invokes ESLint (see [Lint scripts](#lint-scripts)), and offers to add
|
|
123
|
+
`allowImportingTsExtensions: true` to the tsconfig that really governs those
|
|
124
|
+
files. In a solution-style project that is the referenced `tsconfig.node.json`,
|
|
125
|
+
not the root — and if an applicable config already sets the flag, or no tsconfig
|
|
126
|
+
type-checks those files, you are not asked at all. That edit is surgical —
|
|
127
|
+
comments, key order and indentation survive — and declining it prints the exact
|
|
128
|
+
snippet to paste.
|
|
113
129
|
|
|
114
130
|
To migrate an existing v1 `eslint.config.{js,mjs,cjs}` or a legacy
|
|
115
131
|
`.eslintrc.{js,cjs}`:
|
|
@@ -432,8 +448,51 @@ silenceRules(['no-magic-numbers']);
|
|
|
432
448
|
export const { useInsiderESLint, useInsiderOXC } = useInsider({ /* ... */ });
|
|
433
449
|
```
|
|
434
450
|
|
|
451
|
+
## Lint scripts
|
|
452
|
+
|
|
453
|
+
Both linters have to run, and oxlint has to run *first* — the ESLint side
|
|
454
|
+
turns off every rule oxlint owns, so ESLint alone silently drops those checks.
|
|
455
|
+
The canonical script is:
|
|
456
|
+
|
|
457
|
+
```json
|
|
458
|
+
{ "scripts": { "lint": "oxlint --disable-nested-config && eslint ." } }
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
The CLI never replaces a script it did not author. It finds the **one** script
|
|
462
|
+
that really invokes the `eslint` binary and **prepends** oxlint to it, leaving
|
|
463
|
+
the original command verbatim inside the new value. A composed `lint` that fans
|
|
464
|
+
out to a type-check and a stylelint pass is therefore untouched:
|
|
465
|
+
|
|
466
|
+
```diff
|
|
467
|
+
"lint": "run-p lint:type lint:es lint:style",
|
|
468
|
+
"lint:es": "npm run lint:es-ci -- --cache",
|
|
469
|
+
- "lint:es-ci": "NODE_OPTIONS=--max-old-space-size=6144 eslint .",
|
|
470
|
+
+ "lint:es-ci": "oxlint --disable-nested-config && NODE_OPTIONS=--max-old-space-size=6144 eslint .",
|
|
471
|
+
"lint:style": "npm run lint:style-ci -- --cache",
|
|
472
|
+
"lint:type": "run-p lint:type:app lint:type:test lint:type:node",
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Prepending rather than appending is deliberate: a `--` passthrough
|
|
476
|
+
(`npm run lint:es-ci -- --cache`) lands on the *last* command, so `--cache`
|
|
477
|
+
still reaches ESLint. A script whose name ends in a `--fix` invocation gets
|
|
478
|
+
`oxlint --disable-nested-config --fix`. Every rewrite is printed with its
|
|
479
|
+
previous value.
|
|
480
|
+
|
|
481
|
+
Exactly one script is rewritten. When several entry points run ESLint —
|
|
482
|
+
`lint`, `lint:fix`, `lint:ci` — prepending to all of them would run oxlint once
|
|
483
|
+
per entry point over the same tree, so the CLI picks the entry point `lint`
|
|
484
|
+
designates (itself, or the leaf it delegates to) and reports the rest with the
|
|
485
|
+
prefix to add if you want oxlint on them too.
|
|
486
|
+
|
|
487
|
+
If no script invokes `eslint` and a `lint` script already exists, nothing is
|
|
488
|
+
written — the conflict is reported with the command to add by hand.
|
|
489
|
+
|
|
435
490
|
## Migrating to v4
|
|
436
491
|
|
|
492
|
+
v4 requires **Node.js 24.19+** (`engines.node` was `>=20` through v3). Check
|
|
493
|
+
your CI images and any repo pinning an older Node before upgrading: npm and
|
|
494
|
+
pnpm warn with `EBADENGINE`, and fail outright under `engine-strict`.
|
|
495
|
+
|
|
437
496
|
v4 splits the lint run across ESLint and oxlint. Run the CLI to do all of
|
|
438
497
|
this automatically:
|
|
439
498
|
|
|
@@ -441,6 +500,37 @@ this automatically:
|
|
|
441
500
|
npx @useinsider/eslint-config migrate
|
|
442
501
|
```
|
|
443
502
|
|
|
503
|
+
### The guided migration offer
|
|
504
|
+
|
|
505
|
+
You do not have to know that command exists. Running lint against a v3 config
|
|
506
|
+
under v4 detects the outdated structure and, in an interactive terminal, offers
|
|
507
|
+
to run the migration there and then:
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
@useinsider/eslint-config: eslint.config.js still uses the v3 single-file layout.
|
|
511
|
+
/repo/eslint.config.js calls `useInsider()` itself, and
|
|
512
|
+
insider.lint.ts and oxlint.config.ts are missing.
|
|
513
|
+
|
|
514
|
+
Starting the guided migration. It shows every change before applying it.
|
|
515
|
+
Skip it with INSIDER_NO_PROMPT=true.
|
|
516
|
+
|
|
517
|
+
◆ eslint.config.js still uses the v3 single-file layout. Migrate it now?
|
|
518
|
+
│ ● Yes / ○ No
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Accepting runs the normal `migrate` flow — same summary, same confirmations —
|
|
522
|
+
and then exits non-zero asking you to re-run lint, because this process already
|
|
523
|
+
loaded the configuration that was just rewritten.
|
|
524
|
+
|
|
525
|
+
The offer is suppressed, and the failure message leads with the `migrate`
|
|
526
|
+
command instead, whenever prompting would be wrong: under oxlint (whose stdout
|
|
527
|
+
is its report), in CI, without a TTY, with `INSIDER_NO_PROMPT=true`, or with
|
|
528
|
+
`INSIDER_SKIP_CONFIG_VALIDATION=true`.
|
|
529
|
+
|
|
530
|
+
The directive rewrite is reported as its own step, with the file count shown
|
|
531
|
+
*before* anything is written, so you can take it as a separate commit or
|
|
532
|
+
decline it and run `migrate` again later.
|
|
533
|
+
|
|
444
534
|
What changes:
|
|
445
535
|
|
|
446
536
|
- **`eslint-disable` no longer suppresses an oxlint-owned rule. This is the
|
|
@@ -509,7 +599,8 @@ What changes:
|
|
|
509
599
|
nothing here applies TypeScript's `.js` → `.ts` rewriting — and `tsc` fails
|
|
510
600
|
with `TS5097` otherwise. The CLI prompts to add it and resolves the full
|
|
511
601
|
`extends` chain first, so a shared base that already sets it is left alone;
|
|
512
|
-
declining prints the snippet to paste.
|
|
602
|
+
declining prints the snippet to paste. A project that emits is never edited —
|
|
603
|
+
TypeScript rejects the flag there with `TS5096`.
|
|
513
604
|
- **`@typescript-eslint` type-aware rules, `sonarjs`, `@stylistic`, `@cspell`,
|
|
514
605
|
`jsdoc/check-tag-names` and `no-inner-declarations` stay with ESLint.** Two
|
|
515
606
|
of those are permanent, for different reasons:
|
|
@@ -44,5 +44,11 @@ declare const scopeMap: {
|
|
|
44
44
|
};
|
|
45
45
|
export type ScopeName = keyof typeof scopeMap;
|
|
46
46
|
export declare function isScopeName(key: string): key is ScopeName;
|
|
47
|
+
/**
|
|
48
|
+
* Every scope name `useInsider()` accepts, for error messages that have to spell the options out.
|
|
49
|
+
* @example
|
|
50
|
+
* scopeNames().includes('typescriptNode'); // true
|
|
51
|
+
*/
|
|
52
|
+
export declare function scopeNames(): ScopeName[];
|
|
47
53
|
export declare function buildScopeBlocks(scope: ScopeName, entries: readonly ScopeEntry[]): Promise<Linter.Config[]>;
|
|
48
54
|
export {};
|