Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c7ed0ce
chore: flat-config workspace, node:test, changesets, GitHub Actions
Nov 10, 2026
b55b0f4
Merge task 1: workspace scaffold
Nov 10, 2026
c9139b1
test: lint helper for config fixtures
Nov 10, 2026
d9f49d6
Merge task 2: lint helper
Nov 10, 2026
9b19c53
feat(base): flat-config JS ruleset
Nov 10, 2026
743baf2
Merge task 3: base JS config
Nov 10, 2026
c8004c9
feat(base): TypeScript config
Nov 10, 2026
c5485d2
Merge task 4: base TypeScript config
Nov 10, 2026
d3d1116
feat(base): node config
Nov 10, 2026
c358e4b
Merge task 5: node config
Nov 10, 2026
c4f3884
feat(react): React JS and TypeScript configs
Nov 10, 2026
45fb573
Merge task 6: react configs
Nov 10, 2026
d7a8629
docs: rule comparison table
Nov 10, 2026
ae034ff
Merge task 7: rule comparison table
Nov 10, 2026
7ea93e3
docs: resolve rule table per file
Nov 10, 2026
08c05c3
Merge: rule table resolves rules per file
Nov 10, 2026
83fd2b5
feat: apply rule review decisions
Nov 10, 2026
67d825e
Merge task 8: rule review decisions
Nov 10, 2026
9fa123f
docs: usage, migration, 8.0.0 changeset
Nov 10, 2026
ff3c5b3
chore: commit lockfile so changesets and npm ci work
Nov 10, 2026
562415f
Merge task 9: docs, migration, changeset
Nov 10, 2026
34e2ef8
fix(base): keep TS-only rule objects off JS files matched by files
Nov 10, 2026
52328ca
Merge: JS files in TS config
Nov 10, 2026
c007462
fix(base): node globals for scripts/ in every config; migration note …
Nov 10, 2026
5e065ee
Merge: scripts globals, migration note
Nov 10, 2026
896cba3
feat: per-plugin rule files, completeness tests, review fixes
Nov 10, 2026
798d419
feat: zero-option default exports, project service defaults
Nov 10, 2026
a2db234
feat: untyped tests/configs by default, typedTests option; formatjs p…
Nov 10, 2026
490e505
feat: package-root TS files untyped, import-x/first off in tests
Nov 10, 2026
21e0c8d
feat(base): node-js entry
Nov 10, 2026
5d3753f
chore: dogfood base config, peer floors + pack smoke scripts, package…
Nov 10, 2026
c8146a1
docs, CI matrix + peer floors + smoke, release provenance, rule table…
Nov 10, 2026
03e8102
Merge task 10: per-plugin rules, completeness tests, zero-option defa…
Nov 10, 2026
8de3380
docs: spec amendments
Nov 10, 2026
e9186b0
test: disable typescript-estree single-run inference (CI=true cached …
Nov 10, 2026
ec23fc0
ci: dry-run publish with pending changesets; docs: RELEASING.md
Nov 10, 2026
d00bc99
refactor: four packages (base, typescript, react, react-typescript)
Nov 10, 2026
912d479
docs: comment block (description, reasoning, URL) per rule; regenerat…
Nov 10, 2026
7085231
Merge task 11: four packages, documented rules
Nov 10, 2026
9d5d88b
test: every rule has a comment block (description, reasoning, URL) an…
Nov 10, 2026
21a2519
Merge task 12: rule documentation check
Nov 10, 2026
410fe70
chore: drop Node 20 (engines ^22.13 || >=24, CI on 22.13, 24, 26)
Nov 10, 2026
73d4144
test: generic names instead of vitest examples in fixtures and docs
Nov 10, 2026
ffc5aa4
chore: address PR review (drop plans/specs, MIGRATION, rule table; on…
Nov 10, 2026
9d0b29f
chore: unpublished packages start at 0.0.0; changesets (fixed group) …
Nov 10, 2026
027d038
Merge task 13: PR review fixes
Nov 10, 2026
d4f22e5
fix: react-typescript depends on typescript ^0.0.0 until changesets b…
Nov 10, 2026
dbc496d
chore: address PR review (drop decisions tests, keep bug guards; CI o…
Nov 10, 2026
fceca71
Merge task 14: PR review round 2
Nov 10, 2026
877e88c
fix: node entry parses ESM and CJS (no module-type detection), pin ac…
Nov 10, 2026
2371468
Merge task 15: ponytail review fixes
Nov 10, 2026
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
16 changes: 16 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"fixed": [
[
"@cospired/eslint-config-base",
"@cospired/eslint-config-typescript",
"@cospired/eslint-config-react",
"@cospired/eslint-config-react-typescript"
]
]
}
21 changes: 21 additions & 0 deletions .changeset/flat-config-rewrite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@cospired/eslint-config-base': major
'@cospired/eslint-config-typescript': major
'@cospired/eslint-config-react': major
'@cospired/eslint-config-react-typescript': major
---

Rewrite for ESLint 10 flat config, replacing airbnb and the hapi plugin.

Four packages: `base` (JavaScript, plus `/node`), `typescript` (plus `/node`), `react` and `react-typescript`. `base` and `react` keep their names; `typescript` and `react-typescript` are new, so JavaScript-only projects no longer need the TypeScript peers. Every entry works with no options: `export { default } from '@cospired/eslint-config-typescript/node';` is a complete `eslint.config.js`.

Migrating from 7.x:

- **Flat config only, ESLint 10, Node `^22.13 || >=24`.** `.eslintrc` is gone; use `eslint.config.js`.
- **No airbnb, no hapi.** The airbnb rules we kept are documented in the rule files (`src/rules/`); the rest are dropped. hapi `scope-start` cannot coexist with Prettier and `no-arrowception` is dropped. Blank lines before `return` and after declarations come from `@stylistic/padding-line-between-statements`.
- **Every rule is configured** (on, or off with a reason) for ESLint, typescript-eslint, import-x, @stylistic, eslint-comments, todo-plz, react-hooks, react-refresh and formatjs. Each rule has a comment block with description, reasoning and link. Rules left off that you want can be turned on in your own config.
- **No `warn` severities:** every rule is `error` or off.
- **Peers changed:** `@hapi/eslint-plugin`, `eslint-plugin-import`, `eslint-plugin-eslint-comments`, `eslint-plugin-react` and `eslint-plugin-jsx-a11y` are no longer used. New: `eslint-plugin-import-x`, `@eslint-community/eslint-plugin-eslint-comments`, `eslint-plugin-todo-plz`, `@stylistic/eslint-plugin`, `typescript-eslint`, `eslint-import-resolver-typescript`, `eslint-plugin-react-hooks`, `eslint-plugin-react-refresh`, `eslint-plugin-formatjs`.
- **React:** the `/hooks` export is gone (hooks rules are part of the default). jsx-a11y returns when the plugin supports ESLint 10.
- **Type-aware linting:** source files are linted with type information through typescript-eslint's project service and must belong to a tsconfig. Tests, scripts and TypeScript files at the package root (bundler, test runner or ORM configs) are linted without type information because build tsconfigs usually exclude them; `typedTests: true` type-checks those too (they must then be in a tsconfig).
- **Rules you will notice:** disable comments need `-- reason`; TODOs need a ticket (`#123`); imports are alphabetized; `no-console` allows only `warn`/`error`; `eqeqeq`, `no-shadow`, `no-nested-ternary`, `prefer-template` and `no-await-in-loop` are errors; TypeScript source files need explicit return types.
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
# Keeps the SHA-pinned actions in .github/workflows up to date.
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
72 changes: 72 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: CI

on:
push:
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: test (node ${{ matrix.node }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [22.13.0, 24, 26, latest]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test
- run: npm run lint

peer-floors:
name: peer dependency floors
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: node scripts/peer-floors.js
- run: npm test

smoke:
name: packed tarballs work in a clean project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run smoke

publish-dry-run:
name: publish dry run (pending changesets applied)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: npm
registry-url: https://registry.npmjs.org
- run: npm ci
- name: Apply pending changesets and dry-run publish
run: |
if [ -z "$(find .changeset -name '*.md' ! -name README.md)" ]; then
echo "No pending changesets: nothing to publish."
exit 0
fi
npx changeset version
npm publish --dry-run --workspaces --access public
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Release

on:
push:
branches: [main]

concurrency:
group: release
cancel-in-progress: false

permissions:
contents: write
pull-requests: write
id-token: write # npm provenance

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: npm
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm test
- run: npm run lint
- uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
with:
version: npm run version
publish: npm run release
title: 'chore: version packages'
commit: 'chore: version packages'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: 'true'
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules
.DS_Store
.superpowers
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

36 changes: 0 additions & 36 deletions .travis.yml

This file was deleted.

30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
[![Build Status](https://travis-ci.com/cospired/javascript.svg?branch=master)](https://travis-ci.com/cospired/javascript)
# @cospired/javascript

# javascript
basic packages for JS development
Slightly opinionated ESLint flat configs (ESLint 10) for JavaScript and TypeScript.

| Package | Entries | For |
|---|---|---|
| [`@cospired/eslint-config-base`](packages/eslint-config-base) | `.`, `./node` | JavaScript, Node.js JavaScript |
| [`@cospired/eslint-config-typescript`](packages/eslint-config-typescript) | `.`, `./node` | TypeScript (type-aware), Node.js TypeScript |
| [`@cospired/eslint-config-react`](packages/eslint-config-react) | `.` | React JavaScript |
| [`@cospired/eslint-config-react-typescript`](packages/eslint-config-react-typescript) | `.` | React TypeScript |

Every entry works with no options: `export { default } from '@cospired/eslint-config-typescript/node';` is a complete `eslint.config.js`.

Every rule is documented in the rule files under `packages/*/src/rules/`: a comment block with the description, the reasoning and a link.

## Development

```sh
npm install
npm test # node:test over every package and the test helpers
npm run lint # the repo lints itself with its own config
npm run smoke # pack the packages, install them in a clean project, lint with one-line configs
npx changeset # describe a change for the next release
```

Releases are cut by the changesets GitHub Action on `main`.

CI runs the tests on Node 22.13, 24, 26 and the latest release, again with every peer dependency at its minimum version, and the pack smoke test.
18 changes: 18 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Releasing

Releases are cut by the `Release` workflow (`.github/workflows/release.yml`) with changesets.

## One-time setup

1. Give the repo an npm credential that can publish to the `@cospired` scope, either:
- a repo secret `NPM_TOKEN` (npm automation token), or
- npm trusted publishing: on npmjs.com add this repo and the `release.yml` workflow as a trusted publisher for both packages (then no token is needed).
2. Allow GitHub Actions to create pull requests (Settings, Actions, General, Workflow permissions).

## Each release

1. Describe the change with `npx changeset` (CI's "publish dry run" job applies pending changesets and dry-runs the publish on every push).
2. Merge to `main`. The workflow opens a "chore: version packages" PR that bumps versions and changelogs.
3. Merge that PR. The workflow publishes to npm with provenance and tags the release.

`npm run smoke` packs both packages and lints a clean project with every entry; CI runs it too.
17 changes: 17 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import nodeJs from '@cospired/eslint-config-base/node';

// The repo lints itself with the config it ships.
export default [
...nodeJs,
{ ignores: ['**/fixtures/**', '.superpowers/**'] },
{
// Test helpers import the plugins they check; they are devDependencies of the repo.
files: ['test-utils/**'],
rules: { 'import-x/no-extraneous-dependencies': ['error', { devDependencies: true }] },
},
{
// Tests and scripts import the shared helpers through relative paths across package boundaries.
files: ['**/test/**', 'test-utils/**', 'scripts/**'],
rules: { 'import-x/no-relative-packages': 'off', 'no-await-in-loop': 'off' },
},
];
Loading
Loading