Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions .eslintrc.yaml

This file was deleted.

27 changes: 25 additions & 2 deletions .github/workflows/build-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,35 @@ jobs:

test:
runs-on: 'ubuntu-latest'
strategy:
fail-fast: false
matrix:
# 20.19 is the engines floor (ESLint 10's minimum); 22 is the build
# version (.nvmrc); 24 is current
node-version: ['20.19', '22', '24']
steps:
- name: 'Setup node 🛎️'
uses: 'dudeofawesome/actions/node-setup@a850bdff4354bdb891a86116f0378f8a890e2c16' # v1
- name: 'Checkout 🛎️'
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # v4
- name: 'Setup node ${{ matrix.node-version }} 🛎️'
uses: 'actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af' # v4
with:
node-version: '${{ matrix.node-version }}'
cache: 'npm'
- name: 'Install 📦'
run: 'npm ci'
- name: 'Test 🧪'
run: 'npm run test'

e2e:
runs-on: 'ubuntu-latest'
needs:
- 'build'
steps:
- name: 'Setup node 🛎️'
uses: 'dudeofawesome/actions/node-setup@a850bdff4354bdb891a86116f0378f8a890e2c16' # v1
- name: 'Scaffold & lint a sample project 🧪'
run: 'scripts/e2e-create-configs.sh'

publish:
runs-on: 'ubuntu-latest'
environment: 'production'
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20
22
2 changes: 0 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@
}
],
"material-icon-theme.files.associations": {
"eslint-config*/*.yaml": "eslint",
"styleint-config/*.yaml": "stylelint",
"typescript-configs/**/*.json": "tsconfig",
"javascript-configs/**/*.json": "jsconfig"
},
"yaml.schemas": {
"https://json.schemastore.org/eslintrc": ["eslint-config*/**/*.yaml"],
"https://json.schemastore.org/stylelintrc.json": ["stylelint-config/*.yaml"]
},
"cSpell.words": ["esmodule"]
Expand Down
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
# Contributing

## Release lines & dist-tags

- **`main` carries the 3.x line** (ESLint 10, flat config). While 3.x is in
prerelease (`3.0.0-N`), every workspace `package.json` must carry
`"publishConfig": { "tag": "next" }` so prereleases don't capture the
`latest` dist-tag — the publish action runs a bare `npm publish`.
Remove that field in the commit that cuts the stable `3.0.0`.
- **The 2.x line is frozen** at `2.0.0-35` (eslintrc, ESLint 8). If a fix is
ever needed there, branch `v2.x` from the `v2.0.0-35` tag.
- Verify after each publish: `npm view @code-style/eslint-config dist-tags`.

## Authoring a new release

1. Author your code changes.
1. Commit your changes.
1. Run [`scripts/bump.ts`](scripts/bump.ts) to bump all package versions.
**Only run this on `main` when you intend to release** — CI publishes any
pushed branch whose version is new (gated by the `production` environment
approval).
1. Push your changes!

## Updating dependencies across workspaces / packages
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

See [create-configs](/packages/create-configs/README.md) for usage information.

> **v3** targets **ESLint 10** with native flat configs (`eslint.config.mjs`).
> Coming from a 2.x (`.eslintrc`-based) setup? See
> [docs/migrating-v2-to-v3.md](/docs/migrating-v2-to-v3.md).

## Contributing

See [`CONTRIBUTING.md`](./CONTRIBUTING.md)
2 changes: 0 additions & 2 deletions code-style.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,11 @@
},
],
"material-icon-theme.files.associations": {
"eslint-config*/*.yaml": "eslint",
"styleint-config/*.yaml": "stylelint",
"typescript-configs/**/*.json": "tsconfig",
"javascript-configs/**/*.json": "jsconfig",
},
"yaml.schemas": {
"https://json.schemastore.org/eslintrc": ["eslint-config*/**/*.yaml"],
"https://json.schemastore.org/stylelintrc.json": [
"stylelint-config/*.yaml",
],
Expand Down
117 changes: 117 additions & 0 deletions docs/migrating-v2-to-v3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Migrating from @code-style 2.x to 3.x

3.x is the ESLint 10 / flat config release. It replaces every `.eslintrc.*`
file with a single `eslint.config.mjs` and drops support for ESLint 8/9.

## Who this affects

Anyone depending on `@code-style/*` at `^2.0.0-N` — those repos use the
eslintrc-format configs on ESLint 8. Nothing forces you to move: the 2.x line
keeps working (frozen, no further releases), and `^2.0.0-35`-style ranges will
never resolve to 3.x.

## Requirements

- **Node `^20.19.0 || ^22.13.0 || >=24.0.0`** (ESLint 10's floor)
- **ESLint 10** — installed as a direct devDependency now

## The easy path: re-run the scaffolder

```sh
npx @code-style/create-configs@next
```

With `overwrite: true` it deletes your `.eslintrc.yaml` (and `.eslintignore`)
and writes `eslint.config.mjs`.

> **Warning:** any repo-local rule overrides living in your `.eslintrc.yaml`
> are deleted with it. Copy them out first, then port them into
> `eslint.config.mjs` as a trailing config object:
>
> ```js
> export default defineConfig(base, node, typescript, {
> rules: { 'some-rule': 'off' },
> });
> ```

## Manual migration

1. Replace your `.eslintrc.yaml` with an `eslint.config.mjs` composing the
same layers, in the same order:

```js
import { defineConfig } from 'eslint/config';

import base from '@code-style/eslint-config';
import node from '@code-style/eslint-config-node';
import typescript from '@code-style/eslint-config-typescript';
import esmodule from '@code-style/eslint-config-esmodule';

export default defineConfig(base, node, typescript, esmodule);
```

Lenient variants are separate subpath imports layered after their base:
`import node_lenient from '@code-style/eslint-config-node/lenient'`.

2. Delete `.eslintignore` if you have one (unsupported in flat config); move
its patterns into your config:
`defineConfig(globalIgnores(['generated/']), base, …)`.

3. Package changes:

```sh
npm rm @rushstack/eslint-patch @types/eslint eslint-plugin-import \
@typescript-eslint/eslint-plugin @typescript-eslint/parser
npm i -D eslint @code-style/eslint-config@next # …and your other layers
```

4. Drop `--ext` from lint scripts — `eslint . --cache` is all you need (the
configs' `files` globs cover `.ts`/`.tsx`/`.json` etc.).

5. Update engines/CI to the Node floor above.

## Rule & behavior changes to expect

Rule *content* was preserved, but the forced plugin major bumps change some
behavior:

- **ESLint 10 `eslint:recommended` additions**: `no-useless-assignment`,
`no-unassigned-vars`, `preserve-caught-error`, `no-empty-static-block`,
`no-unused-private-class-members`, core `no-loss-of-precision`.
- **typescript-eslint 8** (strict/stylistic-type-checked additions & renames):
`ban-types` split into `no-empty-object-type` / `no-unsafe-function-type` /
`no-wrapper-object-types`; `no-throw-literal` → `only-throw-error`;
`no-var-requires` → `no-require-imports`; new `no-deprecated`,
`no-misused-spread`, `no-unnecessary-type-parameters`,
`no-unnecessary-type-conversion`, `related-getter-setter-pairs`, and more.
`switch-exhaustiveness-check` is pinned to the v7 semantics (a `default`
clause satisfies it).
- **eslint-plugin-import → import-x**: same `import/*` rule IDs (we register
the plugin under the `import` namespace), slightly different resolver;
TypeScript resolution now goes through `eslint-import-resolver-typescript`.
- **eslint-plugin-n 18**: `no-unpublished-bin` left the recommended set; the
engine-version checks now follow your `engines.node`.
- **react/jsx-a11y/next**: recommended sets grew with their majors;
react-hooks stays pinned to `rules-of-hooks` + `exhaustive-deps`.

## Troubleshooting

- **VS Code shows eslintrc errors**: update the ESLint extension
(`dbaeumer.vscode-eslint` ≥3.0.10 auto-detects flat configs).
- **`Parsing error: "parserOptions.project" has been provided…`** on a config
or script file: that file isn't covered by your `tsconfig.json`. Config
files (`eslint.config.*`) are already exempted; for others, add them to a
tsconfig or lint them without type information.
- **Monorepos**: ESLint 10 looks for the nearest `eslint.config.*` starting
from each linted file's directory — nested configs now work naturally, and
a nested config *fully replaces* the root one for its subtree.
- **`-react` / `-nextjs` on npm install**: `eslint-plugin-react` and
`eslint-plugin-jsx-a11y` haven't added ESLint 10 to their peer ranges yet.
Until they do, add to your `package.json`:

```json
"overrides": {
"eslint-plugin-react": { "eslint": "$eslint" },
"eslint-plugin-jsx-a11y": { "eslint": "$eslint" }
}
```
42 changes: 30 additions & 12 deletions docs/upgrading-to-eslint-flat-config.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
# Upgrading to ESLint v9 flat configs
# ESLint flat config migration — status

## Dependencies that have yet to add support for flat configs
**Done.** As of v3, every `@code-style/eslint-config*` package is a native
[flat config](https://eslint.org/docs/latest/use/configure/configuration-files)
targeting **ESLint 10**. The eslintrc format is not supported by ESLint 10 (or
by these packages) anymore.

- [ ] [eslint-plugin-import / eslint-plugin-i](https://github.com/import-js/eslint-plugin-import/issues/2948)
- [x] [eslint-plugin-json-files](https://github.com/kellyselden/eslint-plugin-json-files) - no config provided
| Release line | Config format | ESLint | Status |
| --- | --- | --- | --- |
| 2.x (`2.0.0-N`) | `.eslintrc.*` | ^8.49 | frozen — no further releases planned |
| 3.x | flat (`eslint.config.mjs`) | ^10 | active |

Consumers migrating from 2.x: see [migrating-v2-to-v3.md](./migrating-v2-to-v3.md).

## Historical blockers (all since resolved upstream)

- [x] eslint-plugin-import — replaced with `eslint-plugin-import-x`
(registered under the `import` namespace, so rule IDs are unchanged)
- [x] eslint-plugin-json-files — works as a rule bag + explicit processor
(with a small ESLint 10 context shim; see
`packages/eslint-config/src/overrides/json.ts`)
- [x] eslint-plugin-prettier
- [ ] [eslint-plugin-promise](https://github.com/eslint-community/eslint-plugin-promise/issues/449)
- [x] eslint-plugin-jest
- [x] eslint-plugin-n
- [ ] [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/891)
- [x] eslint-plugin-react
- [ ] [eslint-plugin-react-hooks](https://github.com/facebook/react/issues/28313)
- [x] @typescript-eslint/\*
- [x] [@eslint-community/eslint-plugin-eslint-comments](https://github.com/eslint-community/eslint-plugin-eslint-comments/issues/182)
- [x] eslint-plugin-promise — v7
- [x] eslint-plugin-jest — v29
- [x] eslint-plugin-n — v18
- [x] eslint-plugin-jsx-a11y — flat configs shipped in 6.10 (ESLint 10
support still pending upstream; we shim the removed context methods)
- [x] eslint-plugin-react — flat configs shipped in 7.35+ (ESLint 10 support
still pending upstream; we shim the removed context methods)
- [x] eslint-plugin-react-hooks — v7 (we pin the classic rule pair; adopting
the React Compiler rules is a deliberate future change)
- [x] @typescript-eslint/\* — v8 via the `typescript-eslint` meta-package
- [x] @eslint-community/eslint-plugin-eslint-comments — `/configs` subpath
34 changes: 34 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { defineConfig, globalIgnores } from 'eslint/config';

import base from '@code-style/eslint-config';
import node from '@code-style/eslint-config-node';
import cli from '@code-style/eslint-config-cli';
import jest from '@code-style/eslint-config-jest';
import typescript from '@code-style/eslint-config-typescript';
import esmodule from '@code-style/eslint-config-esmodule';

export default defineConfig(
globalIgnores([
'.prettierrc.mjs',
'**/test/*/**',
// these packages lint themselves with their own eslint.config.mjs
'packages/code-style/',
'packages/create-configs/',
'packages/eslint-config/',
'packages/eslint-config-typescript/',
'packages/eslint-plugin-nest/',
'packages/jest-configs/',
'utils/',
]),
base,
node,
cli,
jest,
typescript,
esmodule,
{
// flat config modules export their config arrays as default exports
files: ['packages/*/src/**'],
rules: { 'import/no-default-export': 'off' },
},
);
Loading
Loading