@st1ggy/linter-config 7.2.2 → 7.3.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 +25 -2
- package/data/linter-config-inventory.json +2508 -2
- package/docs/RULE_MAPPING.md +2 -0
- package/index.d.ts +9 -0
- package/package.json +12 -1
- package/scripts/README.md +5 -1
- package/scripts/init-solid.sh +5 -0
- package/scripts/inventory.mjs +5 -0
- package/scripts/linter-init-core.mjs +13 -2
- package/scripts/linter-init.mjs +2 -0
- package/scripts/solid-config.test.mjs +134 -0
- package/src/eslint/configs/config.solid.js +36 -0
- package/src/eslint/eslint.config.solid.js +4 -0
- package/src/examples/example.solid.tsx +3 -0
- package/src/index.js +1 -0
package/docs/RULE_MAPPING.md
CHANGED
|
@@ -19,6 +19,7 @@ node scripts/inventory.mjs
|
|
|
19
19
|
| Unicorn | `config.unicorn.js` | eslint-plugin-unicorn. |
|
|
20
20
|
| SonarJS | `config.sonarjs.js` | eslint-plugin-sonarjs. |
|
|
21
21
|
| React | `config.react.js` | React/JSX + hooks. |
|
|
22
|
+
| SolidJS | `config.solid.js` | `eslint-plugin-solid` recommended for JS/JSX, TypeScript preset for TS/TSX, and JSX formatting. |
|
|
22
23
|
| Next.js | `config.next.js` | `@next/eslint-plugin-next`. |
|
|
23
24
|
| Svelte | `svelte-stack.js` | `eslint-plugin-svelte` flat recommended + TS parser options. |
|
|
24
25
|
| Astro | `astro-stack.js` | `eslint-plugin-astro` recommended. |
|
|
@@ -27,6 +28,7 @@ Inventory rule counts (sample files in this repository):
|
|
|
27
28
|
|
|
28
29
|
- `eslint-common`: effective rules on `src/examples/example.ts`
|
|
29
30
|
- `eslint-react`: includes React layer on the same sample
|
|
31
|
+
- `eslint-solid`: effective rules on `src/examples/example.solid.tsx`
|
|
30
32
|
- `eslint-next`: Next layer on the same sample
|
|
31
33
|
- `eslint-svelte`: effective rules on `src/examples/example.svelte`
|
|
32
34
|
- `eslint-astro`: effective rules on `src/examples/example.astro`
|
package/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ declare module '@st1ggy/linter-config' {
|
|
|
9
9
|
|
|
10
10
|
export const eslintCommon: Linter.Config[]
|
|
11
11
|
export const eslintReact: Linter.Config[]
|
|
12
|
+
export const eslintSolid: Linter.Config[]
|
|
12
13
|
export const eslintNext: Linter.Config[]
|
|
13
14
|
export const eslintSvelte: Linter.Config[]
|
|
14
15
|
export const eslintAstro: Linter.Config[]
|
|
@@ -36,6 +37,14 @@ declare module '@st1ggy/linter-config/eslint-react' {
|
|
|
36
37
|
export default config
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
declare module '@st1ggy/linter-config/eslint-solid' {
|
|
41
|
+
import type { Linter } from 'eslint'
|
|
42
|
+
|
|
43
|
+
const config: Linter.Config[]
|
|
44
|
+
|
|
45
|
+
export default config
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
declare module '@st1ggy/linter-config/eslint-svelte' {
|
|
40
49
|
import type { Linter } from 'eslint'
|
|
41
50
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@st1ggy/linter-config",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"description": "ESLint, Prettier, and Stylelint shareable configs (sources under src/).",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -73,6 +73,11 @@
|
|
|
73
73
|
"import": "./src/eslint/eslint.config.react.js",
|
|
74
74
|
"default": "./src/eslint/eslint.config.react.js"
|
|
75
75
|
},
|
|
76
|
+
"./eslint-solid": {
|
|
77
|
+
"types": "./index.d.ts",
|
|
78
|
+
"import": "./src/eslint/eslint.config.solid.js",
|
|
79
|
+
"default": "./src/eslint/eslint.config.solid.js"
|
|
80
|
+
},
|
|
76
81
|
"./eslint-svelte": {
|
|
77
82
|
"types": "./index.d.ts",
|
|
78
83
|
"import": "./src/eslint/eslint-svelte.config.js",
|
|
@@ -140,6 +145,7 @@
|
|
|
140
145
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
141
146
|
"eslint-plugin-react": "^7.37.5",
|
|
142
147
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
148
|
+
"eslint-plugin-solid": "^0.18.0",
|
|
143
149
|
"eslint-plugin-svelte": "^3.22.0",
|
|
144
150
|
"next": "^16.3.0",
|
|
145
151
|
"prettier": "^3.9.6",
|
|
@@ -165,6 +171,9 @@
|
|
|
165
171
|
"eslint-plugin-react-hooks": {
|
|
166
172
|
"optional": true
|
|
167
173
|
},
|
|
174
|
+
"eslint-plugin-solid": {
|
|
175
|
+
"optional": true
|
|
176
|
+
},
|
|
168
177
|
"eslint-plugin-svelte": {
|
|
169
178
|
"optional": true
|
|
170
179
|
},
|
|
@@ -202,9 +211,11 @@
|
|
|
202
211
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
203
212
|
"eslint-plugin-react": "^7.37.5",
|
|
204
213
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
214
|
+
"eslint-plugin-solid": "^0.18.0",
|
|
205
215
|
"eslint-plugin-svelte": "^3.22.0",
|
|
206
216
|
"prettier-plugin-astro": "^0.14.1",
|
|
207
217
|
"prettier-plugin-svelte": "^4.1.1",
|
|
218
|
+
"solid-js": "^1.9.15",
|
|
208
219
|
"svelte": "^5.56.8"
|
|
209
220
|
}
|
|
210
221
|
}
|
package/scripts/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Direct CLI (paths from repo root — same entry as published **`@st1ggy/linter-c
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
node ./scripts/linter-init.mjs init --react
|
|
22
|
+
node ./scripts/linter-init.mjs init --solid --dir ./my-solid-app
|
|
22
23
|
node ./scripts/linter-init.mjs migrate --svelte --dir ./my-app
|
|
23
24
|
node ./scripts/linter-init.mjs init --astro
|
|
24
25
|
```
|
|
@@ -30,6 +31,7 @@ The CLI is published under the name **`@st1ggy/linter-config`** (see [`package.j
|
|
|
30
31
|
```bash
|
|
31
32
|
npx @st1ggy/linter-config init --common
|
|
32
33
|
npx @st1ggy/linter-config init --next
|
|
34
|
+
npx @st1ggy/linter-config init --solid
|
|
33
35
|
npm exec @st1ggy/linter-config -- migrate --svelte
|
|
34
36
|
```
|
|
35
37
|
|
|
@@ -39,8 +41,10 @@ npm exec @st1ggy/linter-config -- migrate --svelte
|
|
|
39
41
|
npx --yes @st1ggy/linter-config init --astro
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
- **Stack (optional, default common):** `--common` | `--react` | `--next` | `--svelte` | `--astro` (at most one).
|
|
44
|
+
- **Stack (optional, default common):** `--common` | `--react` | `--solid` | `--next` | `--svelte` | `--astro` (at most one).
|
|
43
45
|
- **`--skip-install`:** only write wrapper files; do not run npm/pnpm/yarn/bun.
|
|
44
46
|
- Every CLI command opens an interactive wizard. `init` creates missing wrappers; `migrate` can remove selected legacy configs and replaces wrappers.
|
|
45
47
|
|
|
46
48
|
Shell shortcuts in this folder (`init-common.sh`, etc.) call **`node …/linter-init.mjs`** (same script as the published **`@st1ggy/linter-config`** CLI) with the matching stack; pass **`--skip-install`** through when needed.
|
|
49
|
+
|
|
50
|
+
For SolidJS, `init-solid.sh` selects `eslint-solid`, `prettier-common`, and `stylelint-scss`, and installs `eslint-plugin-solid` alongside `@st1ggy/linter-config`.
|
package/scripts/inventory.mjs
CHANGED
|
@@ -26,6 +26,11 @@ const flatConfigPresets = [
|
|
|
26
26
|
module: 'src/eslint/eslint.config.react.js',
|
|
27
27
|
sampleFile: 'src/examples/example.ts',
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
id: 'eslint-solid',
|
|
31
|
+
module: 'src/eslint/eslint.config.solid.js',
|
|
32
|
+
sampleFile: 'src/examples/example.solid.tsx',
|
|
33
|
+
},
|
|
29
34
|
{
|
|
30
35
|
id: 'eslint-next',
|
|
31
36
|
module: 'src/eslint/eslint.config.next.js',
|
|
@@ -6,7 +6,7 @@ import path from 'node:path'
|
|
|
6
6
|
|
|
7
7
|
export const PACKAGE = '@st1ggy/linter-config'
|
|
8
8
|
|
|
9
|
-
export const STACK_KEYS = ['common', 'react', 'next', 'svelte', 'astro']
|
|
9
|
+
export const STACK_KEYS = ['common', 'react', 'solid', 'next', 'svelte', 'astro']
|
|
10
10
|
|
|
11
11
|
export const STACKS = {
|
|
12
12
|
common: {
|
|
@@ -21,6 +21,12 @@ export const STACKS = {
|
|
|
21
21
|
stylelint: 'stylelint-scss',
|
|
22
22
|
packages: ['eslint-plugin-react', 'eslint-plugin-react-hooks'],
|
|
23
23
|
},
|
|
24
|
+
solid: {
|
|
25
|
+
eslint: 'eslint-solid',
|
|
26
|
+
prettier: 'prettier-common',
|
|
27
|
+
stylelint: 'stylelint-scss',
|
|
28
|
+
packages: ['eslint-plugin-solid'],
|
|
29
|
+
},
|
|
24
30
|
next: {
|
|
25
31
|
eslint: 'eslint-next',
|
|
26
32
|
prettier: 'prettier-common',
|
|
@@ -50,6 +56,10 @@ export const STACK_CHOICES = [
|
|
|
50
56
|
value: 'react',
|
|
51
57
|
name: 'react — React + hooks on top of common',
|
|
52
58
|
},
|
|
59
|
+
{
|
|
60
|
+
value: 'solid',
|
|
61
|
+
name: 'solid — SolidJS + reactivity rules on top of common',
|
|
62
|
+
},
|
|
53
63
|
{
|
|
54
64
|
value: 'next',
|
|
55
65
|
name: 'next — Next.js App Router on top of react',
|
|
@@ -260,7 +270,7 @@ Unless --skip-install: if package.json exists, runs the detected package manager
|
|
|
260
270
|
${PACKAGE} and the selected stack's integration plugins.
|
|
261
271
|
|
|
262
272
|
Stack (at most one; default: common):
|
|
263
|
-
--common | --react | --next | --svelte | --astro
|
|
273
|
+
--common | --react | --solid | --next | --svelte | --astro
|
|
264
274
|
|
|
265
275
|
Options:
|
|
266
276
|
--dir, -d Target directory (default: current working directory).
|
|
@@ -271,6 +281,7 @@ Examples (after: npm i -D ${PACKAGE}):
|
|
|
271
281
|
npx ${PACKAGE}
|
|
272
282
|
npx ${PACKAGE} init
|
|
273
283
|
npx ${PACKAGE} init --react
|
|
284
|
+
npx ${PACKAGE} init --solid
|
|
274
285
|
npx ${PACKAGE} migrate --svelte --dir ./apps/web
|
|
275
286
|
npx ${PACKAGE} init --astro
|
|
276
287
|
npm exec ${PACKAGE} -- init --common
|
package/scripts/linter-init.mjs
CHANGED
|
@@ -34,6 +34,7 @@ function parseArgv(argv) {
|
|
|
34
34
|
'biome',
|
|
35
35
|
'common',
|
|
36
36
|
'react',
|
|
37
|
+
'solid',
|
|
37
38
|
'next',
|
|
38
39
|
'svelte',
|
|
39
40
|
'astro',
|
|
@@ -59,6 +60,7 @@ function argvToValues(argv) {
|
|
|
59
60
|
biome: Boolean(argv.biome),
|
|
60
61
|
common: Boolean(argv.common),
|
|
61
62
|
react: Boolean(argv.react),
|
|
63
|
+
solid: Boolean(argv.solid),
|
|
62
64
|
next: Boolean(argv.next),
|
|
63
65
|
svelte: Boolean(argv.svelte),
|
|
64
66
|
astro: Boolean(argv.astro),
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/* eslint-disable unicorn/no-incorrect-template-string-interpolation -- Fixtures contain literal JSX expressions. */
|
|
2
|
+
|
|
3
|
+
import { ESLint } from 'eslint'
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
|
6
|
+
import { tmpdir } from 'node:os'
|
|
7
|
+
import path from 'node:path'
|
|
8
|
+
import test from 'node:test'
|
|
9
|
+
|
|
10
|
+
import solidConfig from '@st1ggy/linter-config/eslint-solid'
|
|
11
|
+
|
|
12
|
+
import prettierConfig from '../src/prettier/prettier.config.common.js'
|
|
13
|
+
|
|
14
|
+
import { resolveStackKey, run, stackPackages } from './linter-init-core.mjs'
|
|
15
|
+
|
|
16
|
+
function createProject(context) {
|
|
17
|
+
const prefix = path.join(tmpdir(), 'linter-solid-')
|
|
18
|
+
const directory = realpathSync(mkdtempSync(prefix))
|
|
19
|
+
|
|
20
|
+
context.after(() => rmSync(directory, { recursive: true, force: true }))
|
|
21
|
+
writeFileSync(
|
|
22
|
+
path.join(directory, 'tsconfig.json'),
|
|
23
|
+
JSON.stringify({ compilerOptions: { allowJs: true, jsx: 'preserve', noEmit: true }, include: ['*.jsx', '*.tsx'] }),
|
|
24
|
+
)
|
|
25
|
+
writeFileSync(path.join(directory, '.prettierrc.json'), JSON.stringify(prettierConfig))
|
|
26
|
+
|
|
27
|
+
return directory
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function lintSource(context, extension, source) {
|
|
31
|
+
const directory = createProject(context)
|
|
32
|
+
const filePath = path.join(directory, `component.${extension}`)
|
|
33
|
+
|
|
34
|
+
writeFileSync(filePath, source)
|
|
35
|
+
const eslint = new ESLint({
|
|
36
|
+
cwd: directory,
|
|
37
|
+
overrideConfigFile: true,
|
|
38
|
+
overrideConfig: [...solidConfig, { languageOptions: { parserOptions: { tsconfigRootDir: directory } } }],
|
|
39
|
+
})
|
|
40
|
+
const [result] = await eslint.lintFiles([filePath])
|
|
41
|
+
|
|
42
|
+
assert.equal(result.fatalErrorCount, 0, JSON.stringify(result.messages))
|
|
43
|
+
|
|
44
|
+
return result.messages
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
test('Solid preset is available through both public exports', async () => {
|
|
48
|
+
const { eslintSolid } = await import('@st1ggy/linter-config')
|
|
49
|
+
|
|
50
|
+
assert.equal(eslintSolid, solidConfig)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
for (const extension of ['jsx', 'tsx']) {
|
|
54
|
+
const annotation = extension === 'tsx' ? ': { name: string }' : ''
|
|
55
|
+
|
|
56
|
+
test(`Solid preset accepts reactive props in ${extension}`, async (context) => {
|
|
57
|
+
const messages = await lintSource(
|
|
58
|
+
context,
|
|
59
|
+
extension,
|
|
60
|
+
`export const Greeting = (props${annotation}) => <p>{props.name}</p>\n`,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
assert.deepEqual(messages, [])
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test(`Solid preset reports destructured props in ${extension}`, async (context) => {
|
|
67
|
+
const messages = await lintSource(
|
|
68
|
+
context,
|
|
69
|
+
extension,
|
|
70
|
+
`export const Greeting = ({ name }${annotation}) => <p>{name}</p>\n`,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert.ok(messages.some((message) => message.ruleId === 'solid/no-destructure' && message.severity === 2))
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test(`Solid preset reports lost reactivity in ${extension}`, async (context) => {
|
|
77
|
+
const messages = await lintSource(
|
|
78
|
+
context,
|
|
79
|
+
extension,
|
|
80
|
+
`export const Greeting = (props${annotation}) => {
|
|
81
|
+
const name = props.name
|
|
82
|
+
|
|
83
|
+
return <p>{name}</p>
|
|
84
|
+
}
|
|
85
|
+
`,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
assert.ok(messages.some((message) => message.ruleId === 'solid/reactivity' && message.severity === 1))
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test(`Solid preset recognizes components used in ${extension}`, async (context) => {
|
|
92
|
+
const messages = await lintSource(
|
|
93
|
+
context,
|
|
94
|
+
extension,
|
|
95
|
+
'const Greeting = () => <p>Hello</p>\n\nexport const App = () => <Greeting />\n',
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
assert.deepEqual(messages, [])
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
test('Solid stack generates wrappers, preserves them on init and replaces them on migrate', (context) => {
|
|
103
|
+
const directory = createProject(context)
|
|
104
|
+
const expected = {
|
|
105
|
+
'eslint.config.js': "export { default } from '@st1ggy/linter-config/eslint-solid';\n",
|
|
106
|
+
'prettier.config.js': "export { default } from '@st1ggy/linter-config/prettier-common';\n",
|
|
107
|
+
'stylelint.config.js': "export { default } from '@st1ggy/linter-config/stylelint-scss';\n",
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
run('init', directory, 'solid', { quiet: true })
|
|
111
|
+
|
|
112
|
+
for (const [file, content] of Object.entries(expected)) {
|
|
113
|
+
assert.equal(readFileSync(path.join(directory, file), 'utf8'), content)
|
|
114
|
+
writeFileSync(path.join(directory, file), '// existing config\n')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
run('init', directory, 'solid', { quiet: true })
|
|
118
|
+
|
|
119
|
+
for (const file of Object.keys(expected)) {
|
|
120
|
+
assert.equal(readFileSync(path.join(directory, file), 'utf8'), '// existing config\n')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
run('migrate', directory, 'solid', { quiet: true })
|
|
124
|
+
|
|
125
|
+
for (const [file, content] of Object.entries(expected)) {
|
|
126
|
+
assert.equal(readFileSync(path.join(directory, file), 'utf8'), content)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('Solid stack selects its integration plugin and rejects conflicting flags', () => {
|
|
131
|
+
assert.equal(resolveStackKey({ solid: true }), 'solid')
|
|
132
|
+
assert.deepEqual(stackPackages('solid'), ['@st1ggy/linter-config', 'eslint-plugin-solid'])
|
|
133
|
+
assert.throws(() => resolveStackKey({ solid: true, react: true }), /Pick at most one stack flag/)
|
|
134
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { defineConfig } from 'eslint/config'
|
|
2
|
+
import solidPlugin from 'eslint-plugin-solid'
|
|
3
|
+
|
|
4
|
+
export default defineConfig([
|
|
5
|
+
{
|
|
6
|
+
...solidPlugin.configs['flat/recommended'],
|
|
7
|
+
files: ['**/*.{js,jsx,mjs,cjs}'],
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
...solidPlugin.configs['flat/typescript'],
|
|
11
|
+
files: ['**/*.{ts,tsx,mts,cts}'],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
files: ['**/*.{jsx,tsx}'],
|
|
15
|
+
rules: {
|
|
16
|
+
'solid/self-closing-comp': 'error',
|
|
17
|
+
'@stylistic/jsx-closing-bracket-location': 'error',
|
|
18
|
+
'@stylistic/jsx-closing-tag-location': 'error',
|
|
19
|
+
'@stylistic/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
|
|
20
|
+
'@stylistic/jsx-curly-newline': ['error', { multiline: 'consistent', singleline: 'forbid' }],
|
|
21
|
+
'@stylistic/jsx-curly-spacing': [
|
|
22
|
+
'error',
|
|
23
|
+
{
|
|
24
|
+
when: 'never',
|
|
25
|
+
attributes: { allowMultiline: false },
|
|
26
|
+
children: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
'@stylistic/jsx-equals-spacing': ['error', 'never'],
|
|
30
|
+
'@stylistic/jsx-first-prop-new-line': ['error', 'multiline'],
|
|
31
|
+
'@stylistic/jsx-max-props-per-line': ['error', { when: 'multiline' }],
|
|
32
|
+
'@stylistic/no-multi-spaces': 'error',
|
|
33
|
+
'@stylistic/jsx-tag-spacing': 'error',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
])
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as eslintCommon } from './eslint/eslint.config.common.js'
|
|
2
2
|
export { default as eslintReact } from './eslint/eslint.config.react.js'
|
|
3
|
+
export { default as eslintSolid } from './eslint/eslint.config.solid.js'
|
|
3
4
|
export { default as eslintNext } from './eslint/eslint.config.next.js'
|
|
4
5
|
export { default as eslintSvelte } from './eslint/eslint-svelte.config.js'
|
|
5
6
|
export { default as eslintAstro } from './eslint/eslint.config.astro.js'
|