@santux/eslint-config 0.0.0 → 1.0.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/README.md +68 -28
- package/package.json +168 -6
- package/src/astro-react.d.ts +4 -0
- package/src/astro-react.js +6 -0
- package/src/astro.d.ts +4 -0
- package/src/astro.js +68 -0
- package/src/commitlint.d.ts +2 -0
- package/src/commitlint.js +8 -0
- package/src/format-astro.d.ts +4 -0
- package/src/format-astro.js +5 -0
- package/src/format-tailwind.d.ts +4 -0
- package/src/format-tailwind.js +4 -0
- package/src/format.d.ts +4 -0
- package/src/format.js +15 -0
- package/src/import-order.d.ts +1 -0
- package/src/import-order.js +17 -0
- package/src/lint.d.ts +4 -0
- package/src/lint.js +169 -0
- package/src/next.d.ts +4 -0
- package/src/next.js +18 -0
- package/src/react-vite.d.ts +4 -0
- package/src/react-vite.js +15 -0
- package/src/react.d.ts +6 -0
- package/src/react.js +37 -0
- package/src/tailwind.d.ts +4 -0
- package/src/tailwind.js +16 -0
- package/src/tanstack-start.d.ts +4 -0
- package/src/tanstack-start.js +22 -0
package/README.md
CHANGED
|
@@ -1,45 +1,85 @@
|
|
|
1
1
|
# @santux/eslint-config
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared ESLint, Prettier and commitlint config for TypeScript projects.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Entries
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
| Entry | Tool | Export |
|
|
8
|
+
| --------------------------------------- | ---------- | ----------------- |
|
|
9
|
+
| `@santux/eslint-config/lint` | ESLint 9 | Flat config array |
|
|
10
|
+
| `@santux/eslint-config/next` | ESLint 9 | Flat config array |
|
|
11
|
+
| `@santux/eslint-config/react-vite` | ESLint 9 | Flat config array |
|
|
12
|
+
| `@santux/eslint-config/tanstack-start` | ESLint 9 | Flat config array |
|
|
13
|
+
| `@santux/eslint-config/astro` | ESLint 9 | Flat config array |
|
|
14
|
+
| `@santux/eslint-config/astro-react` | ESLint 9 | Flat config array |
|
|
15
|
+
| `@santux/eslint-config/tailwind` | ESLint 9 | Flat config array |
|
|
16
|
+
| `@santux/eslint-config/format` | Prettier | Config object |
|
|
17
|
+
| `@santux/eslint-config/format-astro` | Prettier | Config object |
|
|
18
|
+
| `@santux/eslint-config/format-tailwind` | Prettier | Config object |
|
|
19
|
+
| `@santux/eslint-config/commitlint` | commitlint | Config object |
|
|
8
20
|
|
|
9
|
-
|
|
21
|
+
The lint entry uses typescript-eslint `strictTypeChecked` + `stylisticTypeChecked`, `eslint-plugin-import-x` (with the TypeScript import resolver, so `no-cycle`, `no-self-import` and `no-extraneous-dependencies` work on `.ts`/`.tsx`), unicorn and check-file naming.
|
|
10
22
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@santux/eslint-config`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
23
|
+
The `next` entry builds on the lint entry with react, react-hooks, jsx-a11y and Next.js rules (`react.version` is `19`). Compose it after the base: `defineConfig([santux, next])`. Its plugins (`@next/eslint-plugin-next`, `eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-jsx-a11y`, `globals`) are optional peer dependencies, needed only when you use it.
|
|
15
24
|
|
|
16
|
-
|
|
25
|
+
The `react-vite` entry builds on the lint entry the same way, with react, react-hooks, jsx-a11y and react-refresh rules. Its plugins (`eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-jsx-a11y`, `eslint-plugin-react-refresh`, `globals`) are optional peer dependencies.
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
The `tanstack-start` entry builds on the lint entry the same way, with react, react-hooks and jsx-a11y rules and browser plus Node globals. Its plugins (`eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-jsx-a11y`, `globals`) are optional peer dependencies.
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
The `astro` entry builds on the lint entry with the `eslint-plugin-astro` recommended rules and its jsx-a11y rules for `.astro` files, plus browser and Node globals. It parses `.astro` files with TypeScript and runs the type-aware rules there too, allows the `Props` interface Astro reads component props from, ignores `.astro/` and turns off the file and folder name rules under `pages`. Its plugins (`eslint-plugin-astro`, `eslint-plugin-jsx-a11y`, `globals`) are optional peer dependencies. `eslint-plugin-astro` stays on 1.7.0, the last version that supports ESLint 9, until this package moves to ESLint 10.
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
The `astro-react` entry adds the react, react-hooks and jsx-a11y rules to the `.jsx` and `.tsx` files of an Astro project with React islands (`@astrojs/react`), without react-refresh. Compose it after the `astro` entry: `defineConfig([santux, astro, astroReact])`. It needs `eslint-plugin-react` and `eslint-plugin-react-hooks`, optional peer dependencies.
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
33
|
+
The `tailwind` entry adds [`@shadcn/lint`](https://github.com/shadcn-ui/lint) (an optional peer dependency) with every rule except `require-static-classes`: `no-unknown-classes`, `no-raw-colors`, `no-arbitrary-values`, `no-inline-styles` and `no-restyle`. It needs no shadcn/ui: it finds the stylesheet that imports Tailwind, and it reads `components.json` when there is one. `no-restyle` only knows your design system components through `components.json` or `settings.shadcn.componentImports`. Compose it after the Framework entry: `defineConfig([santux, next, tailwind])`.
|
|
28
34
|
|
|
29
|
-
|
|
35
|
+
The `format-tailwind` entry is a Prettier config fragment that sorts Tailwind classes with `prettier-plugin-tailwindcss` (an optional peer dependency). Spread it after the format entry: `export default { ...santux, ...formatTailwind }`. For Tailwind v4 projects with a custom stylesheet, also set `tailwindStylesheet` in your Prettier config.
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
37
|
+
The `format-astro` entry formats `.astro` files with `prettier-plugin-astro` (an optional peer dependency), pinned to 0.14.1 because `prettier-plugin-tailwindcss` does not sort classes in `.astro` files with its 1.x. Spreading two entries keeps only the last `plugins`, so merge them with the Tailwind plugin last:
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
```js
|
|
40
|
+
export default {
|
|
41
|
+
...santux,
|
|
42
|
+
...formatAstro,
|
|
43
|
+
...formatTailwind,
|
|
44
|
+
plugins: [...formatAstro.plugins, ...formatTailwind.plugins],
|
|
45
|
+
}
|
|
46
|
+
```
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
48
|
+
## Usage
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
Every tool is an exact-version peer dependency and must be installed in your project:
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
```sh
|
|
53
|
+
pnpm add -D @santux/eslint-config @commitlint/cli @commitlint/config-conventional @eslint/js @stylistic/eslint-plugin eslint eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-check-file eslint-plugin-import-x eslint-plugin-unicorn husky prettier typescript typescript-eslint
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// eslint.config.js
|
|
58
|
+
import santux from '@santux/eslint-config/lint'
|
|
59
|
+
import { defineConfig } from 'eslint/config'
|
|
60
|
+
|
|
61
|
+
export default defineConfig([
|
|
62
|
+
santux,
|
|
63
|
+
// Override, disable or add rules locally
|
|
64
|
+
{ rules: { 'no-console': 'off', 'unicorn/prefer-node-protocol': 'error' } },
|
|
65
|
+
{ files: ['**/*.test.ts'], rules: { '@typescript-eslint/no-non-null-assertion': 'off' } },
|
|
66
|
+
])
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// prettier.config.js
|
|
71
|
+
import santux from '@santux/eslint-config/format'
|
|
72
|
+
|
|
73
|
+
export default { ...santux, printWidth: 120 }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
// commitlint.config.js
|
|
78
|
+
export { default } from '@santux/eslint-config/commitlint'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Requires Node `>=22.18`.
|
|
82
|
+
|
|
83
|
+
## Versioning
|
|
84
|
+
|
|
85
|
+
This package follows strict semver: **any change that can make previously passing code fail is a major release**. That includes a new error-level rule, a rule moving from warning to error, and a newer required tool or Node version. Projects can stay on an older major for as long as they want.
|
package/package.json
CHANGED
|
@@ -1,10 +1,172 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@santux/eslint-config",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared ESLint, Prettier and commitlint config for TypeScript projects.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
6
|
+
"commitlint",
|
|
7
|
+
"eslint",
|
|
8
|
+
"eslint-config",
|
|
9
|
+
"prettier"
|
|
10
|
+
],
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Nicolás Santuccio",
|
|
14
|
+
"email": "nico.santuccio@gmail.com"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/nicosantux/santux.git",
|
|
19
|
+
"directory": "packages/eslint-config"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"!src/test",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
"./lint": {
|
|
29
|
+
"types": "./src/lint.d.ts",
|
|
30
|
+
"default": "./src/lint.js"
|
|
31
|
+
},
|
|
32
|
+
"./format": {
|
|
33
|
+
"types": "./src/format.d.ts",
|
|
34
|
+
"default": "./src/format.js"
|
|
35
|
+
},
|
|
36
|
+
"./format-astro": {
|
|
37
|
+
"types": "./src/format-astro.d.ts",
|
|
38
|
+
"default": "./src/format-astro.js"
|
|
39
|
+
},
|
|
40
|
+
"./format-tailwind": {
|
|
41
|
+
"types": "./src/format-tailwind.d.ts",
|
|
42
|
+
"default": "./src/format-tailwind.js"
|
|
43
|
+
},
|
|
44
|
+
"./commitlint": {
|
|
45
|
+
"types": "./src/commitlint.d.ts",
|
|
46
|
+
"default": "./src/commitlint.js"
|
|
47
|
+
},
|
|
48
|
+
"./astro": {
|
|
49
|
+
"types": "./src/astro.d.ts",
|
|
50
|
+
"default": "./src/astro.js"
|
|
51
|
+
},
|
|
52
|
+
"./astro-react": {
|
|
53
|
+
"types": "./src/astro-react.d.ts",
|
|
54
|
+
"default": "./src/astro-react.js"
|
|
55
|
+
},
|
|
56
|
+
"./next": {
|
|
57
|
+
"types": "./src/next.d.ts",
|
|
58
|
+
"default": "./src/next.js"
|
|
59
|
+
},
|
|
60
|
+
"./react-vite": {
|
|
61
|
+
"types": "./src/react-vite.d.ts",
|
|
62
|
+
"default": "./src/react-vite.js"
|
|
63
|
+
},
|
|
64
|
+
"./tanstack-start": {
|
|
65
|
+
"types": "./src/tanstack-start.d.ts",
|
|
66
|
+
"default": "./src/tanstack-start.js"
|
|
67
|
+
},
|
|
68
|
+
"./tailwind": {
|
|
69
|
+
"types": "./src/tailwind.d.ts",
|
|
70
|
+
"default": "./src/tailwind.js"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public"
|
|
75
|
+
},
|
|
76
|
+
"scripts": {
|
|
77
|
+
"test": "vitest run",
|
|
78
|
+
"typecheck": "tsc --noEmit"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@commitlint/cli": "21.2.4",
|
|
82
|
+
"@commitlint/config-conventional": "21.2.4",
|
|
83
|
+
"@eslint/js": "9.39.5",
|
|
84
|
+
"@next/eslint-plugin-next": "16.4.6",
|
|
85
|
+
"@shadcn/lint": "0.2.5",
|
|
86
|
+
"@stylistic/eslint-plugin": "5.10.0",
|
|
87
|
+
"eslint": "9.39.5",
|
|
88
|
+
"eslint-config-prettier": "10.1.8",
|
|
89
|
+
"eslint-import-resolver-typescript": "4.4.5",
|
|
90
|
+
"eslint-plugin-astro": "1.7.0",
|
|
91
|
+
"eslint-plugin-check-file": "3.3.2",
|
|
92
|
+
"eslint-plugin-import-x": "4.18.1",
|
|
93
|
+
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
94
|
+
"eslint-plugin-react": "7.37.5",
|
|
95
|
+
"eslint-plugin-react-hooks": "7.1.1",
|
|
96
|
+
"eslint-plugin-react-refresh": "0.5.7",
|
|
97
|
+
"eslint-plugin-unicorn": "65.0.1",
|
|
98
|
+
"globals": "17.14.0",
|
|
99
|
+
"husky": "9.1.7",
|
|
100
|
+
"prettier": "3.10.1",
|
|
101
|
+
"prettier-plugin-astro": "0.14.1",
|
|
102
|
+
"prettier-plugin-tailwindcss": "0.8.1",
|
|
103
|
+
"tailwindcss": "4.3.5",
|
|
104
|
+
"typescript": "6.0.3",
|
|
105
|
+
"typescript-eslint": "8.72.2",
|
|
106
|
+
"vitest": "5.1.0"
|
|
107
|
+
},
|
|
108
|
+
"peerDependencies": {
|
|
109
|
+
"@commitlint/cli": "21.2.4",
|
|
110
|
+
"@commitlint/config-conventional": "21.2.4",
|
|
111
|
+
"@eslint/js": "9.39.5",
|
|
112
|
+
"@next/eslint-plugin-next": "16.4.6",
|
|
113
|
+
"@shadcn/lint": "0.2.5",
|
|
114
|
+
"@stylistic/eslint-plugin": "5.10.0",
|
|
115
|
+
"@typescript/typescript6": "6.0.2",
|
|
116
|
+
"eslint": "9.39.5",
|
|
117
|
+
"eslint-config-prettier": "10.1.8",
|
|
118
|
+
"eslint-import-resolver-typescript": "4.4.5",
|
|
119
|
+
"eslint-plugin-astro": "1.7.0",
|
|
120
|
+
"eslint-plugin-check-file": "3.3.2",
|
|
121
|
+
"eslint-plugin-import-x": "4.18.1",
|
|
122
|
+
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
123
|
+
"eslint-plugin-react": "7.37.5",
|
|
124
|
+
"eslint-plugin-react-hooks": "7.1.1",
|
|
125
|
+
"eslint-plugin-react-refresh": "0.5.7",
|
|
126
|
+
"eslint-plugin-unicorn": "65.0.1",
|
|
127
|
+
"globals": "17.14.0",
|
|
128
|
+
"husky": "9.1.7",
|
|
129
|
+
"prettier": "3.10.1",
|
|
130
|
+
"prettier-plugin-astro": "0.14.1",
|
|
131
|
+
"prettier-plugin-tailwindcss": "0.8.1",
|
|
132
|
+
"typescript-eslint": "8.72.2"
|
|
133
|
+
},
|
|
134
|
+
"peerDependenciesMeta": {
|
|
135
|
+
"@next/eslint-plugin-next": {
|
|
136
|
+
"optional": true
|
|
137
|
+
},
|
|
138
|
+
"@shadcn/lint": {
|
|
139
|
+
"optional": true
|
|
140
|
+
},
|
|
141
|
+
"@typescript/typescript6": {
|
|
142
|
+
"optional": true
|
|
143
|
+
},
|
|
144
|
+
"eslint-plugin-astro": {
|
|
145
|
+
"optional": true
|
|
146
|
+
},
|
|
147
|
+
"eslint-plugin-jsx-a11y": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"eslint-plugin-react": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"eslint-plugin-react-hooks": {
|
|
154
|
+
"optional": true
|
|
155
|
+
},
|
|
156
|
+
"eslint-plugin-react-refresh": {
|
|
157
|
+
"optional": true
|
|
158
|
+
},
|
|
159
|
+
"globals": {
|
|
160
|
+
"optional": true
|
|
161
|
+
},
|
|
162
|
+
"prettier-plugin-astro": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"prettier-plugin-tailwindcss": {
|
|
166
|
+
"optional": true
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"engines": {
|
|
170
|
+
"node": ">=22.18"
|
|
171
|
+
}
|
|
10
172
|
}
|
package/src/astro.d.ts
ADDED
package/src/astro.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
|
|
3
|
+
import astroPlugin from 'eslint-plugin-astro'
|
|
4
|
+
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
|
|
5
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
6
|
+
import globals from 'globals'
|
|
7
|
+
import tsEslint from 'typescript-eslint'
|
|
8
|
+
|
|
9
|
+
// The rules of eslint-plugin-astro's `jsx-a11y-recommended` config, limited to .astro files. The
|
|
10
|
+
// config itself registers its own copy of the `jsx-a11y` plugin, which clashes with the React rules
|
|
11
|
+
const a11yRules = Object.fromEntries(
|
|
12
|
+
Object.entries(jsxA11yPlugin.flatConfigs.recommended.rules).map(([name, entry]) => [
|
|
13
|
+
`astro/${name}`,
|
|
14
|
+
entry,
|
|
15
|
+
]),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// import-x parses the modules an .astro file imports with the .astro parser unless told otherwise
|
|
19
|
+
const tsParserPath = createRequire(import.meta.resolve('typescript-eslint')).resolve(
|
|
20
|
+
'@typescript-eslint/parser',
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export default defineConfig([
|
|
24
|
+
globalIgnores(['**/.astro/**']),
|
|
25
|
+
astroPlugin.configs['flat/recommended'],
|
|
26
|
+
{
|
|
27
|
+
files: ['**/*.astro'],
|
|
28
|
+
// eslint-plugin-astro only picks the TS parser and the TS <script> processor when it finds the
|
|
29
|
+
// parser hoisted to the project root, and astro-eslint-parser does not support `projectService`
|
|
30
|
+
processor: 'astro/client-side-ts',
|
|
31
|
+
languageOptions: {
|
|
32
|
+
parserOptions: {
|
|
33
|
+
parser: tsEslint.parser,
|
|
34
|
+
extraFileExtensions: ['.astro'],
|
|
35
|
+
project: true,
|
|
36
|
+
projectService: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
settings: {
|
|
40
|
+
'import-x/parsers': {
|
|
41
|
+
[tsParserPath]: ['.js', '.cjs', '.mjs', '.jsx', '.ts', '.cts', '.mts', '.tsx'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
rules: {
|
|
45
|
+
...a11yRules,
|
|
46
|
+
// Astro types a component's props through an interface named `Props`
|
|
47
|
+
'@typescript-eslint/naming-convention': [
|
|
48
|
+
'error',
|
|
49
|
+
{ format: ['PascalCase'], selector: ['typeLike', 'enumMember'] },
|
|
50
|
+
{
|
|
51
|
+
custom: { match: false, regex: '^I[A-Z]|^(Interface|State)$' },
|
|
52
|
+
format: ['PascalCase'],
|
|
53
|
+
selector: 'interface',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// The <script> blocks are virtual files that no tsconfig includes, so they get no type information
|
|
59
|
+
{ files: ['**/*.astro/*.ts'], ...tsEslint.configs.disableTypeChecked },
|
|
60
|
+
{
|
|
61
|
+
files: ['**/pages/**'],
|
|
62
|
+
rules: {
|
|
63
|
+
'check-file/filename-naming-convention': 'off',
|
|
64
|
+
'check-file/folder-naming-convention': 'off',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
|
|
68
|
+
])
|
package/src/format.d.ts
ADDED
package/src/format.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @type {import('prettier').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
arrowParens: 'always',
|
|
4
|
+
bracketSameLine: false,
|
|
5
|
+
bracketSpacing: true,
|
|
6
|
+
endOfLine: 'auto',
|
|
7
|
+
jsxSingleQuote: true,
|
|
8
|
+
printWidth: 100,
|
|
9
|
+
quoteProps: 'as-needed',
|
|
10
|
+
semi: false,
|
|
11
|
+
singleQuote: true,
|
|
12
|
+
tabWidth: 2,
|
|
13
|
+
trailingComma: 'all',
|
|
14
|
+
useTabs: false,
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function importOrder(earlyPackages: string[]): [string, object]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** `import-x/order` options. Packages in `earlyPackages` are grouped before the other builtins. */
|
|
2
|
+
export function importOrder(earlyPackages) {
|
|
3
|
+
return [
|
|
4
|
+
'warn',
|
|
5
|
+
{
|
|
6
|
+
alphabetize: { order: 'asc', caseInsensitive: true },
|
|
7
|
+
groups: ['type', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
|
|
8
|
+
'newlines-between': 'always',
|
|
9
|
+
pathGroups: [
|
|
10
|
+
{ pattern: '@/**', group: 'internal', position: 'after' },
|
|
11
|
+
{ pattern: `{${earlyPackages.join(',')}}`, group: 'builtin', position: 'before' },
|
|
12
|
+
],
|
|
13
|
+
pathGroupsExcludedImportTypes: ['builtin', 'internal'],
|
|
14
|
+
warnOnUnassignedImports: true,
|
|
15
|
+
},
|
|
16
|
+
]
|
|
17
|
+
}
|
package/src/lint.d.ts
ADDED
package/src/lint.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import eslint from '@eslint/js'
|
|
2
|
+
import stylistic from '@stylistic/eslint-plugin'
|
|
3
|
+
import eslintConfigPrettier from 'eslint-config-prettier/flat'
|
|
4
|
+
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
|
|
5
|
+
import checkFilePlugin from 'eslint-plugin-check-file'
|
|
6
|
+
import { importX } from 'eslint-plugin-import-x'
|
|
7
|
+
import unicornPlugin from 'eslint-plugin-unicorn'
|
|
8
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
9
|
+
import tsEslint from 'typescript-eslint'
|
|
10
|
+
|
|
11
|
+
import { importOrder } from './import-order.js'
|
|
12
|
+
|
|
13
|
+
export default defineConfig([
|
|
14
|
+
globalIgnores(['**/node_modules', '**/build', '**/dist']),
|
|
15
|
+
// Vanilla ESLint
|
|
16
|
+
{
|
|
17
|
+
extends: [eslint.configs.recommended],
|
|
18
|
+
rules: {
|
|
19
|
+
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
|
20
|
+
'no-await-in-loop': 'error',
|
|
21
|
+
'no-console': 'error',
|
|
22
|
+
'no-else-return': 'warn',
|
|
23
|
+
'no-implicit-coercion': 'error',
|
|
24
|
+
'no-param-reassign': 'error',
|
|
25
|
+
'no-self-compare': 'error',
|
|
26
|
+
'no-undef-init': 'warn',
|
|
27
|
+
'no-unneeded-ternary': 'error',
|
|
28
|
+
'no-var': 'error',
|
|
29
|
+
'object-shorthand': 'warn',
|
|
30
|
+
'prefer-object-spread': 'warn',
|
|
31
|
+
'prefer-rest-params': 'error',
|
|
32
|
+
'prefer-spread': 'error',
|
|
33
|
+
'prefer-template': 'warn',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
// Check files
|
|
37
|
+
{
|
|
38
|
+
plugins: { 'check-file': checkFilePlugin },
|
|
39
|
+
rules: {
|
|
40
|
+
'check-file/filename-naming-convention': [
|
|
41
|
+
'error',
|
|
42
|
+
{ '**/*.{js,jsx,ts,tsx}': 'KEBAB_CASE' },
|
|
43
|
+
{ ignoreMiddleExtensions: true },
|
|
44
|
+
],
|
|
45
|
+
'check-file/folder-naming-convention': ['error', { '**/src/**/': 'KEBAB_CASE' }],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
// Import order
|
|
49
|
+
{
|
|
50
|
+
plugins: { 'import-x': importX },
|
|
51
|
+
settings: {
|
|
52
|
+
'import-x/extensions': ['.js', '.jsx', '.ts', '.tsx'],
|
|
53
|
+
'import-x/resolver-next': [createTypeScriptImportResolver()],
|
|
54
|
+
},
|
|
55
|
+
rules: {
|
|
56
|
+
'import-x/first': 'error',
|
|
57
|
+
'import-x/newline-after-import': 'warn',
|
|
58
|
+
'import-x/no-duplicates': ['warn', { 'prefer-inline': true }],
|
|
59
|
+
'import-x/no-cycle': 'error',
|
|
60
|
+
'import-x/no-extraneous-dependencies': ['error', { includeTypes: true }],
|
|
61
|
+
'import-x/no-mutable-exports': 'error',
|
|
62
|
+
'import-x/no-self-import': 'error',
|
|
63
|
+
'import-x/no-useless-path-segments': 'error',
|
|
64
|
+
'import-x/order': importOrder(['react', 'react-dom', 'react-dom/**']),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
// Stylistic
|
|
68
|
+
{
|
|
69
|
+
plugins: { '@stylistic': stylistic },
|
|
70
|
+
rules: {
|
|
71
|
+
'@stylistic/padding-line-between-statements': [
|
|
72
|
+
'warn',
|
|
73
|
+
{ blankLine: 'always', prev: 'directive', next: '*' },
|
|
74
|
+
{ blankLine: 'always', prev: '*', next: 'block-like' },
|
|
75
|
+
{ blankLine: 'always', prev: 'block-like', next: '*' },
|
|
76
|
+
{ blankLine: 'always', prev: '*', next: 'return' },
|
|
77
|
+
{ blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' },
|
|
78
|
+
{
|
|
79
|
+
blankLine: 'any',
|
|
80
|
+
prev: ['singleline-const', 'singleline-let', 'singleline-var'],
|
|
81
|
+
next: ['singleline-const', 'singleline-let', 'singleline-var'],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
// TypeScript
|
|
87
|
+
{
|
|
88
|
+
extends: [tsEslint.configs.strictTypeChecked, tsEslint.configs.stylisticTypeChecked],
|
|
89
|
+
languageOptions: { parserOptions: { projectService: true } },
|
|
90
|
+
rules: {
|
|
91
|
+
'@typescript-eslint/consistent-type-exports': [
|
|
92
|
+
'warn',
|
|
93
|
+
{ fixMixedExportsWithInlineTypeSpecifier: true },
|
|
94
|
+
],
|
|
95
|
+
'@typescript-eslint/consistent-type-imports': ['warn', { fixStyle: 'inline-type-imports' }],
|
|
96
|
+
'@typescript-eslint/default-param-last': 'error',
|
|
97
|
+
'@typescript-eslint/member-ordering': [
|
|
98
|
+
'error',
|
|
99
|
+
{ interfaces: { order: 'natural-case-insensitive' } },
|
|
100
|
+
],
|
|
101
|
+
'@typescript-eslint/method-signature-style': 'warn',
|
|
102
|
+
'@typescript-eslint/naming-convention': [
|
|
103
|
+
'error',
|
|
104
|
+
{ format: ['PascalCase'], selector: ['typeLike', 'enumMember'] },
|
|
105
|
+
{
|
|
106
|
+
custom: {
|
|
107
|
+
match: false,
|
|
108
|
+
regex: '^I[A-Z]|^(Interface|Props|State)$',
|
|
109
|
+
},
|
|
110
|
+
format: ['PascalCase'],
|
|
111
|
+
selector: 'interface',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
'@typescript-eslint/no-import-type-side-effects': 'warn',
|
|
115
|
+
'@typescript-eslint/no-confusing-void-expression': ['error', { ignoreArrowShorthand: true }],
|
|
116
|
+
'@typescript-eslint/no-loop-func': 'error',
|
|
117
|
+
'@typescript-eslint/no-redundant-type-constituents': 'warn',
|
|
118
|
+
'@typescript-eslint/no-unnecessary-qualifier': 'warn',
|
|
119
|
+
'@typescript-eslint/no-unused-vars': [
|
|
120
|
+
'error',
|
|
121
|
+
{
|
|
122
|
+
args: 'all',
|
|
123
|
+
argsIgnorePattern: '^_',
|
|
124
|
+
caughtErrors: 'all',
|
|
125
|
+
caughtErrorsIgnorePattern: '^_',
|
|
126
|
+
destructuredArrayIgnorePattern: '^_',
|
|
127
|
+
varsIgnorePattern: '^_',
|
|
128
|
+
ignoreRestSiblings: true,
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
'@typescript-eslint/prefer-nullish-coalescing': [
|
|
132
|
+
'error',
|
|
133
|
+
{
|
|
134
|
+
ignorePrimitives: {
|
|
135
|
+
bigint: true,
|
|
136
|
+
boolean: true,
|
|
137
|
+
number: true,
|
|
138
|
+
string: true,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
'@typescript-eslint/require-array-sort-compare': 'error',
|
|
143
|
+
'@typescript-eslint/switch-exhaustiveness-check': 'error',
|
|
144
|
+
'@typescript-eslint/triple-slash-reference': 'off',
|
|
145
|
+
// After the typescript-eslint presets, which set it to error in TypeScript files
|
|
146
|
+
'prefer-const': 'warn',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
// Disable type checked in .js files
|
|
150
|
+
{
|
|
151
|
+
files: ['**/*.{js,cjs,mjs}'],
|
|
152
|
+
...tsEslint.configs.disableTypeChecked,
|
|
153
|
+
},
|
|
154
|
+
// Unicorn
|
|
155
|
+
{
|
|
156
|
+
plugins: { unicorn: unicornPlugin },
|
|
157
|
+
rules: {
|
|
158
|
+
'unicorn/error-message': 'error',
|
|
159
|
+
'unicorn/no-empty-file': 'error',
|
|
160
|
+
'unicorn/no-instanceof-builtins': 'error',
|
|
161
|
+
'unicorn/no-useless-spread': 'warn',
|
|
162
|
+
'unicorn/prefer-array-flat-map': 'warn',
|
|
163
|
+
'unicorn/prefer-node-protocol': 'warn',
|
|
164
|
+
'unicorn/throw-new-error': 'error',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
// Prettier
|
|
168
|
+
eslintConfigPrettier,
|
|
169
|
+
])
|
package/src/next.d.ts
ADDED
package/src/next.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import nextPlugin from '@next/eslint-plugin-next'
|
|
2
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
3
|
+
import globals from 'globals'
|
|
4
|
+
|
|
5
|
+
import { importOrder } from './import-order.js'
|
|
6
|
+
import { reactConfig } from './react.js'
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
globalIgnores(['**/.next/**', '**/out/**', '**/next-env.d.ts']),
|
|
10
|
+
{
|
|
11
|
+
rules: {
|
|
12
|
+
'check-file/folder-naming-convention': ['error', { '**/src/**/': 'NEXT_JS_APP_ROUTER_CASE' }],
|
|
13
|
+
'import-x/order': importOrder(['next', 'next/**', 'react', 'react-dom', 'react-dom/**']),
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
nextPlugin.configs['core-web-vitals'],
|
|
17
|
+
reactConfig({ ...globals.browser, ...globals.node }),
|
|
18
|
+
])
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import reactRefreshPlugin from 'eslint-plugin-react-refresh'
|
|
2
|
+
import { defineConfig } from 'eslint/config'
|
|
3
|
+
import globals from 'globals'
|
|
4
|
+
|
|
5
|
+
import { importOrder } from './import-order.js'
|
|
6
|
+
import { reactConfig } from './react.js'
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
{
|
|
10
|
+
rules: {
|
|
11
|
+
'import-x/order': importOrder(['react', 'react-dom', 'react-dom/**']),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
reactConfig(globals.browser, [reactRefreshPlugin.configs.vite]),
|
|
15
|
+
])
|
package/src/react.d.ts
ADDED
package/src/react.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
|
|
2
|
+
import reactPlugin from 'eslint-plugin-react'
|
|
3
|
+
import reactHooksPlugin from 'eslint-plugin-react-hooks'
|
|
4
|
+
|
|
5
|
+
/** React config block shared by the Framework entries. `extraExtends` go after the React presets. */
|
|
6
|
+
export function reactConfig(globals, extraExtends = []) {
|
|
7
|
+
return {
|
|
8
|
+
files: ['**/*.{js,mjs,cjs,jsx,ts,tsx}'],
|
|
9
|
+
extends: [
|
|
10
|
+
reactPlugin.configs.flat.recommended,
|
|
11
|
+
reactPlugin.configs.flat['jsx-runtime'],
|
|
12
|
+
reactHooksPlugin.configs.flat['recommended-latest'],
|
|
13
|
+
jsxA11yPlugin.flatConfigs.recommended,
|
|
14
|
+
...extraExtends,
|
|
15
|
+
],
|
|
16
|
+
languageOptions: {
|
|
17
|
+
globals,
|
|
18
|
+
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
'react/function-component-definition': 'warn',
|
|
22
|
+
'react/hook-use-state': 'error',
|
|
23
|
+
'react/jsx-boolean-value': 'warn',
|
|
24
|
+
'react/jsx-curly-brace-presence': 'warn',
|
|
25
|
+
'react/jsx-fragments': 'warn',
|
|
26
|
+
'react/jsx-no-leaked-render': 'warn',
|
|
27
|
+
'react/jsx-no-target-blank': ['error', { allowReferrer: true }],
|
|
28
|
+
'react/jsx-no-useless-fragment': ['warn', { allowExpressions: true }],
|
|
29
|
+
'react/jsx-sort-props': ['warn', { noSortAlphabetically: false }],
|
|
30
|
+
'react/no-array-index-key': 'warn',
|
|
31
|
+
'react/no-unstable-nested-components': 'error',
|
|
32
|
+
'react/prop-types': 'off',
|
|
33
|
+
'react/self-closing-comp': 'warn',
|
|
34
|
+
},
|
|
35
|
+
settings: { react: { version: '19' } },
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/tailwind.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { plugin as shadcnPlugin } from '@shadcn/lint'
|
|
2
|
+
import { defineConfig } from 'eslint/config'
|
|
3
|
+
|
|
4
|
+
export default defineConfig([
|
|
5
|
+
{
|
|
6
|
+
files: ['**/*.{js,mjs,cjs,jsx,ts,tsx}'],
|
|
7
|
+
plugins: { shadcn: shadcnPlugin },
|
|
8
|
+
rules: {
|
|
9
|
+
'shadcn/no-arbitrary-values': 'error',
|
|
10
|
+
'shadcn/no-inline-styles': 'error',
|
|
11
|
+
'shadcn/no-raw-colors': 'error',
|
|
12
|
+
'shadcn/no-restyle': 'error',
|
|
13
|
+
'shadcn/no-unknown-classes': 'error',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
])
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
|
|
4
|
+
import { importOrder } from './import-order.js'
|
|
5
|
+
import { reactConfig } from './react.js'
|
|
6
|
+
|
|
7
|
+
export default defineConfig([
|
|
8
|
+
globalIgnores(['**/.output/**', '**/.tanstack/**', '**/routeTree.gen.ts']),
|
|
9
|
+
{
|
|
10
|
+
rules: {
|
|
11
|
+
'import-x/order': importOrder(['react', 'react-dom', 'react-dom/**']),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
files: ['**/routes/**'],
|
|
16
|
+
rules: {
|
|
17
|
+
'check-file/filename-naming-convention': 'off',
|
|
18
|
+
'check-file/folder-naming-convention': 'off',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
reactConfig({ ...globals.browser, ...globals.node }),
|
|
22
|
+
])
|