@syntopica/eslint-config 0.8.0
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/LICENSE +21 -0
- package/PUBLIC_API.md +47 -0
- package/README.md +106 -0
- package/eslint.config.ts +7 -0
- package/package.json +186 -0
- package/src/accessibility.ts +26 -0
- package/src/astro.ts +62 -0
- package/src/base.ts +252 -0
- package/src/code-quality-sonar.ts +27 -0
- package/src/code-quality.ts +119 -0
- package/src/data-files.ts +71 -0
- package/src/eslint-config-next.d.ts +2 -0
- package/src/frontend-boundaries.ts +92 -0
- package/src/nestjs.ts +25 -0
- package/src/nextjs.ts +85 -0
- package/src/node.ts +28 -0
- package/src/react-version.ts +37 -0
- package/src/tailwind.ts +64 -0
- package/src/testing.ts +35 -0
- package/src/vite-react.ts +64 -0
- package/src/vite-vue.ts +88 -0
- package/tsconfig.json +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) BusiRocket
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PUBLIC_API.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Public API (`@syntopica/eslint-config`)
|
|
2
|
+
|
|
3
|
+
Semver applies to **export subpaths** listed below. Import paths not listed here
|
|
4
|
+
are **private** and may change without a major bump.
|
|
5
|
+
|
|
6
|
+
## Stable exports (semver)
|
|
7
|
+
|
|
8
|
+
| Export subpath | Purpose |
|
|
9
|
+
| ---------------------------------------------- | ----------------------------------------------------------------- |
|
|
10
|
+
| `@syntopica/eslint-config/base` | Core JS/TS correctness, imports, promise, security, Prettier last |
|
|
11
|
+
| `@syntopica/eslint-config/nextjs` | Next.js App Router + React + frontend boundaries |
|
|
12
|
+
| `@syntopica/eslint-config/vite-react` | Vite + React + frontend boundaries |
|
|
13
|
+
| `@syntopica/eslint-config/astro` | Astro + TS + frontend boundaries |
|
|
14
|
+
| `@syntopica/eslint-config/node` | Node globals + unicorn prefer-node-protocol |
|
|
15
|
+
| `@syntopica/eslint-config/nestjs` | Node preset + NestJS decorator-aware rule tweaks |
|
|
16
|
+
| `@syntopica/eslint-config/code-quality` | Sonar + code-policy structural rules |
|
|
17
|
+
| `@syntopica/eslint-config/accessibility` | jsx-a11y |
|
|
18
|
+
| `@syntopica/eslint-config/tailwind` | Tailwind CSS plugin |
|
|
19
|
+
| `@syntopica/eslint-config/frontend-boundaries` | `eslint-plugin-boundaries` layer map only |
|
|
20
|
+
| `@syntopica/eslint-config/testing` | Vitest + Testing Library rules for test files |
|
|
21
|
+
|
|
22
|
+
Each entry resolves to **TypeScript source** (`*.ts`) published in the package.
|
|
23
|
+
Consumers load flat config with **ESM** and a TypeScript-aware runner (for
|
|
24
|
+
example `jiti` or your bundler) as shown in the package README.
|
|
25
|
+
|
|
26
|
+
## Implementation detail
|
|
27
|
+
|
|
28
|
+
Internal modules under `src/` that are not re-exported through the table above
|
|
29
|
+
are **not** public API.
|
|
30
|
+
|
|
31
|
+
## `publint`/`attw` findings that are expected, not defects
|
|
32
|
+
|
|
33
|
+
`publish:check` (`publint --strict && attw --pack . --profile node16`) ignores
|
|
34
|
+
two `attw` rules for this package: `cjs-resolves-to-esm` and
|
|
35
|
+
`internal-resolution-error`. Both are consequences of the no-build design above,
|
|
36
|
+
not resolution bugs:
|
|
37
|
+
|
|
38
|
+
- **`cjs-resolves-to-esm`** — every export subpath resolves to raw `.ts` ESM
|
|
39
|
+
source; there is no CommonJS build to satisfy a `require()` caller. Consumers
|
|
40
|
+
load these configs with ESM and a TypeScript-aware runtime (`jiti`), never
|
|
41
|
+
`require`.
|
|
42
|
+
- **`internal-resolution-error`** — subpaths such as `./nextjs` and `./astro`
|
|
43
|
+
import framework packages (`@next/eslint-plugin-next`, `eslint-plugin-astro`,
|
|
44
|
+
etc.) declared as **optional peer dependencies**. `attw`'s sandbox only
|
|
45
|
+
contains this package's own tarball, so it cannot see peers a real consumer
|
|
46
|
+
installs, and flags those imports as unresolved. This is the intended shape of
|
|
47
|
+
an optional peer, not a broken `exports` entry.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @syntopica/eslint-config
|
|
2
|
+
|
|
3
|
+
Flat ESLint configuration for JavaScript and TypeScript projects: shared
|
|
4
|
+
**base** rules plus optional layers for Next.js, Vite + React, Astro, Node,
|
|
5
|
+
Tailwind, accessibility, and code-quality (including
|
|
6
|
+
`eslint-plugin-code-policy`).
|
|
7
|
+
|
|
8
|
+
- **Public API (semver):** see [PUBLIC_API.md](./PUBLIC_API.md).
|
|
9
|
+
- **Platform decisions:**
|
|
10
|
+
[engineering-baseline/docs/platform-decisions.md](https://github.com/BusiRocket/engineering-baseline/blob/main/docs/platform-decisions.md).
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Node.js 20+
|
|
15
|
+
- ESLint 9+ (flat config)
|
|
16
|
+
- TypeScript 5.4+ for type-aware linting
|
|
17
|
+
|
|
18
|
+
Install `eslint` and `typescript` in your project, plus every package listed for
|
|
19
|
+
the subpaths you import - see [Stacks](#stacks).
|
|
20
|
+
|
|
21
|
+
This package ships **TypeScript source**, so each subpath's `import` statements
|
|
22
|
+
are resolved from your project, not from a build. Under pnpm's isolated
|
|
23
|
+
`node_modules` that means a plugin must be a direct dependency of the consumer
|
|
24
|
+
even when it is a `dependencies` entry here: a missing one fails `tsc --noEmit`
|
|
25
|
+
with `Cannot find module '<plugin>'` before ESLint ever runs. Optional peers are
|
|
26
|
+
listed in `package.json` under `peerDependencies` / `peerDependenciesMeta`.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add -D @syntopica/eslint-config@^0.1.0 eslint typescript
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Add peers for the stacks you use (React, Next.js, Astro, and so on). The
|
|
35
|
+
**base** stack ships several dependencies bundled with this package;
|
|
36
|
+
framework-specific plugins remain peers.
|
|
37
|
+
|
|
38
|
+
## New project
|
|
39
|
+
|
|
40
|
+
1. Add `@syntopica/eslint-config` and peers for your stack.
|
|
41
|
+
2. Add `eslint.config.ts` (or `.mjs`) using `jiti` / `tsx` / Node 22+ TypeScript
|
|
42
|
+
support as needed.
|
|
43
|
+
3. Follow
|
|
44
|
+
[docs/adoption/new-repo.md](https://github.com/BusiRocket/engineering-baseline/blob/main/docs/adoption/new-repo.md).
|
|
45
|
+
|
|
46
|
+
Minimal `eslint.config.ts` (Node / library):
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { createBaseConfig } from '@syntopica/eslint-config/base'
|
|
50
|
+
|
|
51
|
+
export default createBaseConfig({ tsconfigRootDir: import.meta.dirname })
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Next.js App Router: import `createNextjsConfig` from
|
|
55
|
+
`@syntopica/eslint-config/nextjs` and compose with `createBaseConfig` as in the
|
|
56
|
+
the Next.js template in
|
|
57
|
+
[engineering-baseline](https://github.com/BusiRocket/engineering-baseline/tree/main/templates/nextjs-app).
|
|
58
|
+
|
|
59
|
+
## Existing project
|
|
60
|
+
|
|
61
|
+
See
|
|
62
|
+
[docs/adoption/existing-repo.md](https://github.com/BusiRocket/engineering-baseline/blob/main/docs/adoption/existing-repo.md)
|
|
63
|
+
and
|
|
64
|
+
[docs/migration.md](https://github.com/BusiRocket/engineering-baseline/blob/main/docs/migration.md).
|
|
65
|
+
Migrate to flat config first, then layer `@syntopica/eslint-config`.
|
|
66
|
+
|
|
67
|
+
## Stacks
|
|
68
|
+
|
|
69
|
+
Install the packages in the right-hand column alongside the subpath you import.
|
|
70
|
+
The list is transitive: `/code-quality` also pulls in what `/testing` needs, and
|
|
71
|
+
`/nestjs` what `/node` needs.
|
|
72
|
+
|
|
73
|
+
| Import subpath | Use case | Install alongside |
|
|
74
|
+
| ---------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `/base` | Core TS/JS, imports, promise, security | `@eslint/js`, `eslint-config-prettier`, `eslint-plugin-import`, `eslint-import-resolver-typescript`, `eslint-plugin-promise`, `eslint-plugin-security`, `eslint-plugin-unused-imports`, `typescript-eslint` |
|
|
76
|
+
| `/nextjs` | Next.js + React + boundaries | `@next/eslint-plugin-next`, `eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-boundaries` |
|
|
77
|
+
| `/vite-react` | Vite + React + boundaries | `eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-react-refresh`, `eslint-plugin-boundaries` |
|
|
78
|
+
| `/vite-vue` | Vite + Vue | `eslint-plugin-vue`, `eslint-plugin-vuejs-accessibility`, `vue-eslint-parser`, `eslint-plugin-unused-imports`, `typescript-eslint` |
|
|
79
|
+
| `/astro` | Astro | `eslint-plugin-astro`, `typescript-eslint`, `eslint-plugin-boundaries` |
|
|
80
|
+
| `/node` | Node libraries | `eslint-plugin-unicorn`, `globals` |
|
|
81
|
+
| `/nestjs` | NestJS services | `eslint-plugin-unicorn`, `globals` |
|
|
82
|
+
| `/code-quality` | Sonar + code-policy | `eslint-plugin-code-policy`, `eslint-plugin-sonarjs`, `eslint-plugin-testing-library`, `@vitest/eslint-plugin` |
|
|
83
|
+
| `/testing` | Vitest + Testing Library | `@vitest/eslint-plugin`, `eslint-plugin-testing-library` |
|
|
84
|
+
| `/accessibility` | jsx-a11y | `eslint-plugin-jsx-a11y` |
|
|
85
|
+
| `/tailwind` | Tailwind plugin | `eslint-plugin-tailwindcss` |
|
|
86
|
+
| `/frontend-boundaries` | Boundaries only | `eslint-plugin-boundaries` |
|
|
87
|
+
|
|
88
|
+
`/code-quality` composes `/testing` unconditionally, so
|
|
89
|
+
`eslint-plugin-testing-library` is required there even in a project with no
|
|
90
|
+
tests.
|
|
91
|
+
|
|
92
|
+
## CLI helper
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm dlx @syntopica/create-baseline@^0.1.0 --check
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`--soft` prints install commands; `--hard` also requires `eslint.config.*` in
|
|
99
|
+
the repo root.
|
|
100
|
+
|
|
101
|
+
## Related
|
|
102
|
+
|
|
103
|
+
- **Plugin (rules only):** `eslint-plugin-code-policy` — full baseline is
|
|
104
|
+
composed here, not in the plugin.
|
|
105
|
+
- **Prettier / TS configs:** `@syntopica/prettier-config`,
|
|
106
|
+
`@syntopica/tsconfig`.
|
package/eslint.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@syntopica/eslint-config",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"packageManager": "pnpm@11.25.0",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"description": "Shared ESLint configuration baseline",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/BusiRocket/baseline.git",
|
|
11
|
+
"directory": "packages/eslint-config"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/BusiRocket/baseline/tree/main/packages/eslint-config#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/BusiRocket/baseline/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md",
|
|
27
|
+
"src",
|
|
28
|
+
"eslint.config.ts",
|
|
29
|
+
"tsconfig.json",
|
|
30
|
+
"PUBLIC_API.md"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
"./base": "./src/base.ts",
|
|
34
|
+
"./nextjs": "./src/nextjs.ts",
|
|
35
|
+
"./astro": "./src/astro.ts",
|
|
36
|
+
"./vite-react": "./src/vite-react.ts",
|
|
37
|
+
"./vite-vue": "./src/vite-vue.ts",
|
|
38
|
+
"./node": "./src/node.ts",
|
|
39
|
+
"./nestjs": "./src/nestjs.ts",
|
|
40
|
+
"./code-quality": "./src/code-quality.ts",
|
|
41
|
+
"./accessibility": "./src/accessibility.ts",
|
|
42
|
+
"./tailwind": "./src/tailwind.ts",
|
|
43
|
+
"./frontend-boundaries": "./src/frontend-boundaries.ts",
|
|
44
|
+
"./testing": "./src/testing.ts",
|
|
45
|
+
"./data-files": "./src/data-files.ts"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"fix": "pnpm run lint:fix",
|
|
49
|
+
"lint": "eslint src --max-warnings 0",
|
|
50
|
+
"lint:fix": "eslint src --fix",
|
|
51
|
+
"lint:suppress": "eslint src --suppress-all",
|
|
52
|
+
"lint:prune": "eslint src --prune-suppressions",
|
|
53
|
+
"type-check": "tsc --noEmit -p tsconfig.json",
|
|
54
|
+
"publish:check": "baseline-licenses && publint --strict && attw --pack . --profile node16 --ignore-rules cjs-resolves-to-esm internal-resolution-error"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@eslint/js": "^10.0.1",
|
|
58
|
+
"eslint-config-prettier": "^10.1.8",
|
|
59
|
+
"eslint-import-resolver-typescript": "^4.4.5",
|
|
60
|
+
"eslint-plugin-import": "^2.32.0",
|
|
61
|
+
"eslint-plugin-promise": "^7.3.0",
|
|
62
|
+
"eslint-plugin-security": "^4.0.1",
|
|
63
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
64
|
+
"typescript-eslint": "^8.68.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@eslint/markdown": ">=8.0.0",
|
|
68
|
+
"@next/eslint-plugin-next": ">=15.0.0",
|
|
69
|
+
"@vitest/eslint-plugin": ">=1.0.0",
|
|
70
|
+
"eslint": ">=9.0.0",
|
|
71
|
+
"eslint-config-next": ">=15.0.0",
|
|
72
|
+
"eslint-plugin-astro": ">=0.34.0",
|
|
73
|
+
"eslint-plugin-boundaries": ">=7.0.0",
|
|
74
|
+
"eslint-plugin-code-policy": ">=0.4.0",
|
|
75
|
+
"eslint-plugin-jsonc": ">=3.0.0",
|
|
76
|
+
"eslint-plugin-jsx-a11y": ">=6.8.0",
|
|
77
|
+
"eslint-plugin-react": ">=7.37.0",
|
|
78
|
+
"eslint-plugin-react-hooks": ">=5.0.0",
|
|
79
|
+
"eslint-plugin-react-refresh": ">=0.4.0",
|
|
80
|
+
"eslint-plugin-regexp": ">=2.7.0",
|
|
81
|
+
"eslint-plugin-sonarjs": ">=3.0.0",
|
|
82
|
+
"eslint-plugin-tailwindcss": "^4.2.0",
|
|
83
|
+
"eslint-plugin-testing-library": ">=7.0.0",
|
|
84
|
+
"eslint-plugin-unicorn": ">=57.0.0",
|
|
85
|
+
"eslint-plugin-vue": ">=10.0.0",
|
|
86
|
+
"eslint-plugin-vuejs-accessibility": ">=2.4.0",
|
|
87
|
+
"eslint-plugin-yml": ">=3.0.0",
|
|
88
|
+
"globals": ">=15.0.0",
|
|
89
|
+
"typescript": ">=5.4.0",
|
|
90
|
+
"vue-eslint-parser": ">=10.0.0"
|
|
91
|
+
},
|
|
92
|
+
"peerDependenciesMeta": {
|
|
93
|
+
"@next/eslint-plugin-next": {
|
|
94
|
+
"optional": true
|
|
95
|
+
},
|
|
96
|
+
"@vitest/eslint-plugin": {
|
|
97
|
+
"optional": true
|
|
98
|
+
},
|
|
99
|
+
"eslint-config-next": {
|
|
100
|
+
"optional": true
|
|
101
|
+
},
|
|
102
|
+
"eslint-plugin-astro": {
|
|
103
|
+
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"eslint-plugin-boundaries": {
|
|
106
|
+
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"eslint-plugin-code-policy": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"eslint-plugin-jsx-a11y": {
|
|
112
|
+
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"eslint-plugin-react": {
|
|
115
|
+
"optional": true
|
|
116
|
+
},
|
|
117
|
+
"eslint-plugin-react-hooks": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"eslint-plugin-react-refresh": {
|
|
121
|
+
"optional": true
|
|
122
|
+
},
|
|
123
|
+
"eslint-plugin-sonarjs": {
|
|
124
|
+
"optional": true
|
|
125
|
+
},
|
|
126
|
+
"eslint-plugin-tailwindcss": {
|
|
127
|
+
"optional": true
|
|
128
|
+
},
|
|
129
|
+
"eslint-plugin-testing-library": {
|
|
130
|
+
"optional": true
|
|
131
|
+
},
|
|
132
|
+
"eslint-plugin-unicorn": {
|
|
133
|
+
"optional": true
|
|
134
|
+
},
|
|
135
|
+
"eslint-plugin-vue": {
|
|
136
|
+
"optional": true
|
|
137
|
+
},
|
|
138
|
+
"eslint-plugin-vuejs-accessibility": {
|
|
139
|
+
"optional": true
|
|
140
|
+
},
|
|
141
|
+
"globals": {
|
|
142
|
+
"optional": true
|
|
143
|
+
},
|
|
144
|
+
"vue-eslint-parser": {
|
|
145
|
+
"optional": true
|
|
146
|
+
},
|
|
147
|
+
"@eslint/markdown": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"eslint-plugin-jsonc": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"eslint-plugin-yml": {
|
|
154
|
+
"optional": true
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"devDependencies": {
|
|
158
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
159
|
+
"@eslint/markdown": "^8.0.3",
|
|
160
|
+
"@next/eslint-plugin-next": "^16.3.3",
|
|
161
|
+
"@types/node": "^26.4.0",
|
|
162
|
+
"@vitest/eslint-plugin": "^1.6.27",
|
|
163
|
+
"eslint": "^10.9.1",
|
|
164
|
+
"eslint-plugin-astro": "^3.1.0",
|
|
165
|
+
"eslint-plugin-boundaries": "^7.2.0",
|
|
166
|
+
"eslint-plugin-code-policy": "workspace:*",
|
|
167
|
+
"eslint-plugin-jsonc": "^3.4.2",
|
|
168
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
169
|
+
"eslint-plugin-react": "^7.37.5",
|
|
170
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
171
|
+
"eslint-plugin-react-refresh": "^0.5.5",
|
|
172
|
+
"eslint-plugin-regexp": "^3.2.0",
|
|
173
|
+
"eslint-plugin-sonarjs": "^4.2.0",
|
|
174
|
+
"eslint-plugin-tailwindcss": "^4.4.0",
|
|
175
|
+
"eslint-plugin-testing-library": "^7.16.2",
|
|
176
|
+
"eslint-plugin-unicorn": "^74.0.0",
|
|
177
|
+
"eslint-plugin-vue": "^10.10.0",
|
|
178
|
+
"eslint-plugin-vuejs-accessibility": "^2.6.0",
|
|
179
|
+
"eslint-plugin-yml": "^3.8.1",
|
|
180
|
+
"globals": "^17.11.0",
|
|
181
|
+
"jiti": "^2.7.0",
|
|
182
|
+
"publint": "^0.3.24",
|
|
183
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
184
|
+
"vue-eslint-parser": "^10.4.1"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
2
|
+
const jsxA11y = require('eslint-plugin-jsx-a11y') as {
|
|
3
|
+
flatConfigs: { recommended: unknown; strict: unknown }
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Accessibility rules for JSX-based projects (React, Next.js, Astro).
|
|
8
|
+
* Enforces WCAG compliance at lint time: alt text, ARIA roles,
|
|
9
|
+
* keyboard navigation, form labels, and more.
|
|
10
|
+
*
|
|
11
|
+
* `strict`, not `recommended`. The two differ on the rules that catch the
|
|
12
|
+
* failures a sighted mouse user never sees: a `role` on an element that
|
|
13
|
+
* already has one, an interactive handler on a `div` with no keyboard
|
|
14
|
+
* equivalent, a label that is adjacent to its control rather than associated
|
|
15
|
+
* with it. `recommended` relaxes exactly those to keep large existing
|
|
16
|
+
* codebases green, which is the wrong trade for a baseline whose templates
|
|
17
|
+
* start at zero findings.
|
|
18
|
+
*
|
|
19
|
+
* Static analysis is half the gate: it reads the source, not the rendered
|
|
20
|
+
* tree, so a violation composed at runtime is invisible to it. The templates
|
|
21
|
+
* pair this with an axe assertion over the rendered component.
|
|
22
|
+
*
|
|
23
|
+
* Spread this as the final layer (after framework config, before architecture):
|
|
24
|
+
* ...createAccessibilityConfig()
|
|
25
|
+
*/
|
|
26
|
+
export const createAccessibilityConfig = () => [jsxA11y.flatConfigs.strict]
|
package/src/astro.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import astroPlugin from 'eslint-plugin-astro'
|
|
2
|
+
import { globalIgnores } from 'eslint/config'
|
|
3
|
+
import tseslint from 'typescript-eslint'
|
|
4
|
+
|
|
5
|
+
import { createFrontendBoundariesConfig } from './frontend-boundaries'
|
|
6
|
+
|
|
7
|
+
export type AstroConfigOptions = {
|
|
8
|
+
tsconfigRootDir?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type FlatConfigLike = Record<string, unknown>
|
|
12
|
+
const astroConfigs = astroPlugin.configs as unknown as Record<
|
|
13
|
+
string,
|
|
14
|
+
FlatConfigLike[]
|
|
15
|
+
>
|
|
16
|
+
const astroRecommended = astroConfigs['flat/recommended'] ?? []
|
|
17
|
+
const astroA11yRecommended = astroConfigs['flat/jsx-a11y-recommended'] ?? []
|
|
18
|
+
|
|
19
|
+
export const createAstroConfig = (options: AstroConfigOptions = {}) => {
|
|
20
|
+
const tsconfigRootDir = options.tsconfigRootDir ?? process.cwd()
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
globalIgnores(['dist/**', '.astro/**', 'coverage/**']),
|
|
24
|
+
...astroRecommended,
|
|
25
|
+
...astroA11yRecommended,
|
|
26
|
+
{
|
|
27
|
+
files: ['**/*.astro'],
|
|
28
|
+
languageOptions: {
|
|
29
|
+
parserOptions: {
|
|
30
|
+
parser: tseslint.parser,
|
|
31
|
+
project: true,
|
|
32
|
+
tsconfigRootDir,
|
|
33
|
+
extraFileExtensions: ['.astro'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
files: ['**/*.{ts,tsx}'],
|
|
39
|
+
languageOptions: {
|
|
40
|
+
parserOptions: {
|
|
41
|
+
projectService: true,
|
|
42
|
+
tsconfigRootDir,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
rules: {
|
|
46
|
+
'@typescript-eslint/no-confusing-void-expression': 'off',
|
|
47
|
+
'@typescript-eslint/unbound-method': 'off',
|
|
48
|
+
'@typescript-eslint/require-await': 'off',
|
|
49
|
+
'@typescript-eslint/no-misused-promises': [
|
|
50
|
+
'error',
|
|
51
|
+
{ checksVoidReturn: false },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
files: ['**/*.d.ts'],
|
|
57
|
+
rules: { '@typescript-eslint/triple-slash-reference': 'off' },
|
|
58
|
+
},
|
|
59
|
+
// Same layered boundaries as Next.js / Vite React (components, services, hooks, …)
|
|
60
|
+
...createFrontendBoundariesConfig(),
|
|
61
|
+
]
|
|
62
|
+
}
|