@eimerreis/linting 0.1.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 +85 -7
- package/bin/init.mjs +361 -66
- package/oxfmt.config.json +17 -17
- package/oxlint.config.json +36 -36
- package/package.json +57 -55
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Personal linting and formatting defaults for new JavaScript/TypeScript projects.
|
|
4
4
|
|
|
5
|
+
Requires Node `^20.19.0 || >=22.12.0`.
|
|
6
|
+
|
|
7
|
+
Zero peer-dependency setup for consumers: install only `@eimerreis/linting`.
|
|
8
|
+
|
|
5
9
|
Built on:
|
|
6
10
|
|
|
7
11
|
- `oxlint` for linting
|
|
@@ -17,7 +21,13 @@ Default focus:
|
|
|
17
21
|
## Install
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
|
-
npm add -D @eimerreis/linting
|
|
24
|
+
npm add -D @eimerreis/linting
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`lint` also executes react-doctor via:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx react-doctor -y .
|
|
21
31
|
```
|
|
22
32
|
|
|
23
33
|
## Quick Start
|
|
@@ -39,7 +49,7 @@ This creates:
|
|
|
39
49
|
- `.oxlintrc.json`
|
|
40
50
|
- `.oxfmtrc.json`
|
|
41
51
|
|
|
42
|
-
And updates `package.json` scripts (if present):
|
|
52
|
+
And updates `package.json` scripts (if present) so linting is executed through this package:
|
|
43
53
|
|
|
44
54
|
- `lint`
|
|
45
55
|
- `lint:fix`
|
|
@@ -49,11 +59,30 @@ And updates `package.json` scripts (if present):
|
|
|
49
59
|
### CLI options
|
|
50
60
|
|
|
51
61
|
```bash
|
|
52
|
-
eimerreis-linting
|
|
62
|
+
eimerreis-linting init [targetDir] [--force]
|
|
63
|
+
eimerreis-linting lint [targetDir] [--fix]
|
|
64
|
+
eimerreis-linting format [targetDir] [--check]
|
|
53
65
|
```
|
|
54
66
|
|
|
55
|
-
- `
|
|
56
|
-
-
|
|
67
|
+
- `init --force`: overwrite existing `.oxlintrc.json` / `.oxfmtrc.json` and script values
|
|
68
|
+
- `lint --fix`: run `oxlint --fix .` and then run react-doctor
|
|
69
|
+
- `format --check`: run `oxfmt --check .`
|
|
70
|
+
|
|
71
|
+
`react-doctor` runs only when the target package has `react`, `react-dom`, or `next` in dependencies/devDependencies/peerDependencies.
|
|
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.
|
|
57
86
|
|
|
58
87
|
## Manual Setup
|
|
59
88
|
|
|
@@ -63,7 +92,7 @@ If you do not want to use the init script, create the config files manually.
|
|
|
63
92
|
|
|
64
93
|
```json
|
|
65
94
|
{
|
|
66
|
-
|
|
95
|
+
"extends": ["./node_modules/@eimerreis/linting/oxlint.config.json"]
|
|
67
96
|
}
|
|
68
97
|
```
|
|
69
98
|
|
|
@@ -71,6 +100,55 @@ If you do not want to use the init script, create the config files manually.
|
|
|
71
100
|
|
|
72
101
|
```json
|
|
73
102
|
{
|
|
74
|
-
|
|
103
|
+
"extends": ["./node_modules/@eimerreis/linting/oxfmt.config.json"]
|
|
75
104
|
}
|
|
76
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
|
@@ -1,102 +1,397 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
3
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import {
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import { dirname, resolve } from "node:path";
|
|
6
7
|
import process from "node:process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
7
9
|
|
|
8
10
|
const packageName = "@eimerreis/linting";
|
|
9
11
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
function ensureDirectory(filePath) {
|
|
15
|
+
const dir = dirname(filePath);
|
|
16
|
+
if (!existsSync(dir)) {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
function writeJson(filePath, data) {
|
|
22
|
+
ensureDirectory(filePath);
|
|
23
|
+
writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const targetDir = resolve(process.cwd(), targetArg ?? ".");
|
|
26
|
+
function readJson(filePath) {
|
|
27
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
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
|
+
}
|
|
26
40
|
|
|
27
|
-
|
|
28
|
-
const dir = dirname(filePath);
|
|
29
|
-
if (!existsSync(dir)) {
|
|
30
|
-
mkdirSync(dir, { recursive: true });
|
|
31
|
-
}
|
|
41
|
+
return "latest";
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new Error(`Unable to resolve package root for ${dependencyName}`);
|
|
37
64
|
}
|
|
38
65
|
|
|
39
|
-
function
|
|
40
|
-
|
|
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;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
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
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
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");
|
|
113
|
+
}
|
|
114
|
+
|
|
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
|
+
}
|
|
136
|
+
|
|
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
|
+
}
|
|
155
|
+
|
|
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
|
+
};
|
|
41
186
|
}
|
|
42
187
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function hasReactProject(cwd) {
|
|
208
|
+
const packageJsonPath = resolve(cwd, "package.json");
|
|
209
|
+
|
|
210
|
+
if (!existsSync(packageJsonPath)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const packageJson = readJson(packageJsonPath);
|
|
216
|
+
const dependencyFields = ["dependencies", "devDependencies", "peerDependencies"];
|
|
217
|
+
|
|
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
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function upsertScript(packageJson, name, command, force) {
|
|
228
|
+
if (!packageJson.scripts || typeof packageJson.scripts !== "object") {
|
|
229
|
+
packageJson.scripts = {};
|
|
230
|
+
}
|
|
231
|
+
if (!packageJson.scripts[name] || force) {
|
|
232
|
+
packageJson.scripts[name] = command;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function createExtendsConfig(targetPath, exportPath, force) {
|
|
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}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function maybeUpdatePackageJson(targetPackageJsonPath, force) {
|
|
249
|
+
if (!existsSync(targetPackageJsonPath)) {
|
|
250
|
+
console.log("skip package.json (not found)");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const packageJson = readJson(targetPackageJsonPath);
|
|
255
|
+
|
|
256
|
+
if (!packageJson.devDependencies || typeof packageJson.devDependencies !== "object") {
|
|
257
|
+
packageJson.devDependencies = {};
|
|
258
|
+
}
|
|
259
|
+
|
|
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}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function printNextSteps(targetDir) {
|
|
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");
|
|
50
281
|
}
|
|
51
282
|
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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");
|
|
289
|
+
|
|
290
|
+
if (!existsSync(packageRoot)) {
|
|
291
|
+
console.error("init failed: package root not found");
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
createExtendsConfig(oxlintPath, "oxlint.config.json", force);
|
|
296
|
+
createExtendsConfig(oxfmtPath, "oxfmt.config.json", force);
|
|
297
|
+
maybeUpdatePackageJson(targetPackageJsonPath, force);
|
|
57
298
|
|
|
58
|
-
|
|
59
|
-
extends: [`./node_modules/${packageName}/${exportPath}`],
|
|
60
|
-
});
|
|
61
|
-
console.log(`write ${targetPath}`);
|
|
299
|
+
printNextSteps(targetDir);
|
|
62
300
|
}
|
|
63
301
|
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
302
|
+
async function runLint(args) {
|
|
303
|
+
if (!hasInstalledPackage("oxlint")) {
|
|
304
|
+
console.error("oxlint is not installed. Run: npm install");
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const { targetDir, flags } = parsePathAndFlags(args, ["--fix"]);
|
|
309
|
+
const oxlintBinPath = resolvePackageBin("oxlint", "bin/oxlint");
|
|
310
|
+
const lintArgs = [oxlintBinPath];
|
|
311
|
+
const lintConfigPath = resolveLintConfigPath(targetDir);
|
|
312
|
+
|
|
313
|
+
lintArgs.push("-c", lintConfigPath);
|
|
314
|
+
|
|
315
|
+
if (flags.has("--fix")) {
|
|
316
|
+
lintArgs.push("--fix");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
lintArgs.push(".");
|
|
69
320
|
|
|
70
|
-
|
|
321
|
+
const lintExitCode = await runCommand(process.execPath, lintArgs, targetDir);
|
|
322
|
+
if (lintExitCode !== 0) {
|
|
323
|
+
process.exit(lintExitCode);
|
|
324
|
+
}
|
|
71
325
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
326
|
+
if (!hasReactProject(targetDir)) {
|
|
327
|
+
console.log("skip react-doctor (no react/next dependency found)");
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
76
330
|
|
|
77
|
-
|
|
78
|
-
|
|
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
|
+
}
|
|
79
336
|
}
|
|
80
337
|
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
338
|
+
async function runFormat(args) {
|
|
339
|
+
if (!hasInstalledPackage("oxfmt")) {
|
|
340
|
+
console.error("oxfmt is not installed. Run: npm install");
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const { targetDir, flags } = parsePathAndFlags(args, ["--check"]);
|
|
345
|
+
const oxfmtBinPath = resolvePackageBin("oxfmt", "bin/oxfmt");
|
|
346
|
+
const formatArgs = [oxfmtBinPath];
|
|
347
|
+
const formatConfigPath = resolveFormatConfigPath(targetDir);
|
|
348
|
+
|
|
349
|
+
formatArgs.push("-c", formatConfigPath);
|
|
350
|
+
|
|
351
|
+
if (flags.has("--check")) {
|
|
352
|
+
formatArgs.push("--check");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
formatArgs.push(".");
|
|
356
|
+
|
|
357
|
+
const formatExitCode = await runCommand(process.execPath, formatArgs, targetDir);
|
|
358
|
+
if (formatExitCode !== 0) {
|
|
359
|
+
process.exit(formatExitCode);
|
|
360
|
+
}
|
|
88
361
|
}
|
|
89
362
|
|
|
90
|
-
function main() {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
+
}
|
|
95
387
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
}
|
|
100
395
|
}
|
|
101
396
|
|
|
102
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,57 +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
|
-
|
|
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
|
+
}
|
|
57
59
|
}
|