@st1ggy/linter-config 3.0.0 → 5.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 +77 -35
- package/index.d.ts +32 -0
- package/package.json +88 -31
- package/packages/biome/LICENSE +201 -0
- package/packages/biome/biome.json +5 -0
- package/packages/biome/configs/biome.common.json +44 -0
- package/packages/biome/configs/biome.next.json +10 -0
- package/packages/biome/configs/biome.react.json +12 -0
- package/packages/biome/configs/biome.svelte.json +23 -0
- package/packages/biome/docs/README.md +29 -0
- package/packages/eslint/data/linter-config-inventory.json +9325 -0
- package/packages/eslint/docs/RULE_MAPPING.md +50 -0
- package/packages/eslint/scripts/init-common.sh +4 -0
- package/packages/eslint/scripts/init-next.sh +4 -0
- package/packages/eslint/scripts/init-react.sh +4 -0
- package/packages/eslint/scripts/init-svelte.sh +4 -0
- package/packages/eslint/scripts/inventory.mjs +178 -0
- package/packages/eslint/scripts/linter-init.mjs +209 -0
- package/{src → packages/eslint/src}/eslint/configs/config.prettier.js +2 -2
- package/{src → packages/eslint/src}/eslint/configs/config.typescript.js +2 -2
- package/packages/eslint/src/eslint/configs/constants.js +7 -0
- package/{src/eslint/configs/config.svelte.js → packages/eslint/src/eslint/configs/svelte-stack.js} +2 -7
- package/{src/eslint/eslint.config.svelte.js → packages/eslint/src/eslint/eslint-svelte.config.js} +1 -1
- package/packages/eslint/src/examples/example.scss +3 -0
- package/packages/eslint/src/examples/example.svelte +5 -0
- package/{src → packages/eslint/src}/index.js +2 -2
- package/scripts/README.md +38 -0
- package/scripts/init-common.sh +0 -12
- package/scripts/init-react.sh +0 -12
- package/scripts/init-svelte.sh +0 -12
- package/src/eslint/configs/constants.js +0 -1
- /package/{scripts → packages/eslint/scripts}/remove-current.sh +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.common.js +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.gitignore.js +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.import.js +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.next.js +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.react.js +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.sonarjs.js +0 -0
- /package/{src → packages/eslint/src}/eslint/configs/config.unicorn.js +0 -0
- /package/{src → packages/eslint/src}/eslint/eslint.config.common.js +0 -0
- /package/{src → packages/eslint/src}/eslint/eslint.config.next.js +0 -0
- /package/{src → packages/eslint/src}/eslint/eslint.config.react.js +0 -0
- /package/{src → packages/eslint/src}/examples/example.ts +0 -0
- /package/{src/prettier/prettier.config.svelte.js → packages/eslint/src/prettier/prettier-svelte.config.js} +0 -0
- /package/{src → packages/eslint/src}/prettier/prettier.config.common.js +0 -0
- /package/{src → packages/eslint/src}/stylelint/stylelint.config.scss.js +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# ESLint / Stylelint → Biome mapping (`@st1ggy/linter-config/biome-*`)
|
|
2
|
+
|
|
3
|
+
This document is the **high-level matrix**. The machine-readable inventory (every flat-config rule id and Stylelint rule name with effective severity for sample files) lives in [`../data/linter-config-inventory.json`](../data/linter-config-inventory.json). Regenerate it from a checkout of this repo with:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
node packages/eslint/scripts/inventory.mjs
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
(`LINTER_CONFIG_ROOT` overrides the default `packages/eslint` directory.)
|
|
10
|
+
|
|
11
|
+
## Legend
|
|
12
|
+
|
|
13
|
+
| Status | Meaning |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| **yes** | Reasonable Biome coverage for the intent (names/options may still differ). |
|
|
16
|
+
| **partial** | Some overlap; not a drop-in replacement—review Biome rule docs. |
|
|
17
|
+
| **no** | No equivalent in Biome at preset level—**intentional gap** in `@st1ggy/linter-config/biome-*` (Biome presets do not add other linters or tsc). |
|
|
18
|
+
|
|
19
|
+
## Flat-config layers
|
|
20
|
+
|
|
21
|
+
| Layer | `linter-config` entry | Biome | Notes |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| Base + stylistic | `eslint-common` (`config.common.js`, `@eslint/js` recommended, `@stylistic` all + overrides) | **partial** | Biome formatter + `linter.rules`; no `@stylistic/*` parity; no universal `no-restricted-syntax` AST bans. |
|
|
24
|
+
| TypeScript | `config.typescript.js` | **partial** | Type-aware tseslint rules are not reproduced 1:1. |
|
|
25
|
+
| Imports | `config.import.js` (import-x) | **partial** | Biome import organization differs from import-x groups/resolvers. |
|
|
26
|
+
| Prettier-in-ESLint | `config.prettier.js` | **partial** | Biome is the formatter; semantics differ from `eslint-plugin-prettier`. |
|
|
27
|
+
| Unicorn | `config.unicorn.js` | **no** | No unicorn plugin in Biome. |
|
|
28
|
+
| SonarJS | `config.sonarjs.js` | **partial** | Complexity-style overlap only; not Sonar parity. |
|
|
29
|
+
| React | `config.react.js` | **partial** | React/JSX rules differ from `eslint-plugin-react` + stylistic JSX block. |
|
|
30
|
+
| Next.js | `config.next.js` | **no** | `@next/eslint-plugin-next` rules are not available in Biome. |
|
|
31
|
+
| Svelte | `svelte-stack.js` | **partial** | Depends on Biome Svelte support for your version; not `eslint-plugin-svelte` parity. |
|
|
32
|
+
|
|
33
|
+
Inventory rule counts (sample files in `linter-config` tree):
|
|
34
|
+
|
|
35
|
+
- `eslint-common`: effective rules on `src/examples/example.ts`
|
|
36
|
+
- `eslint-react`: includes React layer on the same sample
|
|
37
|
+
- `eslint-next`: Next layer on the same sample
|
|
38
|
+
- `eslint-svelte`: effective rules on `src/examples/example.svelte`
|
|
39
|
+
|
|
40
|
+
## Stylelint (SCSS preset)
|
|
41
|
+
|
|
42
|
+
| Layer | `linter-config` | Biome | Notes |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| SCSS stack | `stylelint.config.scss.js` (recommended + scss + sass-guidelines + clean-order + plugins) | **partial** | Biome CSS/SCSS linting only—no clean-order / browserslist plugin / stylelint-prettier parity. |
|
|
45
|
+
|
|
46
|
+
Inventory: Stylelint rule entries after `extends` resolution on `src/examples/example.scss`.
|
|
47
|
+
|
|
48
|
+
## Intentional product stance
|
|
49
|
+
|
|
50
|
+
`@st1ggy/linter-config/biome-*` presets ship **Biome only**. Anything marked **no** or incomplete **partial** is either approximated by Biome where practical or listed as a documented gap. Consumers may run other tools in their own repositories; those tools are **not** part of the Biome JSON exports.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Collects effective flat-config rule lists and Stylelint rules from this workspace.
|
|
2
|
+
// Run from repo root: `node packages/eslint/scripts/inventory.mjs`
|
|
3
|
+
// Or set `LINTER_CONFIG_ROOT` to override the default (`packages/eslint`).
|
|
4
|
+
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
7
|
+
import { createRequire } from 'node:module'
|
|
8
|
+
import path from 'node:path'
|
|
9
|
+
import process from 'node:process'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
13
|
+
const LINTER_ROOT = process.env.LINTER_CONFIG_ROOT ?? path.resolve(__dirname, '..')
|
|
14
|
+
|
|
15
|
+
const SOURCE_LABEL = '@st1ggy/linter-config@5.0.0 (local tree)'
|
|
16
|
+
|
|
17
|
+
const flatConfigPresets = [
|
|
18
|
+
{
|
|
19
|
+
id: 'eslint-common',
|
|
20
|
+
module: 'src/eslint/eslint.config.common.js',
|
|
21
|
+
sampleFile: 'src/examples/example.ts',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'eslint-react',
|
|
25
|
+
module: 'src/eslint/eslint.config.react.js',
|
|
26
|
+
sampleFile: 'src/examples/example.ts',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'eslint-next',
|
|
30
|
+
module: 'src/eslint/eslint.config.next.js',
|
|
31
|
+
sampleFile: 'src/examples/example.ts',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'eslint-svelte',
|
|
35
|
+
module: 'src/eslint/eslint-svelte.config.js',
|
|
36
|
+
sampleFile: 'src/examples/example.svelte',
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
function compareKeysAlphabetically(left, right) {
|
|
41
|
+
return left.localeCompare(right)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function severityFromNumberOrString(sev) {
|
|
45
|
+
if (sev === 0 || sev === 'off') {
|
|
46
|
+
return 'off'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (sev === 1 || sev === 'warn') {
|
|
50
|
+
return 'warn'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (sev === 2 || sev === 'error') {
|
|
54
|
+
return 'error'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return sev
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeRuleValue(value) {
|
|
61
|
+
if (value === undefined) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!Array.isArray(value)) {
|
|
66
|
+
return severityFromNumberOrString(value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const [sev, ...rest] = value
|
|
70
|
+
const normalized = severityFromNumberOrString(sev)
|
|
71
|
+
|
|
72
|
+
if (rest.length > 0) {
|
|
73
|
+
return [normalized, ...rest]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return normalized
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function rulesToSortedObject(rules) {
|
|
80
|
+
const out = {}
|
|
81
|
+
const keys = Object.keys(rules).toSorted(compareKeysAlphabetically)
|
|
82
|
+
|
|
83
|
+
for (const key of keys) {
|
|
84
|
+
out[key] = normalizeRuleValue(rules[key])
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return out
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function inventoryFlatConfigs() {
|
|
91
|
+
process.chdir(LINTER_ROOT)
|
|
92
|
+
const { ESLint } = await import('eslint')
|
|
93
|
+
const results = {}
|
|
94
|
+
|
|
95
|
+
for (const preset of flatConfigPresets) {
|
|
96
|
+
const modulePath = path.join(LINTER_ROOT, preset.module)
|
|
97
|
+
const loaded = await import(modulePath)
|
|
98
|
+
const config = loaded.default
|
|
99
|
+
const engine = new ESLint({
|
|
100
|
+
cwd: LINTER_ROOT,
|
|
101
|
+
overrideConfig: config,
|
|
102
|
+
})
|
|
103
|
+
const filePath = path.join(LINTER_ROOT, preset.sampleFile)
|
|
104
|
+
const calculated = await engine.calculateConfigForFile(filePath)
|
|
105
|
+
const rules = rulesToSortedObject(calculated.rules ?? {})
|
|
106
|
+
|
|
107
|
+
results[preset.id] = {
|
|
108
|
+
module: preset.module,
|
|
109
|
+
sampleFile: preset.sampleFile,
|
|
110
|
+
ruleCount: Object.keys(rules).length,
|
|
111
|
+
rules,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return results
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function inventoryStylelint() {
|
|
119
|
+
const requireFromWorkspace = createRequire(path.join(LINTER_ROOT, 'package.json'))
|
|
120
|
+
const stylelintPackageJsonPath = requireFromWorkspace.resolve('stylelint/package.json')
|
|
121
|
+
const stylelintBin = path.join(path.dirname(stylelintPackageJsonPath), 'bin/stylelint.mjs')
|
|
122
|
+
const scssPath = path.join(LINTER_ROOT, 'src/examples/example.scss')
|
|
123
|
+
const configPath = path.join(LINTER_ROOT, 'src/stylelint/stylelint.config.scss.js')
|
|
124
|
+
const spawned = spawnSync(process.execPath, [stylelintBin, '--print-config', scssPath, '--config', configPath], {
|
|
125
|
+
cwd: LINTER_ROOT,
|
|
126
|
+
encoding: 'utf8',
|
|
127
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
if (spawned.status !== 0) {
|
|
131
|
+
throw new Error(`stylelint --print-config failed: ${spawned.stderr || spawned.stdout || 'unknown error'}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const printed = JSON.parse(spawned.stdout)
|
|
135
|
+
const rawRules = printed.rules ?? {}
|
|
136
|
+
const rules = {}
|
|
137
|
+
const keys = Object.keys(rawRules).toSorted(compareKeysAlphabetically)
|
|
138
|
+
|
|
139
|
+
for (const key of keys) {
|
|
140
|
+
rules[key] = rawRules[key]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
configFile: 'src/stylelint/stylelint.config.scss.js',
|
|
145
|
+
sampleFile: 'src/examples/example.scss',
|
|
146
|
+
ruleCount: Object.keys(rules).length,
|
|
147
|
+
rules,
|
|
148
|
+
plugins: printed.plugins ?? [],
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function main() {
|
|
153
|
+
const outputDirectory = path.join(LINTER_ROOT, 'data')
|
|
154
|
+
|
|
155
|
+
mkdirSync(outputDirectory, { recursive: true })
|
|
156
|
+
|
|
157
|
+
const flatConfigs = await inventoryFlatConfigs()
|
|
158
|
+
const stylelint = inventoryStylelint()
|
|
159
|
+
|
|
160
|
+
const meta = {
|
|
161
|
+
generatedAt: new Date().toISOString(),
|
|
162
|
+
source: SOURCE_LABEL,
|
|
163
|
+
linterConfigRoot: LINTER_ROOT,
|
|
164
|
+
flatConfigs,
|
|
165
|
+
stylelint,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const outputPath = path.join(outputDirectory, 'linter-config-inventory.json')
|
|
169
|
+
const body = `${JSON.stringify(meta, null, 2)}\n`
|
|
170
|
+
|
|
171
|
+
writeFileSync(outputPath, body, 'utf8')
|
|
172
|
+
|
|
173
|
+
process.stdout.write(
|
|
174
|
+
`Wrote packages/eslint/data/linter-config-inventory.json (${SOURCE_LABEL}, flat presets: ${Object.keys(flatConfigs).length}, stylelint rules: ${stylelint.ruleCount})\n`,
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await main()
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generates wrapper configs in a consumer project that depend on @st1ggy/linter-config subpath exports.
|
|
3
|
+
// Usage: linter-config <init|reinit|create> [--eslint] [--biome] [--common|--react|--next|--svelte] [--dir path]
|
|
4
|
+
// With --eslint: writes eslint.config.js, prettier.config.js, stylelint.config.js (one stack).
|
|
5
|
+
// With --biome: writes biome.json (same stack key).
|
|
6
|
+
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
8
|
+
import path from 'node:path'
|
|
9
|
+
import process from 'node:process'
|
|
10
|
+
import { parseArgs } from 'node:util'
|
|
11
|
+
|
|
12
|
+
const PACKAGE = '@st1ggy/linter-config'
|
|
13
|
+
|
|
14
|
+
const STACK_KEYS = ['common', 'react', 'next', 'svelte']
|
|
15
|
+
|
|
16
|
+
const STACKS = {
|
|
17
|
+
common: {
|
|
18
|
+
eslint: 'eslint-common',
|
|
19
|
+
prettier: 'prettier-common',
|
|
20
|
+
stylelint: 'stylelint-scss',
|
|
21
|
+
biome: 'biome-common',
|
|
22
|
+
},
|
|
23
|
+
react: {
|
|
24
|
+
eslint: 'eslint-react',
|
|
25
|
+
prettier: 'prettier-common',
|
|
26
|
+
stylelint: 'stylelint-scss',
|
|
27
|
+
biome: 'biome-react',
|
|
28
|
+
},
|
|
29
|
+
next: {
|
|
30
|
+
eslint: 'eslint-next',
|
|
31
|
+
prettier: 'prettier-common',
|
|
32
|
+
stylelint: 'stylelint-scss',
|
|
33
|
+
biome: 'biome-next',
|
|
34
|
+
},
|
|
35
|
+
svelte: {
|
|
36
|
+
eslint: 'eslint-svelte',
|
|
37
|
+
prettier: 'prettier-svelte',
|
|
38
|
+
stylelint: 'stylelint-scss',
|
|
39
|
+
biome: 'biome-svelte',
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const BIOME_SCHEMA = 'https://biomejs.dev/schemas/2.4.11/schema.json'
|
|
44
|
+
|
|
45
|
+
function printHelp() {
|
|
46
|
+
process.stdout.write(`\
|
|
47
|
+
${PACKAGE} — generate local wrapper configs in a consumer project.
|
|
48
|
+
|
|
49
|
+
Commands:
|
|
50
|
+
init Create missing files only (skip existing).
|
|
51
|
+
reinit Overwrite selected files.
|
|
52
|
+
create Same as init.
|
|
53
|
+
|
|
54
|
+
Pick at least one toolchain:
|
|
55
|
+
--eslint Write eslint.config.js, prettier.config.js, and stylelint.config.js (same stack).
|
|
56
|
+
--biome Write biome.json.
|
|
57
|
+
|
|
58
|
+
Stack (at most one; default: common):
|
|
59
|
+
--common | --react | --next | --svelte
|
|
60
|
+
|
|
61
|
+
Options:
|
|
62
|
+
--dir, -d Target directory (default: current working directory).
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
npx linter-config init --eslint --common
|
|
66
|
+
npx linter-config init --biome --svelte
|
|
67
|
+
npx linter-config init --eslint --biome --next
|
|
68
|
+
npx linter-config reinit --eslint --biome --react --dir ./apps/web
|
|
69
|
+
|
|
70
|
+
Repo (paths from root):
|
|
71
|
+
node packages/eslint/scripts/linter-init.mjs init --eslint --biome --common
|
|
72
|
+
`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function jsReExport(subpath) {
|
|
76
|
+
return `export { default } from '${PACKAGE}/${subpath}';\n`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function biomeJson(subpath) {
|
|
80
|
+
const lines = [
|
|
81
|
+
'{',
|
|
82
|
+
` "$schema": "${BIOME_SCHEMA}",`,
|
|
83
|
+
' "root": true,',
|
|
84
|
+
` "extends": ["${PACKAGE}/${subpath}"]`,
|
|
85
|
+
'}',
|
|
86
|
+
'',
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
return lines.join('\n')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function resolveTargetDirectory(raw) {
|
|
93
|
+
return path.resolve(raw)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function writeFile(targetDirectory, name, content, overwrite) {
|
|
97
|
+
const filePath = path.join(targetDirectory, name)
|
|
98
|
+
|
|
99
|
+
if (!overwrite && existsSync(filePath)) {
|
|
100
|
+
process.stdout.write(`skip (exists): ${name}\n`)
|
|
101
|
+
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
mkdirSync(targetDirectory, { recursive: true })
|
|
106
|
+
writeFileSync(filePath, content, 'utf8')
|
|
107
|
+
process.stdout.write(`write: ${name}\n`)
|
|
108
|
+
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resolveStackKey(values) {
|
|
113
|
+
const chosen = []
|
|
114
|
+
|
|
115
|
+
for (const key of STACK_KEYS) {
|
|
116
|
+
if (values[key] === true) {
|
|
117
|
+
chosen.push(key)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (chosen.length === 0) {
|
|
122
|
+
return 'common'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (chosen.length > 1) {
|
|
126
|
+
const flags = STACK_KEYS.map((key) => `--${key}`).join(', ')
|
|
127
|
+
|
|
128
|
+
throw new Error(`Pick at most one stack flag: ${flags}`)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return chosen[0]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function run(mode, targetDirectory, wantEslint, wantBiome, stackKey) {
|
|
135
|
+
const stack = STACKS[stackKey]
|
|
136
|
+
|
|
137
|
+
if (!stack) {
|
|
138
|
+
throw new Error(`Unknown stack "${stackKey}". Use: ${STACK_KEYS.join(', ')}`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!wantEslint && !wantBiome) {
|
|
142
|
+
throw new Error('Pick at least one of: --eslint, --biome')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const overwrite = mode === 'reinit'
|
|
146
|
+
|
|
147
|
+
if (wantEslint) {
|
|
148
|
+
writeFile(targetDirectory, 'eslint.config.js', jsReExport(stack.eslint), overwrite)
|
|
149
|
+
writeFile(targetDirectory, 'prettier.config.js', jsReExport(stack.prettier), overwrite)
|
|
150
|
+
writeFile(targetDirectory, 'stylelint.config.js', jsReExport(stack.stylelint), overwrite)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (wantBiome) {
|
|
154
|
+
writeFile(targetDirectory, 'biome.json', biomeJson(stack.biome), overwrite)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function main() {
|
|
159
|
+
const args = process.argv.slice(2)
|
|
160
|
+
|
|
161
|
+
if (args.length === 0 || args[0] === '-h' || args[0] === '--help') {
|
|
162
|
+
printHelp()
|
|
163
|
+
process.exitCode = args.length === 0 ? 1 : 0
|
|
164
|
+
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const command = args[0]
|
|
169
|
+
|
|
170
|
+
if (command !== 'init' && command !== 'reinit' && command !== 'create') {
|
|
171
|
+
process.stderr.write(`Unknown command "${command}".\n\n`)
|
|
172
|
+
printHelp()
|
|
173
|
+
process.exitCode = 1
|
|
174
|
+
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const { values } = parseArgs({
|
|
179
|
+
args: args.slice(1),
|
|
180
|
+
options: {
|
|
181
|
+
dir: { type: 'string', short: 'd', default: process.cwd() },
|
|
182
|
+
eslint: { type: 'boolean' },
|
|
183
|
+
biome: { type: 'boolean' },
|
|
184
|
+
common: { type: 'boolean' },
|
|
185
|
+
react: { type: 'boolean' },
|
|
186
|
+
next: { type: 'boolean' },
|
|
187
|
+
svelte: { type: 'boolean' },
|
|
188
|
+
},
|
|
189
|
+
strict: true,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
const targetDirectory = resolveTargetDirectory(values.dir)
|
|
193
|
+
const stackKey = resolveStackKey(values)
|
|
194
|
+
const wantEslint = values.eslint === true
|
|
195
|
+
const wantBiome = values.biome === true
|
|
196
|
+
|
|
197
|
+
const cmd = command === 'create' ? 'init' : command
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
run(cmd, targetDirectory, wantEslint, wantBiome, stackKey)
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
203
|
+
|
|
204
|
+
process.stderr.write(`${message}\n`)
|
|
205
|
+
process.exitCode = 1
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
main()
|
|
@@ -2,12 +2,12 @@ import { defineConfig } from 'eslint/config'
|
|
|
2
2
|
import prettierConfig from 'eslint-config-prettier'
|
|
3
3
|
import prettierPlugin from 'eslint-plugin-prettier'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { filesPrettier } from './constants.js'
|
|
6
6
|
|
|
7
7
|
export default defineConfig([
|
|
8
8
|
prettierConfig,
|
|
9
9
|
{
|
|
10
|
-
files,
|
|
10
|
+
files: filesPrettier,
|
|
11
11
|
plugins: { prettier: prettierPlugin },
|
|
12
12
|
rules: { 'prettier/prettier': 'error' },
|
|
13
13
|
},
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { defineConfig } from 'eslint/config'
|
|
2
2
|
import { configs } from 'typescript-eslint'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { filesTypeAware } from './constants.js'
|
|
5
5
|
|
|
6
6
|
export default defineConfig([
|
|
7
7
|
...configs.recommended,
|
|
8
8
|
...configs.stylistic,
|
|
9
9
|
{
|
|
10
|
-
files,
|
|
10
|
+
files: filesTypeAware,
|
|
11
11
|
languageOptions: {
|
|
12
12
|
parserOptions: {
|
|
13
13
|
project: ['tsconfig.json'],
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const files = ['**/*.{js,jsx,ts,tsx,mts,cts,mjs,cjs,vue,svelte}']
|
|
2
|
+
|
|
3
|
+
/** For `parserOptions.project` / type-aware @typescript-eslint rules (omit `.svelte`; Svelte scripts use svelte-eslint-parser). */
|
|
4
|
+
export const filesTypeAware = ['**/*.{js,jsx,ts,tsx,mts,cts,mjs,cjs,vue}']
|
|
5
|
+
|
|
6
|
+
/** Prettier run via eslint-plugin-prettier (omit `.svelte` unless Prettier is configured with `prettier-plugin-svelte`). */
|
|
7
|
+
export const filesPrettier = ['**/*.{js,jsx,ts,tsx,mts,cts,mjs,cjs,vue}']
|
package/{src/eslint/configs/config.svelte.js → packages/eslint/src/eslint/configs/svelte-stack.js}
RENAMED
|
@@ -2,31 +2,26 @@ import { defineConfig } from 'eslint/config'
|
|
|
2
2
|
import sveltePlugin from 'eslint-plugin-svelte'
|
|
3
3
|
import { parser } from 'typescript-eslint'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const svelteFiles = ['**/*.svelte', '**/*.svelte.ts']
|
|
6
6
|
|
|
7
7
|
const extensions = ['.js']
|
|
8
8
|
|
|
9
9
|
export default defineConfig([
|
|
10
10
|
sveltePlugin.configs['flat/recommended'],
|
|
11
11
|
{
|
|
12
|
-
files,
|
|
12
|
+
files: svelteFiles,
|
|
13
13
|
rules: {
|
|
14
14
|
'import-x/named': 'off',
|
|
15
15
|
'import-x/no-cycle': 'off',
|
|
16
16
|
},
|
|
17
17
|
languageOptions: {
|
|
18
18
|
parserOptions: {
|
|
19
|
-
project: ['tsconfig.json'],
|
|
20
19
|
projectService: true,
|
|
21
20
|
extraFileExtensions: ['.svelte', '.svelte.js', '.svelte.ts'],
|
|
22
21
|
parser,
|
|
23
22
|
},
|
|
24
23
|
},
|
|
25
24
|
settings: {
|
|
26
|
-
// 'import-x/parsers': {
|
|
27
|
-
// 'svelte-eslint-parser': ['.svelte'],
|
|
28
|
-
// espree: extensions,
|
|
29
|
-
// },
|
|
30
25
|
'import-x/resolver': {
|
|
31
26
|
'eslint-import-resolver-custom-alias': {
|
|
32
27
|
alias: {
|
|
@@ -1,9 +1,9 @@
|
|
|
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
3
|
export { default as eslintNext } from './eslint/eslint.config.next.js'
|
|
4
|
-
export { default as eslintSvelte } from './eslint/eslint.config.
|
|
4
|
+
export { default as eslintSvelte } from './eslint/eslint-svelte.config.js'
|
|
5
5
|
|
|
6
6
|
export { default as prettierCommon } from './prettier/prettier.config.common.js'
|
|
7
|
-
export { default as prettierSvelte } from './prettier/prettier.config.
|
|
7
|
+
export { default as prettierSvelte } from './prettier/prettier-svelte.config.js'
|
|
8
8
|
|
|
9
9
|
export { default as stylelintScss } from './stylelint/stylelint.config.scss.js'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Consumer config helpers (monorepo root)
|
|
2
|
+
|
|
3
|
+
From this repository root, use the npm scripts in the root [`package.json`](../package.json). They run the CLI with **`--eslint --biome --common`** (full wrapper set for this repo’s default stack):
|
|
4
|
+
|
|
5
|
+
| Script | Effect |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `npm run config:init` | `init` — create missing `eslint.config.js`, `prettier.config.js`, `stylelint.config.js`, `biome.json` |
|
|
8
|
+
| `npm run config:reinit` | `reinit` — overwrite those four files |
|
|
9
|
+
| `npm run config:create` | same as `config:init` |
|
|
10
|
+
|
|
11
|
+
Direct CLI (paths from repo root):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
node packages/eslint/scripts/linter-init.mjs init --eslint --biome --react
|
|
15
|
+
node packages/eslint/scripts/linter-init.mjs reinit --biome --svelte --dir ./my-app
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Flags (published package)
|
|
19
|
+
|
|
20
|
+
The **`linter-config`** binary is declared in [`package.json`](../package.json) `bin`. In another project:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx linter-config init --eslint --common
|
|
24
|
+
npx linter-config init --biome --next
|
|
25
|
+
npm exec linter-config -- reinit --eslint --biome --svelte
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Without installing first:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx --yes --package @st1ggy/linter-config linter-config init --eslint --react
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **`--eslint`** — `eslint.config.js` + `prettier.config.js` + `stylelint.config.js` (one stack).
|
|
35
|
+
- **`--biome`** — `biome.json`.
|
|
36
|
+
- **Stack (optional, default common):** `--common` | `--react` | `--next` | `--svelte` (at most one).
|
|
37
|
+
|
|
38
|
+
Shell shortcuts in [`packages/eslint/scripts/`](../packages/eslint/scripts/) (`init-common.sh`, etc.) call **`init --eslint`** with the matching stack only (no Biome).
|
package/scripts/init-common.sh
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
declare -A postfix_map=(
|
|
4
|
-
[eslint]="common"
|
|
5
|
-
[stylelint]="scss"
|
|
6
|
-
[prettier]="common"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
for l in eslint stylelint prettier; do
|
|
10
|
-
echo "creating $l.config.js with ${l}-${postfix_map[$l]}"
|
|
11
|
-
echo "export { default } from '@st1ggy/linter-config/${l}-${postfix_map[$l]}';" > $l.config.js;
|
|
12
|
-
done
|
package/scripts/init-react.sh
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
declare -A postfix_map=(
|
|
4
|
-
[eslint]="react"
|
|
5
|
-
[stylelint]="scss"
|
|
6
|
-
[prettier]="common"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
for l in eslint stylelint prettier; do
|
|
10
|
-
echo "creating $l.config.js with ${l}-${postfix_map[$l]}"
|
|
11
|
-
echo "export { default } from '@st1ggy/linter-config/${l}-${postfix_map[$l]}';" > $l.config.js;
|
|
12
|
-
done
|
package/scripts/init-svelte.sh
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
declare -A postfix_map=(
|
|
4
|
-
[eslint]="svelte"
|
|
5
|
-
[stylelint]="scss"
|
|
6
|
-
[prettier]="svelte"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
for l in eslint stylelint prettier; do
|
|
10
|
-
echo "creating $l.config.js with ${l}-${postfix_map[$l]}"
|
|
11
|
-
echo "export { default } from '@st1ggy/linter-config/${l}-${postfix_map[$l]}';" > $l.config.js;
|
|
12
|
-
done
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const files = ['**/*.{js,jsx,ts,tsx,mts,cts,mjs,cjs,vue,svelte}']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|