@st1ggy/linter-config 7.3.1 → 8.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 +127 -8
- package/data/oxlint-config-inventory.json +2268 -0
- package/data/oxlint-rule-map.json +91896 -0
- package/data/oxlint-source-inventory.json +107728 -0
- package/docs/OXLINT_RULE_MAPPING.md +2972 -0
- package/docs/RULE_MAPPING.md +15 -0
- package/package.json +68 -3
- package/scripts/README.md +37 -1
- package/scripts/init-astro-ox.sh +4 -0
- package/scripts/init-common-ox.sh +4 -0
- package/scripts/init-next-ox.sh +4 -0
- package/scripts/init-react-ox.sh +4 -0
- package/scripts/init-solid-ox.sh +4 -0
- package/scripts/init-svelte-ox.sh +4 -0
- package/scripts/linter-init-core.mjs +104 -24
- package/scripts/linter-init.mjs +24 -32
- package/src/oxfmt/index.d.ts +8 -0
- package/src/oxfmt/oxfmt.config.common.js +16 -0
- package/src/oxlint/configs/config.astro.js +4093 -0
- package/src/oxlint/configs/config.common.js +3206 -0
- package/src/oxlint/configs/config.next.js +3588 -0
- package/src/oxlint/configs/config.react.js +3499 -0
- package/src/oxlint/configs/config.solid.js +4972 -0
- package/src/oxlint/configs/config.svelte.js +3302 -0
- package/src/oxlint/create-config.js +40 -0
- package/src/oxlint/index.d.ts +49 -0
- package/src/oxlint/oxlint.config.astro.js +4 -0
- package/src/oxlint/oxlint.config.common.js +4 -0
- package/src/oxlint/oxlint.config.next.js +4 -0
- package/src/oxlint/oxlint.config.react.js +4 -0
- package/src/oxlint/oxlint.config.solid.js +4 -0
- package/src/oxlint/oxlint.config.svelte.js +4 -0
- package/src/oxlint/plugins/astro.js +41 -0
- package/src/oxlint/plugins/eslint-core.js +5 -0
- package/src/oxlint/plugins/fresh-source-code.js +30 -0
- package/src/oxlint/plugins/import-x.js +38 -0
- package/src/oxlint/plugins/next.js +1 -0
- package/src/oxlint/plugins/prettier.js +1 -0
- package/src/oxlint/plugins/react-hooks.js +1 -0
- package/src/oxlint/plugins/react.js +1 -0
- package/src/oxlint/plugins/solid.js +1 -0
- package/src/oxlint/plugins/sonarjs.js +5 -0
- package/src/oxlint/plugins/stylistic.js +1 -0
- package/src/oxlint/plugins/svelte.js +42 -0
- package/src/oxlint/plugins/typescript.js +1 -0
- package/src/oxlint/plugins/unicorn.js +5 -0
- package/src/oxlint/stacks.js +17 -0
- package/scripts/package-smoke.mjs +0 -56
- package/scripts/solid-config.test.mjs +0 -134
package/docs/RULE_MAPPING.md
CHANGED
|
@@ -33,6 +33,21 @@ Inventory rule counts (sample files in this repository):
|
|
|
33
33
|
- `eslint-svelte`: effective rules on `src/examples/example.svelte`
|
|
34
34
|
- `eslint-astro`: effective rules on `src/examples/example.astro`
|
|
35
35
|
|
|
36
|
+
## Oxlint counterparts
|
|
37
|
+
|
|
38
|
+
The package also exports `common-ox`, `react-ox`, `solid-ox`, `next-ox`, `svelte-ox` and `astro-ox`. Each is one `OxlintConfig` object, used through `oxlint.config.ts`, with `options.typeAware: true` and the native TS7 tsgolint engine. The original ESLint presets remain available.
|
|
39
|
+
|
|
40
|
+
The new variants use explicit migrated rules plus compatible JS adapters. Common/React/Solid/Next pair with `oxfmt-common` and a separate `oxfmt --check`; their source `prettier/prettier` check is recorded as `formatter` in the audit instead of running a Prettier JS plugin. Svelte/Astro keep their existing Prettier selections. Every stack retains Stylelint. See [OXLINT_RULE_MAPPING.md](OXLINT_RULE_MAPPING.md) for source/target correspondence, per-scope counts, partial mappings and unsupported rules. Native script extraction does not provide Svelte/Astro template linting.
|
|
41
|
+
|
|
42
|
+
Regenerate and verify the Oxlint inventory with:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm run generate:ox
|
|
46
|
+
npm run inventory:ox
|
|
47
|
+
npm run generate:ox:check
|
|
48
|
+
npm run inventory:ox:check
|
|
49
|
+
```
|
|
50
|
+
|
|
36
51
|
## Stylelint (SCSS preset)
|
|
37
52
|
|
|
38
53
|
| Layer | `@st1ggy/linter-config` | Notes |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@st1ggy/linter-config",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "ESLint, Prettier, and Stylelint
|
|
3
|
+
"version": "8.0.0",
|
|
4
|
+
"description": "Shareable ESLint, Oxlint, Oxfmt, Prettier, and Stylelint configs.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -93,11 +93,46 @@
|
|
|
93
93
|
"import": "./src/eslint/eslint.config.common.js",
|
|
94
94
|
"default": "./src/eslint/eslint.config.common.js"
|
|
95
95
|
},
|
|
96
|
+
"./common-ox": {
|
|
97
|
+
"types": "./src/oxlint/index.d.ts",
|
|
98
|
+
"import": "./src/oxlint/oxlint.config.common.js",
|
|
99
|
+
"default": "./src/oxlint/oxlint.config.common.js"
|
|
100
|
+
},
|
|
101
|
+
"./react-ox": {
|
|
102
|
+
"types": "./src/oxlint/index.d.ts",
|
|
103
|
+
"import": "./src/oxlint/oxlint.config.react.js",
|
|
104
|
+
"default": "./src/oxlint/oxlint.config.react.js"
|
|
105
|
+
},
|
|
106
|
+
"./solid-ox": {
|
|
107
|
+
"types": "./src/oxlint/index.d.ts",
|
|
108
|
+
"import": "./src/oxlint/oxlint.config.solid.js",
|
|
109
|
+
"default": "./src/oxlint/oxlint.config.solid.js"
|
|
110
|
+
},
|
|
111
|
+
"./next-ox": {
|
|
112
|
+
"types": "./src/oxlint/index.d.ts",
|
|
113
|
+
"import": "./src/oxlint/oxlint.config.next.js",
|
|
114
|
+
"default": "./src/oxlint/oxlint.config.next.js"
|
|
115
|
+
},
|
|
116
|
+
"./svelte-ox": {
|
|
117
|
+
"types": "./src/oxlint/index.d.ts",
|
|
118
|
+
"import": "./src/oxlint/oxlint.config.svelte.js",
|
|
119
|
+
"default": "./src/oxlint/oxlint.config.svelte.js"
|
|
120
|
+
},
|
|
121
|
+
"./astro-ox": {
|
|
122
|
+
"types": "./src/oxlint/index.d.ts",
|
|
123
|
+
"import": "./src/oxlint/oxlint.config.astro.js",
|
|
124
|
+
"default": "./src/oxlint/oxlint.config.astro.js"
|
|
125
|
+
},
|
|
96
126
|
"./prettier-common": {
|
|
97
127
|
"types": "./index.d.ts",
|
|
98
128
|
"import": "./src/prettier/prettier.config.common.js",
|
|
99
129
|
"default": "./src/prettier/prettier.config.common.js"
|
|
100
130
|
},
|
|
131
|
+
"./oxfmt-common": {
|
|
132
|
+
"types": "./src/oxfmt/index.d.ts",
|
|
133
|
+
"import": "./src/oxfmt/oxfmt.config.common.js",
|
|
134
|
+
"default": "./src/oxfmt/oxfmt.config.common.js"
|
|
135
|
+
},
|
|
101
136
|
"./prettier-svelte": {
|
|
102
137
|
"types": "./index.d.ts",
|
|
103
138
|
"import": "./src/prettier/prettier-svelte.config.js",
|
|
@@ -119,7 +154,12 @@
|
|
|
119
154
|
"src",
|
|
120
155
|
"scripts",
|
|
121
156
|
"data",
|
|
122
|
-
"docs"
|
|
157
|
+
"docs",
|
|
158
|
+
"!scripts/fixtures",
|
|
159
|
+
"!scripts/*.test.mjs",
|
|
160
|
+
"!scripts/oxlint-*.mjs",
|
|
161
|
+
"!scripts/oxfmt-*.mjs",
|
|
162
|
+
"!scripts/package-smoke.mjs"
|
|
123
163
|
],
|
|
124
164
|
"scripts": {
|
|
125
165
|
"lint": "eslint . && stylelint \"src/**/*.{css,scss}\" --config ./src/stylelint/stylelint.config.scss.js --allow-empty-input && prettier --check .",
|
|
@@ -132,6 +172,14 @@
|
|
|
132
172
|
"changelog:dry": "conventional-changelog -p conventionalcommits",
|
|
133
173
|
"publish:npm": "npm publish --access public",
|
|
134
174
|
"inventory": "node ./scripts/inventory.mjs",
|
|
175
|
+
"typecheck:ox": "node ./node_modules/@typescript/native/bin/tsc --noEmit",
|
|
176
|
+
"generate:ox": "node scripts/oxlint-generate.mjs",
|
|
177
|
+
"generate:ox:check": "node scripts/oxlint-generate.mjs --check",
|
|
178
|
+
"inventory:ox": "node scripts/oxlint-inventory.mjs --write",
|
|
179
|
+
"inventory:ox:check": "node scripts/oxlint-inventory.mjs --source-only --check && node scripts/oxlint-inventory.mjs --check",
|
|
180
|
+
"test:ox": "node --test --test-concurrency=2 scripts/oxlint-*.test.mjs",
|
|
181
|
+
"format:ox:compare": "node scripts/oxfmt-compare.mjs",
|
|
182
|
+
"format:ox:check": "oxfmt --config ./src/oxfmt/oxfmt.config.common.js --check .",
|
|
135
183
|
"config:init": "node ./scripts/linter-init.mjs init --common --dir ./examples/init-smoke",
|
|
136
184
|
"config:migrate": "node ./scripts/linter-init.mjs migrate --common --dir ./examples/init-smoke",
|
|
137
185
|
"config:reinit": "node ./scripts/linter-init.mjs reinit --common --dir ./examples/init-smoke",
|
|
@@ -148,6 +196,9 @@
|
|
|
148
196
|
"eslint-plugin-solid": "^0.18.0",
|
|
149
197
|
"eslint-plugin-svelte": "^3.22.0",
|
|
150
198
|
"next": "^16.3.0",
|
|
199
|
+
"oxlint": "1.86.0",
|
|
200
|
+
"oxfmt": "0.71.0",
|
|
201
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
151
202
|
"prettier": "^3.9.6",
|
|
152
203
|
"prettier-plugin-astro": "^0.14.1",
|
|
153
204
|
"prettier-plugin-svelte": "^4.1.1",
|
|
@@ -180,6 +231,15 @@
|
|
|
180
231
|
"next": {
|
|
181
232
|
"optional": true
|
|
182
233
|
},
|
|
234
|
+
"oxlint": {
|
|
235
|
+
"optional": true
|
|
236
|
+
},
|
|
237
|
+
"oxfmt": {
|
|
238
|
+
"optional": true
|
|
239
|
+
},
|
|
240
|
+
"oxlint-tsgolint": {
|
|
241
|
+
"optional": true
|
|
242
|
+
},
|
|
183
243
|
"astro": {
|
|
184
244
|
"optional": true
|
|
185
245
|
},
|
|
@@ -203,6 +263,8 @@
|
|
|
203
263
|
}
|
|
204
264
|
},
|
|
205
265
|
"devDependencies": {
|
|
266
|
+
"@typescript/native": "npm:typescript@7.0.2",
|
|
267
|
+
"pnpm": "10.32.1",
|
|
206
268
|
"@next/eslint-plugin-next": "^16.3.0",
|
|
207
269
|
"@stylistic/eslint-plugin-migrate": "^4.4.1",
|
|
208
270
|
"conventional-changelog": "^8.1.0",
|
|
@@ -213,6 +275,9 @@
|
|
|
213
275
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
214
276
|
"eslint-plugin-solid": "^0.18.0",
|
|
215
277
|
"eslint-plugin-svelte": "^3.22.0",
|
|
278
|
+
"oxlint": "1.86.0",
|
|
279
|
+
"oxfmt": "0.71.0",
|
|
280
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
216
281
|
"prettier-plugin-astro": "^0.14.1",
|
|
217
282
|
"prettier-plugin-svelte": "^4.1.1",
|
|
218
283
|
"solid-js": "^1.9.15",
|
package/scripts/README.md
CHANGED
|
@@ -41,10 +41,46 @@ npm exec @st1ggy/linter-config -- migrate --svelte
|
|
|
41
41
|
npx --yes @st1ggy/linter-config init --astro
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
- **
|
|
44
|
+
- **ESLint stacks:** `--common` | `--react` | `--solid` | `--next` | `--svelte` | `--astro`.
|
|
45
|
+
- **Oxlint stacks:** `--common-ox` | `--react-ox` | `--solid-ox` | `--next-ox` | `--svelte-ox` | `--astro-ox`.
|
|
46
|
+
- Select at most one stack; the default is `common`.
|
|
45
47
|
- **`--skip-install`:** only write wrapper files; do not run npm/pnpm/yarn/bun.
|
|
46
48
|
- Every CLI command opens an interactive wizard. `init` creates missing wrappers; `migrate` can remove selected legacy configs and replaces wrappers.
|
|
47
49
|
|
|
48
50
|
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
51
|
|
|
50
52
|
For SolidJS, `init-solid.sh` selects `eslint-solid`, `prettier-common`, and `stylelint-scss`, and installs `eslint-plugin-solid` alongside `@st1ggy/linter-config`.
|
|
53
|
+
|
|
54
|
+
## Oxlint wrappers
|
|
55
|
+
|
|
56
|
+
The six `init-*-ox.sh` shortcuts select the corresponding `*-ox` stack. All write `oxlint.config.ts` and `stylelint.config.js`. Common/React/Solid/Next write `oxfmt.config.ts`; Svelte/Astro write `prettier.config.js`. The wizard pins `oxlint@1.86.0` and `oxlint-tsgolint@7.0.2003`, adds `oxfmt@0.71.0` to the four Oxfmt stacks, and installs only the selected framework integrations. Svelte projects provide their Svelte runtime; their formatter and ESLint-compatible script rules use it.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx @st1ggy/linter-config init --react-ox
|
|
60
|
+
npx @st1ggy/linter-config migrate --astro-ox
|
|
61
|
+
node ./scripts/linter-init.mjs init --solid-ox --dir ./my-app --skip-install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`init` preserves existing wrappers, including the other linter/formatter config. `migrate` offers the opposite configs and old `.oxlintrc.*`/`.oxfmtrc.*` files for removal before generating the selected wrappers. It never deletes them without confirmation.
|
|
65
|
+
|
|
66
|
+
For the four Oxfmt stacks, run `oxlint .` and `oxfmt --check .` separately. Their lint presets do not run Prettier's JS rule. `oxfmt --write .` formats after lint fixes; import and package.json sorting are disabled. The shared formatter options are available through `@st1ggy/linter-config/oxfmt-common`.
|
|
67
|
+
|
|
68
|
+
Oxlint uses native TS7 for typed linting. The consumer tsconfig must be TS7-compatible; the generator does not modify it. Read the [usage guide](../README.md#oxlint-variants) and [coverage report](../docs/OXLINT_RULE_MAPPING.md) before switching a project with Svelte/Astro templates or TS parser-service-dependent JS rules.
|
|
69
|
+
|
|
70
|
+
## Oxlint development checks
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
node scripts/oxlint-inventory.mjs --source-only
|
|
74
|
+
npm run generate:ox
|
|
75
|
+
npm run inventory:ox
|
|
76
|
+
npm run generate:ox:check
|
|
77
|
+
npm run inventory:ox:check
|
|
78
|
+
npm run test:ox
|
|
79
|
+
npm run format:ox:check
|
|
80
|
+
npm run format:ox:compare
|
|
81
|
+
npm run typecheck:ox
|
|
82
|
+
node scripts/package-smoke.mjs
|
|
83
|
+
node scripts/package-smoke.mjs --pm pnpm
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The parity corpus asserts source and target diagnostics; guardrails reject missing mappings, option/severity drift, absent fixtures and unverified claims of complete SFC coverage. Inventory regeneration is deterministic and does not silently adopt new upstream rules: generated artifacts and fixtures must be reviewed together after dependency updates.
|
|
@@ -4,9 +4,13 @@ import { spawnSync } from 'node:child_process'
|
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
5
5
|
import path from 'node:path'
|
|
6
6
|
|
|
7
|
+
import { OXFMT_STACKS, OX_FRAMEWORK_PACKAGES, oxStackToolVersions } from '../src/oxlint/stacks.js'
|
|
8
|
+
|
|
7
9
|
export const PACKAGE = '@st1ggy/linter-config'
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
const BASE_STACK_KEYS = ['common', 'react', 'solid', 'next', 'svelte', 'astro']
|
|
12
|
+
|
|
13
|
+
export const STACK_KEYS = [...BASE_STACK_KEYS, ...BASE_STACK_KEYS.map((key) => `${key}-ox`)]
|
|
10
14
|
|
|
11
15
|
export const STACKS = {
|
|
12
16
|
common: {
|
|
@@ -74,6 +78,30 @@ export const STACK_CHOICES = [
|
|
|
74
78
|
},
|
|
75
79
|
]
|
|
76
80
|
|
|
81
|
+
for (const key of BASE_STACK_KEYS) {
|
|
82
|
+
const base = STACKS[key]
|
|
83
|
+
|
|
84
|
+
base.linterFile = 'eslint.config.js'
|
|
85
|
+
base.linterPreset = base.eslint
|
|
86
|
+
base.formatterFile = 'prettier.config.js'
|
|
87
|
+
base.formatterPreset = base.prettier
|
|
88
|
+
const hasOxfmt = OXFMT_STACKS.includes(key)
|
|
89
|
+
|
|
90
|
+
STACKS[`${key}-ox`] = {
|
|
91
|
+
linterFile: 'oxlint.config.ts',
|
|
92
|
+
linterPreset: `${key}-ox`,
|
|
93
|
+
prettier: base.prettier,
|
|
94
|
+
formatterFile: hasOxfmt ? 'oxfmt.config.ts' : 'prettier.config.js',
|
|
95
|
+
formatterPreset: hasOxfmt ? 'oxfmt-common' : base.prettier,
|
|
96
|
+
stylelint: base.stylelint,
|
|
97
|
+
packages: [...Object.keys(oxStackToolVersions(key)), ...OX_FRAMEWORK_PACKAGES[key]],
|
|
98
|
+
}
|
|
99
|
+
STACK_CHOICES.push({
|
|
100
|
+
value: `${key}-ox`,
|
|
101
|
+
name: `${key}-ox — Oxlint + native TypeScript 7 + ${hasOxfmt ? 'Oxfmt' : 'Prettier'}`,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
77
105
|
export function readPackageJson(directory) {
|
|
78
106
|
const filePath = path.join(directory, 'package.json')
|
|
79
107
|
|
|
@@ -103,7 +131,7 @@ export function isSelfPackageRoot(directory) {
|
|
|
103
131
|
return packageJson?.name === PACKAGE
|
|
104
132
|
}
|
|
105
133
|
|
|
106
|
-
|
|
134
|
+
function packageManifestPath(startDirectory, packageName) {
|
|
107
135
|
let currentDirectory = path.resolve(startDirectory)
|
|
108
136
|
const packageParts = packageName.split('/')
|
|
109
137
|
|
|
@@ -111,7 +139,7 @@ export function hasResolvablePackage(startDirectory, packageName = PACKAGE) {
|
|
|
111
139
|
const marker = path.join(currentDirectory, 'node_modules', ...packageParts, 'package.json')
|
|
112
140
|
|
|
113
141
|
if (existsSync(marker)) {
|
|
114
|
-
return
|
|
142
|
+
return marker
|
|
115
143
|
}
|
|
116
144
|
|
|
117
145
|
const parentDirectory = path.dirname(currentDirectory)
|
|
@@ -123,7 +151,25 @@ export function hasResolvablePackage(startDirectory, packageName = PACKAGE) {
|
|
|
123
151
|
currentDirectory = parentDirectory
|
|
124
152
|
}
|
|
125
153
|
|
|
126
|
-
return
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function hasResolvablePackage(startDirectory, packageName = PACKAGE) {
|
|
158
|
+
return packageManifestPath(startDirectory, packageName) !== null
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function hasRequestedVersion(directory, packageName, spec) {
|
|
162
|
+
const marker = packageManifestPath(directory, packageName)
|
|
163
|
+
|
|
164
|
+
if (!marker) {
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse(readFileSync(marker, 'utf8')).version === spec.split('@').at(-1)
|
|
170
|
+
} catch {
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
127
173
|
}
|
|
128
174
|
|
|
129
175
|
export function detectPackageManager(startDirectory) {
|
|
@@ -170,7 +216,7 @@ export function runPmSync(command, args, cwd) {
|
|
|
170
216
|
|
|
171
217
|
// skipInstall: skip npm install; options.quiet: suppress stderr warnings
|
|
172
218
|
export function ensureDevDependencies(targetDirectory, packages, shouldSkipInstall, options = {}) {
|
|
173
|
-
const { quiet = false } = options
|
|
219
|
+
const { quiet = false, specs = {} } = options
|
|
174
220
|
|
|
175
221
|
if (shouldSkipInstall) {
|
|
176
222
|
return
|
|
@@ -197,7 +243,11 @@ export function ensureDevDependencies(targetDirectory, packages, shouldSkipInsta
|
|
|
197
243
|
}
|
|
198
244
|
|
|
199
245
|
const dependencies = mergedDependencies(packageJson)
|
|
200
|
-
const packagesToAdd = packages.filter(
|
|
246
|
+
const packagesToAdd = packages.filter(
|
|
247
|
+
(packageName) =>
|
|
248
|
+
!Object.hasOwn(dependencies, packageName) ||
|
|
249
|
+
(specs[packageName] && !hasRequestedVersion(directory, packageName, specs[packageName])),
|
|
250
|
+
)
|
|
201
251
|
const hasUnresolvedPackage = packages.some((packageName) => !hasResolvablePackage(directory, packageName))
|
|
202
252
|
|
|
203
253
|
if (!hasUnresolvedPackage && packagesToAdd.length === 0) {
|
|
@@ -223,15 +273,18 @@ export function ensureDevDependencies(targetDirectory, packages, shouldSkipInsta
|
|
|
223
273
|
return
|
|
224
274
|
}
|
|
225
275
|
|
|
276
|
+
const requested = packagesToAdd.map((name) => specs[name] ?? name)
|
|
277
|
+
const shouldPin = requested.some((name, index) => name !== packagesToAdd[index])
|
|
278
|
+
|
|
226
279
|
if (!quiet) {
|
|
227
|
-
process.stdout.write(`install: ${pm} add -D ${
|
|
280
|
+
process.stdout.write(`install: ${pm} add -D ${requested.join(' ')} (${directory})\n`)
|
|
228
281
|
}
|
|
229
282
|
|
|
230
283
|
const add = {
|
|
231
|
-
npm: () => runPmSync('npm', ['install', '-D', ...
|
|
232
|
-
pnpm: () => runPmSync('pnpm', ['add', '-D', ...
|
|
233
|
-
yarn: () => runPmSync('yarn', ['add', '-D', ...
|
|
234
|
-
bun: () => runPmSync('bun', ['add', '-d', ...
|
|
284
|
+
npm: () => runPmSync('npm', ['install', '-D', ...(shouldPin ? ['--save-exact'] : []), ...requested], directory),
|
|
285
|
+
pnpm: () => runPmSync('pnpm', ['add', '-D', ...(shouldPin ? ['--save-exact'] : []), ...requested], directory),
|
|
286
|
+
yarn: () => runPmSync('yarn', ['add', '-D', ...(shouldPin ? ['--exact'] : []), ...requested], directory),
|
|
287
|
+
bun: () => runPmSync('bun', ['add', '-d', ...(shouldPin ? ['--exact'] : []), ...requested], directory),
|
|
235
288
|
}
|
|
236
289
|
|
|
237
290
|
add[pm]()
|
|
@@ -251,6 +304,18 @@ export function stackPackages(stackKey) {
|
|
|
251
304
|
return [PACKAGE, ...stack.packages]
|
|
252
305
|
}
|
|
253
306
|
|
|
307
|
+
export function stackPackageSpecs(stackKey) {
|
|
308
|
+
stackPackages(stackKey)
|
|
309
|
+
|
|
310
|
+
if (!stackKey.endsWith('-ox')) {
|
|
311
|
+
return {}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const versions = oxStackToolVersions(stackKey.slice(0, -3))
|
|
315
|
+
|
|
316
|
+
return Object.fromEntries(Object.entries(versions).map(([name, version]) => [name, `${name}@${version}`]))
|
|
317
|
+
}
|
|
318
|
+
|
|
254
319
|
export function printHelp() {
|
|
255
320
|
process.stdout.write(`\
|
|
256
321
|
${PACKAGE} — generate local wrapper configs in a consumer project.
|
|
@@ -264,13 +329,16 @@ Commands:
|
|
|
264
329
|
create Alias for init.
|
|
265
330
|
|
|
266
331
|
Writes (each command):
|
|
267
|
-
eslint.config.js, prettier.config.js, stylelint.config.js
|
|
332
|
+
ESLint stacks: eslint.config.js, prettier.config.js, stylelint.config.js.
|
|
333
|
+
common/react/solid/next-ox: oxlint.config.ts, oxfmt.config.ts, stylelint.config.js.
|
|
334
|
+
svelte/astro-ox: oxlint.config.ts, prettier.config.js, stylelint.config.js.
|
|
268
335
|
|
|
269
336
|
Unless --skip-install: if package.json exists, runs the detected package manager to add or sync
|
|
270
337
|
${PACKAGE} and the selected stack's integration plugins.
|
|
271
338
|
|
|
272
339
|
Stack (at most one; default: common):
|
|
273
340
|
--common | --react | --solid | --next | --svelte | --astro
|
|
341
|
+
--common-ox | --react-ox | --solid-ox | --next-ox | --svelte-ox | --astro-ox
|
|
274
342
|
|
|
275
343
|
Options:
|
|
276
344
|
--dir, -d Target directory (default: current working directory).
|
|
@@ -282,6 +350,7 @@ Examples (after: npm i -D ${PACKAGE}):
|
|
|
282
350
|
npx ${PACKAGE} init
|
|
283
351
|
npx ${PACKAGE} init --react
|
|
284
352
|
npx ${PACKAGE} init --solid
|
|
353
|
+
npx ${PACKAGE} init --solid-ox
|
|
285
354
|
npx ${PACKAGE} migrate --svelte --dir ./apps/web
|
|
286
355
|
npx ${PACKAGE} init --astro
|
|
287
356
|
npm exec ${PACKAGE} -- init --common
|
|
@@ -294,8 +363,8 @@ Repo (paths from root):
|
|
|
294
363
|
`)
|
|
295
364
|
}
|
|
296
365
|
|
|
297
|
-
export function jsReExport(subpath) {
|
|
298
|
-
return `export { default } from '${PACKAGE}/${subpath}'
|
|
366
|
+
export function jsReExport(subpath, hasSemicolon = true) {
|
|
367
|
+
return `export { default } from '${PACKAGE}/${subpath}'${hasSemicolon ? ';' : ''}\n`
|
|
299
368
|
}
|
|
300
369
|
|
|
301
370
|
export function resolveTargetDirectory(raw) {
|
|
@@ -324,23 +393,28 @@ export function writeFile(targetDirectory, name, content, overwrite, options = {
|
|
|
324
393
|
return true
|
|
325
394
|
}
|
|
326
395
|
|
|
327
|
-
export function wrapperFileNames() {
|
|
328
|
-
|
|
396
|
+
export function wrapperFileNames(stackKey = 'common') {
|
|
397
|
+
stackPackages(stackKey)
|
|
398
|
+
|
|
399
|
+
return [STACKS[stackKey].linterFile, STACKS[stackKey].formatterFile, 'stylelint.config.js']
|
|
329
400
|
}
|
|
330
401
|
|
|
331
|
-
export function existingWrapperFiles(targetDirectory) {
|
|
332
|
-
return wrapperFileNames().filter((name) => existsSync(path.join(targetDirectory, name)))
|
|
402
|
+
export function existingWrapperFiles(targetDirectory, stackKey = 'common') {
|
|
403
|
+
return wrapperFileNames(stackKey).filter((name) => existsSync(path.join(targetDirectory, name)))
|
|
333
404
|
}
|
|
334
405
|
|
|
335
|
-
export function legacyConfigFiles(targetDirectory) {
|
|
406
|
+
export function legacyConfigFiles(targetDirectory, stackKey = 'common') {
|
|
336
407
|
if (!existsSync(targetDirectory)) {
|
|
337
408
|
return []
|
|
338
409
|
}
|
|
339
410
|
|
|
340
|
-
const wrappers = new Set(wrapperFileNames())
|
|
411
|
+
const wrappers = new Set(wrapperFileNames(stackKey))
|
|
341
412
|
|
|
342
413
|
return readdirSync(targetDirectory, { withFileTypes: true })
|
|
343
|
-
.filter(
|
|
414
|
+
.filter(
|
|
415
|
+
(entry) =>
|
|
416
|
+
entry.isFile() && /(?:eslint|oxlint|oxfmt|prettier|stylelint)/i.test(entry.name) && !wrappers.has(entry.name),
|
|
417
|
+
)
|
|
344
418
|
.map((entry) => entry.name)
|
|
345
419
|
.toSorted((left, right) => left.localeCompare(right))
|
|
346
420
|
}
|
|
@@ -383,7 +457,13 @@ export function run(mode, targetDirectory, stackKey, options = {}) {
|
|
|
383
457
|
|
|
384
458
|
const shouldOverwrite = mode === 'migrate' || mode === 'reinit'
|
|
385
459
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
writeFile(targetDirectory,
|
|
460
|
+
const hasSemicolon = stack.formatterPreset !== 'oxfmt-common'
|
|
461
|
+
|
|
462
|
+
writeFile(targetDirectory, stack.linterFile, jsReExport(stack.linterPreset, hasSemicolon), shouldOverwrite, { quiet })
|
|
463
|
+
writeFile(targetDirectory, stack.formatterFile, jsReExport(stack.formatterPreset, hasSemicolon), shouldOverwrite, {
|
|
464
|
+
quiet,
|
|
465
|
+
})
|
|
466
|
+
writeFile(targetDirectory, 'stylelint.config.js', jsReExport(stack.stylelint, hasSemicolon), shouldOverwrite, {
|
|
467
|
+
quiet,
|
|
468
|
+
})
|
|
389
469
|
}
|
package/scripts/linter-init.mjs
CHANGED
|
@@ -20,27 +20,15 @@ import {
|
|
|
20
20
|
resolveStackKey,
|
|
21
21
|
resolveTargetDirectory,
|
|
22
22
|
run,
|
|
23
|
+
stackPackageSpecs,
|
|
23
24
|
stackPackages,
|
|
25
|
+
wrapperFileNames,
|
|
24
26
|
} from './linter-init-core.mjs'
|
|
25
27
|
|
|
26
28
|
function parseArgv(argv) {
|
|
27
29
|
return minimist(argv, {
|
|
28
30
|
string: ['dir', 'd'],
|
|
29
|
-
boolean: [
|
|
30
|
-
'help',
|
|
31
|
-
'h',
|
|
32
|
-
'skip-install',
|
|
33
|
-
'eslint',
|
|
34
|
-
'biome',
|
|
35
|
-
'common',
|
|
36
|
-
'react',
|
|
37
|
-
'solid',
|
|
38
|
-
'next',
|
|
39
|
-
'svelte',
|
|
40
|
-
'astro',
|
|
41
|
-
'interactive',
|
|
42
|
-
'i',
|
|
43
|
-
],
|
|
31
|
+
boolean: ['help', 'h', 'skip-install', 'eslint', 'biome', ...STACK_KEYS, 'interactive', 'i'],
|
|
44
32
|
alias: {
|
|
45
33
|
h: 'help',
|
|
46
34
|
d: 'dir',
|
|
@@ -58,12 +46,7 @@ function argvToValues(argv) {
|
|
|
58
46
|
'skip-install': Boolean(argv['skip-install']),
|
|
59
47
|
eslint: Boolean(argv.eslint),
|
|
60
48
|
biome: Boolean(argv.biome),
|
|
61
|
-
|
|
62
|
-
react: Boolean(argv.react),
|
|
63
|
-
solid: Boolean(argv.solid),
|
|
64
|
-
next: Boolean(argv.next),
|
|
65
|
-
svelte: Boolean(argv.svelte),
|
|
66
|
-
astro: Boolean(argv.astro),
|
|
49
|
+
...Object.fromEntries(STACK_KEYS.map((key) => [key, argv[key] === true])),
|
|
67
50
|
}
|
|
68
51
|
}
|
|
69
52
|
|
|
@@ -77,7 +60,7 @@ async function selectCommand(command) {
|
|
|
77
60
|
choices: [
|
|
78
61
|
{
|
|
79
62
|
value: 'init',
|
|
80
|
-
name: 'init — create missing
|
|
63
|
+
name: 'init — create missing linter, formatter and Stylelint wrappers',
|
|
81
64
|
},
|
|
82
65
|
{
|
|
83
66
|
value: 'migrate',
|
|
@@ -102,14 +85,14 @@ async function selectStack(values) {
|
|
|
102
85
|
})
|
|
103
86
|
}
|
|
104
87
|
|
|
105
|
-
async function selectLegacyFiles(command, directory) {
|
|
88
|
+
async function selectLegacyFiles(command, directory, stack) {
|
|
106
89
|
if (command !== 'migrate') {
|
|
107
90
|
return []
|
|
108
91
|
}
|
|
109
92
|
|
|
110
93
|
const selected = []
|
|
111
94
|
|
|
112
|
-
for (const file of legacyConfigFiles(directory)) {
|
|
95
|
+
for (const file of legacyConfigFiles(directory, stack)) {
|
|
113
96
|
if (await confirm({ message: `Remove legacy config ${file}?`, default: false })) {
|
|
114
97
|
selected.push(file)
|
|
115
98
|
}
|
|
@@ -122,7 +105,7 @@ function printSummary(command, stack, directory, shouldSkipInstall, existing, fi
|
|
|
122
105
|
const wrapperFiles =
|
|
123
106
|
command === 'init' && existing.length > 0
|
|
124
107
|
? `create missing; keep ${existing.join(', ')}`
|
|
125
|
-
:
|
|
108
|
+
: `write ${wrapperFileNames(stack).join(', ')}`
|
|
126
109
|
|
|
127
110
|
process.stdout.write('\n')
|
|
128
111
|
process.stdout.write(` Action: ${command}\n`)
|
|
@@ -148,11 +131,20 @@ async function runInteractive(command, values) {
|
|
|
148
131
|
}
|
|
149
132
|
|
|
150
133
|
const shouldSkipInstall = values['skip-install'] === true
|
|
151
|
-
const existing = existingWrapperFiles(resolved)
|
|
152
|
-
const filesToRemove = await selectLegacyFiles(selectedCommand, resolved)
|
|
134
|
+
const existing = existingWrapperFiles(resolved, selectedStack)
|
|
135
|
+
const filesToRemove = await selectLegacyFiles(selectedCommand, resolved, selectedStack)
|
|
153
136
|
const packages = stackPackages(selectedStack)
|
|
154
|
-
|
|
155
|
-
|
|
137
|
+
const specs = stackPackageSpecs(selectedStack)
|
|
138
|
+
|
|
139
|
+
printSummary(
|
|
140
|
+
selectedCommand,
|
|
141
|
+
selectedStack,
|
|
142
|
+
resolved,
|
|
143
|
+
shouldSkipInstall,
|
|
144
|
+
existing,
|
|
145
|
+
filesToRemove,
|
|
146
|
+
packages.map((name) => specs[name] ?? name),
|
|
147
|
+
)
|
|
156
148
|
|
|
157
149
|
const shouldProceed = await confirm({
|
|
158
150
|
message: 'Proceed?',
|
|
@@ -175,7 +167,7 @@ async function runInteractive(command, values) {
|
|
|
175
167
|
}).start()
|
|
176
168
|
|
|
177
169
|
try {
|
|
178
|
-
ensureDevDependencies(resolved, packages, false, { quiet: true })
|
|
170
|
+
ensureDevDependencies(resolved, packages, false, { quiet: true, specs })
|
|
179
171
|
spinner.succeed(`${selectedStack} linting packages are available`)
|
|
180
172
|
} catch (error) {
|
|
181
173
|
spinner.fail('Dependency step failed')
|
|
@@ -185,7 +177,7 @@ async function runInteractive(command, values) {
|
|
|
185
177
|
|
|
186
178
|
const writeSpinner = ora({
|
|
187
179
|
color: 'green',
|
|
188
|
-
text:
|
|
180
|
+
text: `Writing ${wrapperFileNames(selectedStack).join(', ')}…`,
|
|
189
181
|
}).start()
|
|
190
182
|
|
|
191
183
|
try {
|
|
@@ -213,7 +205,7 @@ function normalizeCommand(command) {
|
|
|
213
205
|
function warnLegacyFlags(values) {
|
|
214
206
|
if (values.biome === true) {
|
|
215
207
|
process.stderr.write(
|
|
216
|
-
`warning: --biome is ignored; Biome presets were removed from ${PACKAGE}.
|
|
208
|
+
`warning: --biome is ignored; Biome presets were removed from ${PACKAGE}. Select an ESLint or Oxlint stack.\n`,
|
|
217
209
|
)
|
|
218
210
|
}
|
|
219
211
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
useTabs: false,
|
|
3
|
+
printWidth: 120,
|
|
4
|
+
tabWidth: 2,
|
|
5
|
+
semi: false,
|
|
6
|
+
singleQuote: true,
|
|
7
|
+
trailingComma: 'all',
|
|
8
|
+
bracketSpacing: true,
|
|
9
|
+
arrowParens: 'always',
|
|
10
|
+
proseWrap: 'never',
|
|
11
|
+
endOfLine: 'lf',
|
|
12
|
+
insertFinalNewline: true,
|
|
13
|
+
sortImports: false,
|
|
14
|
+
sortPackageJson: false,
|
|
15
|
+
ignorePatterns: ['**/node_modules/**', '**/.git/**'],
|
|
16
|
+
}
|