@eimerreis/linting 0.2.0 → 0.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 +70 -5
- package/bin/init.mjs +317 -176
- package/oxfmt.config.json +17 -17
- package/oxlint.config.json +36 -36
- package/package.json +57 -58
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Personal linting and formatting defaults for new JavaScript/TypeScript projects.
|
|
|
4
4
|
|
|
5
5
|
Requires Node `^20.19.0 || >=22.12.0`.
|
|
6
6
|
|
|
7
|
+
Zero peer-dependency setup for consumers: install only `@eimerreis/linting`.
|
|
8
|
+
|
|
7
9
|
Built on:
|
|
8
10
|
|
|
9
11
|
- `oxlint` for linting
|
|
@@ -19,10 +21,10 @@ Default focus:
|
|
|
19
21
|
## Install
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
|
-
npm add -D @eimerreis/linting
|
|
24
|
+
npm add -D @eimerreis/linting
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
`lint` also executes
|
|
27
|
+
`lint` also executes react-doctor via:
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
30
|
npx react-doctor -y .
|
|
@@ -63,11 +65,25 @@ eimerreis-linting format [targetDir] [--check]
|
|
|
63
65
|
```
|
|
64
66
|
|
|
65
67
|
- `init --force`: overwrite existing `.oxlintrc.json` / `.oxfmtrc.json` and script values
|
|
66
|
-
- `lint --fix`: run `oxlint --fix .` and then
|
|
68
|
+
- `lint --fix`: run `oxlint --fix .` and then run react-doctor
|
|
67
69
|
- `format --check`: run `oxfmt --check .`
|
|
68
70
|
|
|
69
71
|
`react-doctor` runs only when the target package has `react`, `react-dom`, or `next` in dependencies/devDependencies/peerDependencies.
|
|
70
72
|
|
|
73
|
+
### First-time one-shot usage
|
|
74
|
+
|
|
75
|
+
You do not need to run `init` first.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx @eimerreis/linting lint
|
|
79
|
+
npx @eimerreis/linting format --check
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Behavior:
|
|
83
|
+
|
|
84
|
+
- If project config exists (`.oxlintrc.*` / `oxlint.config.*`, `.oxfmtrc.*` / `oxfmt.config.*`), it uses that.
|
|
85
|
+
- If not, it falls back to the package's built-in defaults.
|
|
86
|
+
|
|
71
87
|
## Manual Setup
|
|
72
88
|
|
|
73
89
|
If you do not want to use the init script, create the config files manually.
|
|
@@ -76,7 +92,7 @@ If you do not want to use the init script, create the config files manually.
|
|
|
76
92
|
|
|
77
93
|
```json
|
|
78
94
|
{
|
|
79
|
-
|
|
95
|
+
"extends": ["./node_modules/@eimerreis/linting/oxlint.config.json"]
|
|
80
96
|
}
|
|
81
97
|
```
|
|
82
98
|
|
|
@@ -84,6 +100,55 @@ If you do not want to use the init script, create the config files manually.
|
|
|
84
100
|
|
|
85
101
|
```json
|
|
86
102
|
{
|
|
87
|
-
|
|
103
|
+
"extends": ["./node_modules/@eimerreis/linting/oxfmt.config.json"]
|
|
88
104
|
}
|
|
89
105
|
```
|
|
106
|
+
|
|
107
|
+
## Scripts
|
|
108
|
+
|
|
109
|
+
Generated project scripts:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"scripts": {
|
|
114
|
+
"lint": "eimerreis-linting lint",
|
|
115
|
+
"lint:fix": "eimerreis-linting lint --fix",
|
|
116
|
+
"format": "eimerreis-linting format",
|
|
117
|
+
"format:check": "eimerreis-linting format --check"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`init` also adds this dev dependency automatically (if missing):
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"devDependencies": {
|
|
127
|
+
"@eimerreis/linting": "^<current-version>"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Publish
|
|
133
|
+
|
|
134
|
+
### Git-based release workflow (Changesets + GitHub Actions)
|
|
135
|
+
|
|
136
|
+
1. Create a changeset:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm run changeset
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
2. Commit `.changeset/*.md` and push to `main`.
|
|
143
|
+
3. Workflow `Release` checks for pending changesets.
|
|
144
|
+
4. If changesets exist, it versions, publishes via npm trusted publishing (OIDC), and commits `chore: version packages`.
|
|
145
|
+
|
|
146
|
+
Trusted publishing setup (one-time on npmjs.com):
|
|
147
|
+
|
|
148
|
+
1. Package `@eimerreis/linting` -> Settings -> Trusted Publisher
|
|
149
|
+
2. Provider: GitHub Actions
|
|
150
|
+
3. Organization/user: `eimerreis`
|
|
151
|
+
4. Repository: `eimerreis-linting`
|
|
152
|
+
5. Workflow filename: `release.yml`
|
|
153
|
+
|
|
154
|
+
No `NPM_TOKEN` secret is required for publishing.
|
package/bin/init.mjs
CHANGED
|
@@ -2,255 +2,396 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
6
|
import { dirname, resolve } from "node:path";
|
|
7
7
|
import process from "node:process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
8
9
|
|
|
9
10
|
const packageName = "@eimerreis/linting";
|
|
10
11
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
11
13
|
|
|
12
14
|
function ensureDirectory(filePath) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const dir = dirname(filePath);
|
|
16
|
+
if (!existsSync(dir)) {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
function writeJson(filePath, data) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
ensureDirectory(filePath);
|
|
23
|
+
writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
function readJson(filePath) {
|
|
25
|
-
|
|
27
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
function getSelfDependencyRange() {
|
|
31
|
+
try {
|
|
32
|
+
const selfPackageJsonPath = resolve(packageRoot, "package.json");
|
|
33
|
+
const selfPackageJson = readJson(selfPackageJsonPath);
|
|
34
|
+
if (selfPackageJson.version) {
|
|
35
|
+
return `^${selfPackageJson.version}`;
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// ignore
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return "latest";
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
function resolvePackageBin(dependencyName, binRelativePath) {
|
|
45
|
+
const entryPointPath = requireFromHere.resolve(dependencyName);
|
|
46
|
+
let cursor = dirname(entryPointPath);
|
|
47
|
+
|
|
48
|
+
while (cursor !== dirname(cursor)) {
|
|
49
|
+
const packageJsonPath = resolve(cursor, "package.json");
|
|
50
|
+
if (existsSync(packageJsonPath)) {
|
|
51
|
+
const packageJson = readJson(packageJsonPath);
|
|
52
|
+
if (packageJson.name === dependencyName) {
|
|
53
|
+
const binPath = resolve(cursor, binRelativePath);
|
|
54
|
+
if (!existsSync(binPath)) {
|
|
55
|
+
throw new Error(`Cannot find ${dependencyName} binary at ${binPath}`);
|
|
56
|
+
}
|
|
57
|
+
return binPath;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
cursor = dirname(cursor);
|
|
61
|
+
}
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
command: firstArg,
|
|
46
|
-
args: rawArgs.slice(1),
|
|
47
|
-
};
|
|
63
|
+
throw new Error(`Unable to resolve package root for ${dependencyName}`);
|
|
48
64
|
}
|
|
49
65
|
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
continue;
|
|
66
|
+
function resolvePackageEntry(dependencyName) {
|
|
67
|
+
return requireFromHere.resolve(dependencyName);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hasInstalledPackage(dependencyName) {
|
|
71
|
+
try {
|
|
72
|
+
resolvePackageEntry(dependencyName);
|
|
73
|
+
return true;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
61
76
|
}
|
|
77
|
+
}
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
function resolveExistingConfigPath(targetDir, configCandidates) {
|
|
80
|
+
for (const candidate of configCandidates) {
|
|
81
|
+
const candidatePath = resolve(targetDir, candidate);
|
|
82
|
+
if (existsSync(candidatePath)) {
|
|
83
|
+
return candidatePath;
|
|
84
|
+
}
|
|
65
85
|
}
|
|
66
86
|
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
69
89
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
function resolveLintConfigPath(targetDir) {
|
|
91
|
+
const localConfigPath = resolveExistingConfigPath(targetDir, [
|
|
92
|
+
".oxlintrc.json",
|
|
93
|
+
".oxlintrc.jsonc",
|
|
94
|
+
".oxlintrc.js",
|
|
95
|
+
".oxlintrc.mjs",
|
|
96
|
+
".oxlintrc.cjs",
|
|
97
|
+
".oxlintrc.ts",
|
|
98
|
+
".oxlintrc.mts",
|
|
99
|
+
".oxlintrc.cts",
|
|
100
|
+
"oxlint.config.js",
|
|
101
|
+
"oxlint.config.mjs",
|
|
102
|
+
"oxlint.config.cjs",
|
|
103
|
+
"oxlint.config.ts",
|
|
104
|
+
"oxlint.config.mts",
|
|
105
|
+
"oxlint.config.cts",
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
if (localConfigPath) {
|
|
109
|
+
return localConfigPath;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return resolve(packageRoot, "oxlint.config.json");
|
|
74
113
|
}
|
|
75
114
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
115
|
+
function resolveFormatConfigPath(targetDir) {
|
|
116
|
+
const localConfigPath = resolveExistingConfigPath(targetDir, [
|
|
117
|
+
".oxfmtrc.json",
|
|
118
|
+
".oxfmtrc.jsonc",
|
|
119
|
+
".oxfmtrc.js",
|
|
120
|
+
".oxfmtrc.mjs",
|
|
121
|
+
".oxfmtrc.cjs",
|
|
122
|
+
".oxfmtrc.ts",
|
|
123
|
+
".oxfmtrc.mts",
|
|
124
|
+
".oxfmtrc.cts",
|
|
125
|
+
"oxfmt.config.js",
|
|
126
|
+
"oxfmt.config.mjs",
|
|
127
|
+
"oxfmt.config.cjs",
|
|
128
|
+
"oxfmt.config.ts",
|
|
129
|
+
"oxfmt.config.mts",
|
|
130
|
+
"oxfmt.config.cts",
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
if (localConfigPath) {
|
|
134
|
+
return localConfigPath;
|
|
135
|
+
}
|
|
83
136
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
137
|
+
return resolve(packageRoot, "oxfmt.config.json");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function printUsage() {
|
|
141
|
+
console.log("Usage:");
|
|
142
|
+
console.log(" eimerreis-linting init [targetDir] [--force]");
|
|
143
|
+
console.log(" eimerreis-linting lint [targetDir] [--fix]");
|
|
144
|
+
console.log(" eimerreis-linting format [targetDir] [--check]");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function parseCommand(rawArgs) {
|
|
148
|
+
const firstArg = rawArgs[0];
|
|
149
|
+
if (!firstArg || firstArg === "init" || firstArg.startsWith("-")) {
|
|
150
|
+
return {
|
|
151
|
+
command: "init",
|
|
152
|
+
args: firstArg === "init" ? rawArgs.slice(1) : rawArgs,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
88
155
|
|
|
89
|
-
|
|
90
|
-
|
|
156
|
+
return {
|
|
157
|
+
command: firstArg,
|
|
158
|
+
args: rawArgs.slice(1),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parsePathAndFlags(args, allowedFlags) {
|
|
163
|
+
let targetDirArg;
|
|
164
|
+
const flags = new Set();
|
|
165
|
+
|
|
166
|
+
for (const arg of args) {
|
|
167
|
+
if (arg.startsWith("-")) {
|
|
168
|
+
if (!allowedFlags.includes(arg)) {
|
|
169
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
170
|
+
}
|
|
171
|
+
flags.add(arg);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (targetDirArg) {
|
|
176
|
+
throw new Error(`Unexpected argument: ${arg}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
targetDirArg = arg;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
targetDir: resolve(process.cwd(), targetDirArg ?? "."),
|
|
184
|
+
flags,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function runCommand(command, commandArgs, cwd) {
|
|
189
|
+
return new Promise((resolvePromise) => {
|
|
190
|
+
const child = spawn(command, commandArgs, {
|
|
191
|
+
cwd,
|
|
192
|
+
stdio: "inherit",
|
|
193
|
+
shell: process.platform === "win32",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
child.on("error", (error) => {
|
|
197
|
+
console.error(`failed to run ${command}: ${error.message}`);
|
|
198
|
+
resolvePromise(1);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
child.on("close", (code) => {
|
|
202
|
+
resolvePromise(code ?? 1);
|
|
203
|
+
});
|
|
91
204
|
});
|
|
92
|
-
});
|
|
93
205
|
}
|
|
94
206
|
|
|
95
207
|
function hasReactProject(cwd) {
|
|
96
|
-
|
|
208
|
+
const packageJsonPath = resolve(cwd, "package.json");
|
|
97
209
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
210
|
+
if (!existsSync(packageJsonPath)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
101
213
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
214
|
+
try {
|
|
215
|
+
const packageJson = readJson(packageJsonPath);
|
|
216
|
+
const dependencyFields = ["dependencies", "devDependencies", "peerDependencies"];
|
|
105
217
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
218
|
+
return dependencyFields.some((field) => {
|
|
219
|
+
const deps = packageJson[field];
|
|
220
|
+
return Boolean(deps?.react || deps?.["react-dom"] || deps?.next);
|
|
221
|
+
});
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
113
225
|
}
|
|
114
226
|
|
|
115
227
|
function upsertScript(packageJson, name, command, force) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
228
|
+
if (!packageJson.scripts || typeof packageJson.scripts !== "object") {
|
|
229
|
+
packageJson.scripts = {};
|
|
230
|
+
}
|
|
231
|
+
if (!packageJson.scripts[name] || force) {
|
|
232
|
+
packageJson.scripts[name] = command;
|
|
233
|
+
}
|
|
122
234
|
}
|
|
123
235
|
|
|
124
236
|
function createExtendsConfig(targetPath, exportPath, force) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
237
|
+
if (existsSync(targetPath) && !force) {
|
|
238
|
+
console.log(`skip ${targetPath} (already exists)`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
writeJson(targetPath, {
|
|
243
|
+
extends: [`./node_modules/${packageName}/${exportPath}`],
|
|
244
|
+
});
|
|
245
|
+
console.log(`write ${targetPath}`);
|
|
134
246
|
}
|
|
135
247
|
|
|
136
248
|
function maybeUpdatePackageJson(targetPackageJsonPath, force) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
249
|
+
if (!existsSync(targetPackageJsonPath)) {
|
|
250
|
+
console.log("skip package.json (not found)");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
141
253
|
|
|
142
|
-
|
|
254
|
+
const packageJson = readJson(targetPackageJsonPath);
|
|
143
255
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
upsertScript(packageJson, "format:check", "eimerreis-linting format --check", force);
|
|
256
|
+
if (!packageJson.devDependencies || typeof packageJson.devDependencies !== "object") {
|
|
257
|
+
packageJson.devDependencies = {};
|
|
258
|
+
}
|
|
148
259
|
|
|
149
|
-
|
|
150
|
-
|
|
260
|
+
const selfDependencyRange = getSelfDependencyRange();
|
|
261
|
+
if (!packageJson.devDependencies[packageName] || force) {
|
|
262
|
+
packageJson.devDependencies[packageName] = selfDependencyRange;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
upsertScript(packageJson, "lint", "eimerreis-linting lint", force);
|
|
266
|
+
upsertScript(packageJson, "lint:fix", "eimerreis-linting lint --fix", force);
|
|
267
|
+
upsertScript(packageJson, "format", "eimerreis-linting format", force);
|
|
268
|
+
upsertScript(packageJson, "format:check", "eimerreis-linting format --check", force);
|
|
269
|
+
|
|
270
|
+
writeJson(targetPackageJsonPath, packageJson);
|
|
271
|
+
console.log(`update ${targetPackageJsonPath}`);
|
|
151
272
|
}
|
|
152
273
|
|
|
153
274
|
function printNextSteps(targetDir) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
275
|
+
const relativePath = targetDir === process.cwd() ? "." : targetDir;
|
|
276
|
+
console.log("done");
|
|
277
|
+
console.log("next steps:");
|
|
278
|
+
console.log(`1) cd ${relativePath}`);
|
|
279
|
+
console.log("2) npm install");
|
|
280
|
+
console.log("3) npm run lint && npm run format:check");
|
|
160
281
|
}
|
|
161
282
|
|
|
162
|
-
function runInit(args) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (!existsSync(packageRoot)) {
|
|
170
|
-
console.error("init failed: package root not found");
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
createExtendsConfig(oxlintPath, "oxlint.config.json", force);
|
|
175
|
-
createExtendsConfig(oxfmtPath, "oxfmt.config.json", force);
|
|
176
|
-
maybeUpdatePackageJson(targetPackageJsonPath, force);
|
|
177
|
-
printNextSteps(targetDir);
|
|
178
|
-
}
|
|
283
|
+
async function runInit(args) {
|
|
284
|
+
const { targetDir, flags } = parsePathAndFlags(args, ["--force"]);
|
|
285
|
+
const force = flags.has("--force");
|
|
286
|
+
const targetPackageJsonPath = resolve(targetDir, "package.json");
|
|
287
|
+
const oxlintPath = resolve(targetDir, ".oxlintrc.json");
|
|
288
|
+
const oxfmtPath = resolve(targetDir, ".oxfmtrc.json");
|
|
179
289
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
290
|
+
if (!existsSync(packageRoot)) {
|
|
291
|
+
console.error("init failed: package root not found");
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
183
294
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
295
|
+
createExtendsConfig(oxlintPath, "oxlint.config.json", force);
|
|
296
|
+
createExtendsConfig(oxfmtPath, "oxfmt.config.json", force);
|
|
297
|
+
maybeUpdatePackageJson(targetPackageJsonPath, force);
|
|
187
298
|
|
|
188
|
-
|
|
299
|
+
printNextSteps(targetDir);
|
|
300
|
+
}
|
|
189
301
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
302
|
+
async function runLint(args) {
|
|
303
|
+
if (!hasInstalledPackage("oxlint")) {
|
|
304
|
+
console.error("oxlint is not installed. Run: npm install");
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
194
307
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
308
|
+
const { targetDir, flags } = parsePathAndFlags(args, ["--fix"]);
|
|
309
|
+
const oxlintBinPath = resolvePackageBin("oxlint", "bin/oxlint");
|
|
310
|
+
const lintArgs = [oxlintBinPath];
|
|
311
|
+
const lintConfigPath = resolveLintConfigPath(targetDir);
|
|
199
312
|
|
|
200
|
-
|
|
201
|
-
if (doctorExitCode !== 0) {
|
|
202
|
-
process.exit(doctorExitCode);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
313
|
+
lintArgs.push("-c", lintConfigPath);
|
|
205
314
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
315
|
+
if (flags.has("--fix")) {
|
|
316
|
+
lintArgs.push("--fix");
|
|
317
|
+
}
|
|
209
318
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
319
|
+
lintArgs.push(".");
|
|
320
|
+
|
|
321
|
+
const lintExitCode = await runCommand(process.execPath, lintArgs, targetDir);
|
|
322
|
+
if (lintExitCode !== 0) {
|
|
323
|
+
process.exit(lintExitCode);
|
|
324
|
+
}
|
|
213
325
|
|
|
214
|
-
|
|
326
|
+
if (!hasReactProject(targetDir)) {
|
|
327
|
+
console.log("skip react-doctor (no react/next dependency found)");
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
215
330
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
331
|
+
const reactDoctorEntryPath = resolvePackageEntry("react-doctor");
|
|
332
|
+
const doctorExitCode = await runCommand(process.execPath, [reactDoctorEntryPath, "-y", "."], targetDir);
|
|
333
|
+
if (doctorExitCode !== 0) {
|
|
334
|
+
process.exit(doctorExitCode);
|
|
335
|
+
}
|
|
220
336
|
}
|
|
221
337
|
|
|
222
|
-
async function
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
printUsage();
|
|
227
|
-
process.exit(0);
|
|
338
|
+
async function runFormat(args) {
|
|
339
|
+
if (!hasInstalledPackage("oxfmt")) {
|
|
340
|
+
console.error("oxfmt is not installed. Run: npm install");
|
|
341
|
+
process.exit(1);
|
|
228
342
|
}
|
|
229
343
|
|
|
230
|
-
const {
|
|
344
|
+
const { targetDir, flags } = parsePathAndFlags(args, ["--check"]);
|
|
345
|
+
const oxfmtBinPath = resolvePackageBin("oxfmt", "bin/oxfmt");
|
|
346
|
+
const formatArgs = [oxfmtBinPath];
|
|
347
|
+
const formatConfigPath = resolveFormatConfigPath(targetDir);
|
|
231
348
|
|
|
232
|
-
|
|
233
|
-
runInit(args);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
349
|
+
formatArgs.push("-c", formatConfigPath);
|
|
236
350
|
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
return;
|
|
351
|
+
if (flags.has("--check")) {
|
|
352
|
+
formatArgs.push("--check");
|
|
240
353
|
}
|
|
241
354
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
355
|
+
formatArgs.push(".");
|
|
356
|
+
|
|
357
|
+
const formatExitCode = await runCommand(process.execPath, formatArgs, targetDir);
|
|
358
|
+
if (formatExitCode !== 0) {
|
|
359
|
+
process.exit(formatExitCode);
|
|
245
360
|
}
|
|
361
|
+
}
|
|
246
362
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
363
|
+
async function main() {
|
|
364
|
+
try {
|
|
365
|
+
const rawArgs = process.argv.slice(2);
|
|
366
|
+
if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
|
|
367
|
+
printUsage();
|
|
368
|
+
process.exit(0);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const { command, args } = parseCommand(rawArgs);
|
|
372
|
+
|
|
373
|
+
if (command === "init") {
|
|
374
|
+
await runInit(args);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (command === "lint") {
|
|
379
|
+
await runLint(args);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (command === "format") {
|
|
384
|
+
await runFormat(args);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.error(`Unknown command: ${command}`);
|
|
389
|
+
printUsage();
|
|
390
|
+
process.exit(1);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
254
395
|
}
|
|
255
396
|
|
|
256
397
|
main();
|
package/oxfmt.config.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
3
|
+
"printWidth": 125,
|
|
4
|
+
"tabWidth": 4,
|
|
5
|
+
"semi": true,
|
|
6
|
+
"singleQuote": false,
|
|
7
|
+
"trailingComma": "es5",
|
|
8
|
+
"sortImports": {
|
|
9
|
+
"order": "asc",
|
|
10
|
+
"ignoreCase": true,
|
|
11
|
+
"newlinesBetween": true,
|
|
12
|
+
"groups": ["builtin", "external", "internal", ["parent", "sibling", "index"]],
|
|
13
|
+
"internalPattern": ["@/", "~/"]
|
|
14
|
+
},
|
|
15
|
+
"sortTailwindcss": {
|
|
16
|
+
"functions": ["cn", "clsx", "cva", "tw"]
|
|
17
|
+
},
|
|
18
|
+
"ignorePatterns": ["node_modules/**", ".next/**", "dist/**", "build/**", "coverage/**", "out/**"]
|
|
19
19
|
}
|
package/oxlint.config.json
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["eslint", "typescript", "unicorn", "oxc", "react", "nextjs", "import", "vitest"],
|
|
4
|
+
"categories": {
|
|
5
|
+
"correctness": "error",
|
|
6
|
+
"suspicious": "warn"
|
|
7
|
+
},
|
|
8
|
+
"env": {
|
|
9
|
+
"browser": true,
|
|
10
|
+
"node": true,
|
|
11
|
+
"es6": true
|
|
12
|
+
},
|
|
13
|
+
"rules": {
|
|
14
|
+
"eslint/no-unused-vars": [
|
|
15
|
+
"warn",
|
|
16
|
+
{
|
|
17
|
+
"varsIgnorePattern": "_"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"typescript/no-explicit-any": "off",
|
|
21
|
+
"react/prop-types": "off",
|
|
22
|
+
"react/react-in-jsx-scope": "off",
|
|
23
|
+
"react-hooks/refs": "off",
|
|
24
|
+
"react-hooks/incompatible-library": "off",
|
|
25
|
+
"import/no-duplicates": "error",
|
|
26
|
+
"import/no-self-import": "error",
|
|
27
|
+
"import/no-cycle": "warn"
|
|
28
|
+
},
|
|
29
|
+
"ignorePatterns": ["node_modules/**", ".next/**", "dist/**", "build/**", "coverage/**", "out/**"],
|
|
30
|
+
"overrides": [
|
|
31
|
+
{
|
|
32
|
+
"files": ["**/*.{test,spec}.{js,jsx,ts,tsx}", "**/__tests__/**/*.{js,jsx,ts,tsx}"],
|
|
33
|
+
"rules": {
|
|
34
|
+
"typescript/no-explicit-any": "off"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
38
|
}
|
package/package.json
CHANGED
|
@@ -1,60 +1,59 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
2
|
+
"name": "@eimerreis/linting",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Personal OXC linting and formatting defaults",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"format",
|
|
7
|
+
"lint",
|
|
8
|
+
"nextjs",
|
|
9
|
+
"oxc",
|
|
10
|
+
"oxfmt",
|
|
11
|
+
"oxlint",
|
|
12
|
+
"react",
|
|
13
|
+
"tailwind",
|
|
14
|
+
"typescript"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"url": "https://github.com/eimerreis/linting"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"eimerreis-linting": "bin/init.mjs"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"oxlint.config.json",
|
|
26
|
+
"oxfmt.config.json",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"exports": {
|
|
32
|
+
"./oxlint": "./oxlint.config.json",
|
|
33
|
+
"./oxfmt": "./oxfmt.config.json",
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"changeset": "changeset",
|
|
41
|
+
"lint": "node ./bin/init.mjs lint",
|
|
42
|
+
"lint:fix": "node ./bin/init.mjs lint --fix",
|
|
43
|
+
"format": "node ./bin/init.mjs format",
|
|
44
|
+
"format:check": "node ./bin/init.mjs format --check",
|
|
45
|
+
"version-packages": "changeset version",
|
|
46
|
+
"release": "changeset publish"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"oxfmt": "^0.41.0",
|
|
50
|
+
"oxlint": "^1.56.0",
|
|
51
|
+
"react-doctor": "^0.0.30"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@changesets/cli": "^2.29.7"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
58
|
+
}
|
|
60
59
|
}
|