mirror of
https://github.com/acamarata/eslint-config.git
synced 2026-06-30 18:54:26 +00:00
chore: scaffold @acamarata/eslint-config 0.1.0
This commit is contained in:
commit
3736b98711
15 changed files with 3596 additions and 0 deletions
15
.editorconfig
Normal file
15
.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{c,h}]
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
110
.github/workflows/ci.yml
vendored
Normal file
110
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22, 24]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Test
|
||||
run: node --test test/test.mjs
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Coverage
|
||||
run: node --test --experimental-test-coverage test/test.mjs
|
||||
|
||||
pack-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Pack
|
||||
run: npm pack
|
||||
|
||||
- name: Verify pack contents
|
||||
run: |
|
||||
tar -tf *.tgz | sort
|
||||
tar -tf *.tgz | grep -q "package/dist/index.cjs" || (echo "MISSING: dist/index.cjs" && exit 1)
|
||||
tar -tf *.tgz | grep -q "package/dist/index.mjs" || (echo "MISSING: dist/index.mjs" && exit 1)
|
||||
tar -tf *.tgz | grep -q "package/dist/index.d.ts" || (echo "MISSING: dist/index.d.ts" && exit 1)
|
||||
tar -tf *.tgz | grep -q "package/README.md" || (echo "MISSING: README.md" && exit 1)
|
||||
tar -tf *.tgz | grep -q "package/LICENSE" || (echo "MISSING: LICENSE" && exit 1)
|
||||
echo "Pack check passed"
|
||||
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
node_modules/
|
||||
dist/
|
||||
*.tgz
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
.env.*
|
||||
.claude/
|
||||
.vscode/*
|
||||
.idea/
|
||||
.codex/
|
||||
.cursor/
|
||||
.aider/
|
||||
.aider.chat.history.md
|
||||
.continue/
|
||||
.windsurf/
|
||||
.gemini/
|
||||
.codeium/
|
||||
0
.npmrc
Normal file
0
.npmrc
Normal file
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
24
|
||||
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.0] - 2026-05-28
|
||||
|
||||
### Added
|
||||
|
||||
- Initial package scaffold with four named flat-config exports: `base`, `typescript`, `react`, `node`.
|
||||
- `base`: core rules for all JS/TS files (prefer-const, no-var, eqeqeq, no-unused-vars).
|
||||
- `typescript`: extends base with @typescript-eslint/recommended rules, noExplicitAny error, explicit-module-boundary-types.
|
||||
- `react`: extends typescript with react-hooks/exhaustive-deps, react-hooks/rules-of-hooks, jsx-key.
|
||||
- `node`: extends base with no-console off (appropriate for server/CLI code), no-process-exit error.
|
||||
- Dual CJS/ESM distribution via tsup.
|
||||
- Node.js test runner test suite validating flat-config shape for all four exports.
|
||||
- CI workflow: Node 20/22/24 matrix, corepack, test, typecheck, coverage, pack-check jobs.
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Aric Camarata
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
126
README.md
Normal file
126
README.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# @acamarata/eslint-config
|
||||
|
||||
[](https://www.npmjs.com/package/@acamarata/eslint-config)
|
||||
[](https://github.com/acamarata/eslint-config/actions)
|
||||
[](./LICENSE)
|
||||
|
||||
Shared ESLint flat configs for acamarata packages. Four named exports covering common project shapes: base, TypeScript, React, and Node.js.
|
||||
|
||||
All configs use ESLint's flat config format (eslint.config.js). ESLint 8's legacy `.eslintrc` format is not supported.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
pnpm add -D @acamarata/eslint-config eslint eslint-config-prettier
|
||||
```
|
||||
|
||||
For TypeScript projects, also install the parser and plugin:
|
||||
|
||||
```sh
|
||||
pnpm add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
|
||||
```
|
||||
|
||||
For React projects, add the React plugins as well:
|
||||
|
||||
```sh
|
||||
pnpm add -D eslint-plugin-react eslint-plugin-react-hooks
|
||||
```
|
||||
|
||||
## Peer dependencies
|
||||
|
||||
| Peer | Required for | Version |
|
||||
|------|-------------|---------|
|
||||
| `eslint` | all configs | `>=9.0.0` |
|
||||
| `eslint-config-prettier` | all configs | `>=9.0.0` |
|
||||
| `@typescript-eslint/eslint-plugin` | `typescript` config | `>=8.0.0` |
|
||||
| `@typescript-eslint/parser` | `typescript` config | `>=8.0.0` |
|
||||
| `eslint-plugin-react` | `react` config | `>=7.0.0` (optional) |
|
||||
| `eslint-plugin-react-hooks` | `react` config | `>=5.0.0` (optional) |
|
||||
|
||||
## Usage
|
||||
|
||||
### Base (JS only)
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import { base } from '@acamarata/eslint-config';
|
||||
|
||||
export default [...base];
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||
import { typescript } from '@acamarata/eslint-config';
|
||||
|
||||
export default [
|
||||
{
|
||||
plugins: { '@typescript-eslint': tsPlugin },
|
||||
languageOptions: { parser: tsParser },
|
||||
},
|
||||
...typescript,
|
||||
];
|
||||
```
|
||||
|
||||
### React
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||
import reactPlugin from 'eslint-plugin-react';
|
||||
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||
import { react } from '@acamarata/eslint-config';
|
||||
|
||||
export default [
|
||||
{
|
||||
plugins: {
|
||||
'@typescript-eslint': tsPlugin,
|
||||
react: reactPlugin,
|
||||
'react-hooks': reactHooksPlugin,
|
||||
},
|
||||
languageOptions: { parser: tsParser },
|
||||
},
|
||||
...react,
|
||||
];
|
||||
```
|
||||
|
||||
### Node.js
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import { node } from '@acamarata/eslint-config';
|
||||
|
||||
export default [...node];
|
||||
```
|
||||
|
||||
## Exported configs
|
||||
|
||||
| Export | Extends | Key rules |
|
||||
|--------|---------|-----------|
|
||||
| `base` | none | prefer-const, no-var, eqeqeq, no-unused-vars, no-console warn |
|
||||
| `typescript` | base | @typescript-eslint/no-explicit-any error, explicit-module-boundary-types, prefer-optional-chain |
|
||||
| `react` | typescript | react-hooks/exhaustive-deps error, react-hooks/rules-of-hooks error, jsx-key error |
|
||||
| `node` | base | no-console off (server/CLI code may log), no-process-exit error |
|
||||
|
||||
## TypeScript strict mode
|
||||
|
||||
The `typescript` config sets `@typescript-eslint/no-explicit-any: 'error'`. Every `any` cast in your codebase needs an inline comment explaining why it is necessary. This is intentional: it keeps the type surface honest.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Node.js 20, 22, 24
|
||||
- ESLint 9 flat config only
|
||||
- TypeScript 5.x
|
||||
|
||||
## Related packages
|
||||
|
||||
- [@acamarata/tsconfig](https://github.com/acamarata/tsconfig) - shared TypeScript configs
|
||||
- [@acamarata/prettier-config](https://github.com/acamarata/prettier-config) - shared Prettier config
|
||||
|
||||
## License
|
||||
|
||||
MIT. See [LICENSE](./LICENSE).
|
||||
87
package.json
Normal file
87
package.json
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"name": "@acamarata/eslint-config",
|
||||
"version": "0.1.0",
|
||||
"description": "Shared ESLint flat configs for acamarata packages. TypeScript-aware, Prettier-compatible, with base, browser, node, and React variants.",
|
||||
"author": "Aric Camarata",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"pretest": "tsup",
|
||||
"test": "node --test test/test.mjs",
|
||||
"coverage": "node --test --experimental-test-coverage test/test.mjs",
|
||||
"prepublishOnly": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": ">=8.0.0",
|
||||
"@typescript-eslint/parser": ">=8.0.0",
|
||||
"eslint": ">=9.0.0",
|
||||
"eslint-config-prettier": ">=9.0.0",
|
||||
"eslint-plugin-react": ">=7.0.0",
|
||||
"eslint-plugin-react-hooks": ">=5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"eslint-plugin-react": {
|
||||
"optional": true
|
||||
},
|
||||
"eslint-plugin-react-hooks": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-react": "^7.0.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"tsup": "^8.4.0",
|
||||
"typescript": "^5.8.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/acamarata/eslint-config.git"
|
||||
},
|
||||
"homepage": "https://github.com/acamarata/eslint-config#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/acamarata/eslint-config/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"eslint",
|
||||
"eslint-config",
|
||||
"typescript",
|
||||
"react",
|
||||
"node",
|
||||
"flat-config",
|
||||
"acamarata",
|
||||
"linting"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"packageManager": "pnpm@10.11.1"
|
||||
}
|
||||
2979
pnpm-lock.yaml
Normal file
2979
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
109
src/index.ts
Normal file
109
src/index.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* Purpose: Shared ESLint flat configs for acamarata packages.
|
||||
* Inputs: none — import the named export you need in your eslint.config.js
|
||||
* Outputs: ESLint flat config objects (Linter.Config[]) for base, typescript, react, and node variants
|
||||
* Constraints: Requires ESLint >=9, @typescript-eslint >=8, eslint-config-prettier >=9.
|
||||
* React and node variants are opt-in; import only what you use.
|
||||
* SPORT: packages.md — @acamarata/eslint-config
|
||||
*/
|
||||
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
/** Baseline rules that apply to all JavaScript and TypeScript files. */
|
||||
export const base: Linter.Config[] = [
|
||||
{
|
||||
rules: {
|
||||
// Disallow console in library code — callers decide logging strategy.
|
||||
// WHY: libraries that log unconditionally pollute user output.
|
||||
'no-console': 'warn',
|
||||
// Prefer const — immutable bindings communicate intent clearly.
|
||||
'prefer-const': 'error',
|
||||
// Disallow var — block-scoped let/const prevents hoisting bugs.
|
||||
'no-var': 'error',
|
||||
// Require === — loose equality hides type coercion bugs.
|
||||
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
||||
// Curly braces for all control-flow bodies — prevents off-by-one errors.
|
||||
curly: ['error', 'all'],
|
||||
// Disallow unused variables to keep the surface clean.
|
||||
'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Purpose: TypeScript-aware rules. Extends base + @typescript-eslint/recommended + Prettier compat.
|
||||
* Inputs: @typescript-eslint/eslint-plugin and @typescript-eslint/parser must be installed.
|
||||
* Outputs: Linter.Config[] — spread into your eslint.config.js after the parser config.
|
||||
* Constraints: Requires TypeScript project references or standalone tsconfig.json.
|
||||
* noExplicitAny is error — every `any` cast requires an inline justification comment.
|
||||
* SPORT: packages.md — @acamarata/eslint-config
|
||||
*/
|
||||
export const typescript: Linter.Config[] = [
|
||||
...base,
|
||||
{
|
||||
rules: {
|
||||
// Disable JS version; @typescript-eslint version handles TS specifics.
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
||||
],
|
||||
// Ban `any` — forces intentional unsafe casts with explicit justification.
|
||||
// WHY: unchecked `any` is the #1 source of runtime type errors in TS projects.
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
// Require return type annotations on exported functions.
|
||||
// WHY: public API surface should be explicitly typed; inference inside is fine.
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'error',
|
||||
// Prefer nullish coalescing over || for null/undefined checks.
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'warn',
|
||||
// Prefer optional chaining over manual null checks.
|
||||
'@typescript-eslint/prefer-optional-chain': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Purpose: React-specific rules. Adds eslint-plugin-react and react-hooks rules.
|
||||
* Inputs: eslint-plugin-react and eslint-plugin-react-hooks must be installed (peer deps).
|
||||
* Outputs: Linter.Config[] — spread into your eslint.config.js alongside the typescript config.
|
||||
* Constraints: Optional peer deps. Import this only in React projects.
|
||||
* SPORT: packages.md — @acamarata/eslint-config
|
||||
*/
|
||||
export const react: Linter.Config[] = [
|
||||
...typescript,
|
||||
{
|
||||
rules: {
|
||||
// Exhaustive deps prevent stale closure bugs in hooks.
|
||||
// WHY: missing deps in useEffect/useCallback are the most common React bug class.
|
||||
'react-hooks/exhaustive-deps': 'error',
|
||||
// Enforce hook call rules (top-level only, not inside conditionals).
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
// JSX key prop required in lists — prevents reconciliation bugs.
|
||||
'react/jsx-key': 'error',
|
||||
// Disallow index as key — masking actual identity causes subtle UI bugs.
|
||||
'react/no-array-index-key': 'warn',
|
||||
// Prevent prop-types — projects using TypeScript do not need them.
|
||||
'react/prop-types': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Purpose: Node.js-specific rules. Tightens rules appropriate for backend/CLI code.
|
||||
* Inputs: none — no additional peer deps beyond base.
|
||||
* Outputs: Linter.Config[] — spread into your eslint.config.js for Node-targeted files.
|
||||
* Constraints: Applies only to Node.js code; not appropriate for browser bundles.
|
||||
* SPORT: packages.md — @acamarata/eslint-config
|
||||
*/
|
||||
export const node: Linter.Config[] = [
|
||||
...base,
|
||||
{
|
||||
rules: {
|
||||
// Allow console in Node CLI/server code — logging is expected.
|
||||
// WHY: base bans console; node contexts legitimately need it.
|
||||
'no-console': 'off',
|
||||
// Disallow process.exit in library code — callers control lifecycle.
|
||||
'no-process-exit': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
71
test/test.mjs
Normal file
71
test/test.mjs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Test suite for @acamarata/eslint-config
|
||||
* Validates that each exported flat-config object has the correct shape.
|
||||
* A valid Linter.Config[] entry has a `rules` key (object).
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from 'node:test';
|
||||
import { base, typescript, react, node } from '../dist/index.mjs';
|
||||
|
||||
describe('@acamarata/eslint-config — flat-config shape', () => {
|
||||
it('base is an array', () => {
|
||||
assert.ok(Array.isArray(base), 'base must be an array');
|
||||
});
|
||||
|
||||
it('base contains at least one config object with a rules key', () => {
|
||||
const hasRules = base.some(cfg => cfg !== null && typeof cfg === 'object' && 'rules' in cfg);
|
||||
assert.ok(hasRules, 'base must contain at least one config with rules');
|
||||
});
|
||||
|
||||
it('base.rules.prefer-const is "error"', () => {
|
||||
const cfg = base.find(c => c.rules && 'prefer-const' in c.rules);
|
||||
assert.ok(cfg, 'base must define prefer-const');
|
||||
assert.strictEqual(cfg.rules['prefer-const'], 'error');
|
||||
});
|
||||
|
||||
it('typescript is an array extending base', () => {
|
||||
assert.ok(Array.isArray(typescript), 'typescript must be an array');
|
||||
assert.ok(typescript.length >= base.length, 'typescript must extend base');
|
||||
});
|
||||
|
||||
it('typescript config contains @typescript-eslint/no-explicit-any: "error"', () => {
|
||||
const cfg = typescript.find(c => c.rules && '@typescript-eslint/no-explicit-any' in c.rules);
|
||||
assert.ok(cfg, 'typescript must define @typescript-eslint/no-explicit-any');
|
||||
assert.strictEqual(cfg.rules['@typescript-eslint/no-explicit-any'], 'error');
|
||||
});
|
||||
|
||||
it('react is an array extending typescript', () => {
|
||||
assert.ok(Array.isArray(react), 'react must be an array');
|
||||
assert.ok(react.length >= typescript.length, 'react must extend typescript');
|
||||
});
|
||||
|
||||
it('react config contains react-hooks/exhaustive-deps: "error"', () => {
|
||||
const cfg = react.find(c => c.rules && 'react-hooks/exhaustive-deps' in c.rules);
|
||||
assert.ok(cfg, 'react must define react-hooks/exhaustive-deps');
|
||||
assert.strictEqual(cfg.rules['react-hooks/exhaustive-deps'], 'error');
|
||||
});
|
||||
|
||||
it('node is an array extending base', () => {
|
||||
assert.ok(Array.isArray(node), 'node must be an array');
|
||||
assert.ok(node.length >= base.length, 'node must extend base');
|
||||
});
|
||||
|
||||
it('node config sets no-console to "off" (final effective value)', () => {
|
||||
// node is [...base, nodeOverrides]. base sets no-console:'warn', nodeOverrides sets 'off'.
|
||||
// The last config object wins in flat config. Find the last one that defines no-console.
|
||||
const allWithNoConsole = node.filter(c => c.rules && 'no-console' in c.rules);
|
||||
assert.ok(allWithNoConsole.length > 0, 'node must define no-console');
|
||||
const last = allWithNoConsole[allWithNoConsole.length - 1];
|
||||
assert.strictEqual(last.rules['no-console'], 'off');
|
||||
});
|
||||
|
||||
it('all exports are plain objects (no class instances, no functions)', () => {
|
||||
for (const [name, cfg] of [['base', base], ['typescript', typescript], ['react', react], ['node', node]]) {
|
||||
for (const entry of cfg) {
|
||||
assert.strictEqual(typeof entry, 'object', `${name}: each entry must be a plain object`);
|
||||
assert.ok(entry !== null, `${name}: each entry must not be null`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "test"]
|
||||
}
|
||||
16
tsup.config.ts
Normal file
16
tsup.config.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['cjs', 'esm'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
outDir: 'dist',
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
target: 'es2022',
|
||||
platform: 'node',
|
||||
outExtension({ format }) {
|
||||
return { js: format === 'cjs' ? '.cjs' : '.mjs' };
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue