@howells/lint 0.2.4 → 0.2.6
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 +7 -9
- package/bin/env.mjs +3 -0
- package/bin/howells-format.mjs +1 -4
- package/bin/howells-lint-strict.mjs +28 -29
- package/bin/howells-lint.mjs +33 -25
- package/bin/howells-ox-check.mjs +14 -13
- package/bin/howells-ox-fix.mjs +17 -16
- package/bin/howells-workspace-check.mjs +6 -6
- package/bin/run-manypkg-command.mjs +31 -31
- package/bin/run-package-bin.mjs +60 -70
- package/bin/workspace-preflight.mjs +120 -128
- package/biome/core.json +40 -40
- package/biome/next.json +36 -36
- package/biome/react.json +36 -36
- package/oxlint/boundaries.mjs +44 -46
- package/oxlint/core.mjs +78 -39
- package/package.json +39 -39
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ Node or non-React TypeScript package:
|
|
|
69
69
|
|
|
70
70
|
```json
|
|
71
71
|
{
|
|
72
|
-
"$schema": "https://biomejs.dev/schemas/2.4.
|
|
72
|
+
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
73
73
|
"extends": ["@howells/lint/biome/core"],
|
|
74
74
|
"root": true
|
|
75
75
|
}
|
|
@@ -79,7 +79,7 @@ React package:
|
|
|
79
79
|
|
|
80
80
|
```json
|
|
81
81
|
{
|
|
82
|
-
"$schema": "https://biomejs.dev/schemas/2.4.
|
|
82
|
+
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
83
83
|
"extends": ["@howells/lint/biome/core", "@howells/lint/biome/react"],
|
|
84
84
|
"root": true
|
|
85
85
|
}
|
|
@@ -89,12 +89,8 @@ Next.js app:
|
|
|
89
89
|
|
|
90
90
|
```json
|
|
91
91
|
{
|
|
92
|
-
"$schema": "https://biomejs.dev/schemas/2.4.
|
|
93
|
-
"extends": [
|
|
94
|
-
"@howells/lint/biome/core",
|
|
95
|
-
"@howells/lint/biome/react",
|
|
96
|
-
"@howells/lint/biome/next"
|
|
97
|
-
],
|
|
92
|
+
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
93
|
+
"extends": ["@howells/lint/biome/core", "@howells/lint/biome/react", "@howells/lint/biome/next"],
|
|
98
94
|
"root": true
|
|
99
95
|
}
|
|
100
96
|
```
|
|
@@ -103,7 +99,9 @@ Next.js app:
|
|
|
103
99
|
|
|
104
100
|
Use this lane when a project wants Oxlint and Oxfmt instead of Biome. React and Next presets stack the relevant Ultracite Ox rules with [React Doctor](https://react.doctor) rules in one config.
|
|
105
101
|
|
|
106
|
-
React Doctor severities are passed through as published by React Doctor. Native Oxlint Next.js severities come from Oxlint's official `nextjs` plugin via Ultracite's Next preset. `@howells/lint` adds canonical Howells policy on top for file naming, barrel files, env access, and tests.
|
|
102
|
+
React Doctor severities are passed through as published by React Doctor. Native Oxlint Next.js severities come from Oxlint's official `nextjs` plugin via Ultracite's Next preset. `@howells/lint` adds canonical Howells policy on top for file naming, barrel files, env access, file size, function size, complexity, and tests.
|
|
103
|
+
|
|
104
|
+
The core Oxlint preset enables native Oxlint rules that keep code files navigable: `max-lines` errors above 600 non-comment, non-blank lines; `max-lines-per-function` errors above 120 non-comment, non-blank lines; `max-statements` errors above 45 statements per function; and `complexity` errors above cyclomatic complexity 15. Test files keep the file-level `max-lines` guard but disable function-size, statement-count, and complexity limits, because test framework callbacks naturally wrap many independent cases. Generated files should be ignored at the project level; rare intentional exceptions should use an exact-file override with a short refactor note.
|
|
107
105
|
|
|
108
106
|
Choose the closest preset:
|
|
109
107
|
|
package/bin/env.mjs
ADDED
package/bin/howells-format.mjs
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
import { runPackageBin } from "./run-package-bin.mjs";
|
|
4
4
|
|
|
5
5
|
const targets = process.argv.slice(2);
|
|
6
|
-
const args =
|
|
7
|
-
targets.length > 0
|
|
8
|
-
? ["check", "--write", ...targets]
|
|
9
|
-
: ["check", "--write", "."];
|
|
6
|
+
const args = targets.length > 0 ? ["check", "--write", ...targets] : ["check", "--write", "."];
|
|
10
7
|
|
|
11
8
|
runPackageBin("@biomejs/biome", "biome", args);
|
|
@@ -4,39 +4,38 @@ import { runPackageBin } from "./run-package-bin.mjs";
|
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
const passthroughOptions = new Set(["--help", "-h", "--version", "-V"]);
|
|
7
|
-
const targets =
|
|
8
|
-
args.length === 0 || args[0].startsWith("-") ? [".", ...args] : args;
|
|
7
|
+
const targets = args.length === 0 || args[0].startsWith("-") ? [".", ...args] : args;
|
|
9
8
|
|
|
10
9
|
const strictRuleOptions = [
|
|
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
|
-
|
|
10
|
+
"--only=security",
|
|
11
|
+
"--skip=security/noSecrets",
|
|
12
|
+
"--only=correctness/noConstAssign",
|
|
13
|
+
"--only=correctness/noUnreachable",
|
|
14
|
+
"--only=correctness/noInvalidConstructorSuper",
|
|
15
|
+
"--only=correctness/noSetterReturn",
|
|
16
|
+
"--only=correctness/noUnsafeFinally",
|
|
17
|
+
"--only=correctness/noUnsafeOptionalChaining",
|
|
18
|
+
"--only=correctness/noGlobalObjectCalls",
|
|
19
|
+
"--only=correctness/noSelfAssign",
|
|
20
|
+
"--only=correctness/noSwitchDeclarations",
|
|
21
|
+
"--only=suspicious/noDebugger",
|
|
22
|
+
"--only=suspicious/noDoubleEquals",
|
|
23
|
+
"--only=suspicious/noExplicitAny",
|
|
24
|
+
"--only=suspicious/noCatchAssign",
|
|
25
|
+
"--only=suspicious/noFunctionAssign",
|
|
26
|
+
"--only=suspicious/noGlobalAssign",
|
|
27
|
+
"--only=suspicious/noRedeclare",
|
|
28
|
+
"--only=suspicious/noSparseArray",
|
|
29
|
+
"--only=suspicious/noVar",
|
|
30
|
+
"--only=suspicious/noDuplicateCase",
|
|
31
|
+
"--only=suspicious/noDuplicateObjectKeys",
|
|
32
|
+
"--only=suspicious/noDuplicateParameters",
|
|
33
|
+
"--only=suspicious/noFallthroughSwitchClause",
|
|
34
|
+
"--only=suspicious/noFocusedTests",
|
|
36
35
|
];
|
|
37
36
|
|
|
38
37
|
const resolvedArgs = passthroughOptions.has(args[0])
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
? args
|
|
39
|
+
: ["lint", ...strictRuleOptions, ...targets];
|
|
41
40
|
|
|
42
41
|
runPackageBin("@biomejs/biome", "biome", resolvedArgs);
|
package/bin/howells-lint.mjs
CHANGED
|
@@ -4,33 +4,41 @@ import { runPackageBin } from "./run-package-bin.mjs";
|
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
const biomeCommands = new Set([
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
"version",
|
|
8
|
+
"rage",
|
|
9
|
+
"start",
|
|
10
|
+
"stop",
|
|
11
|
+
"check",
|
|
12
|
+
"lint",
|
|
13
|
+
"format",
|
|
14
|
+
"ci",
|
|
15
|
+
"init",
|
|
16
|
+
"migrate",
|
|
17
|
+
"search",
|
|
18
|
+
"explain",
|
|
19
|
+
"clean",
|
|
20
|
+
"daemon",
|
|
21
|
+
"lsp-proxy",
|
|
22
22
|
]);
|
|
23
23
|
const passthroughOptions = new Set(["--help", "-h", "--version", "-V"]);
|
|
24
24
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
const resolveBiomeArgs = (inputArgs) => {
|
|
26
|
+
if (inputArgs.length === 0) {
|
|
27
|
+
return ["check", "."];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const [firstArg] = inputArgs;
|
|
31
|
+
if (biomeCommands.has(firstArg) || passthroughOptions.has(firstArg)) {
|
|
32
|
+
return inputArgs;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (firstArg.startsWith("-")) {
|
|
36
|
+
return ["check", ".", ...inputArgs];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return ["check", ...inputArgs];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const resolvedArgs = resolveBiomeArgs(args);
|
|
35
43
|
|
|
36
44
|
runPackageBin("@biomejs/biome", "biome", resolvedArgs);
|
package/bin/howells-ox-check.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { inheritedEnv } from "./env.mjs";
|
|
4
5
|
import { resolvePackageBin } from "./run-package-bin.mjs";
|
|
5
6
|
|
|
6
7
|
const args = process.argv.slice(2);
|
|
@@ -8,21 +9,21 @@ const targets = args.filter((arg) => !arg.startsWith("-"));
|
|
|
8
9
|
const oxlintOptions = args.filter((arg) => arg.startsWith("-"));
|
|
9
10
|
const resolvedTargets = targets.length > 0 ? targets : ["."];
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const run = (packageName, binName, commandArgs) => {
|
|
13
|
+
const binPath = resolvePackageBin(packageName, binName);
|
|
14
|
+
const result = spawnSync(binPath, commandArgs, {
|
|
15
|
+
env: inheritedEnv(),
|
|
16
|
+
stdio: "inherit",
|
|
17
|
+
});
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (result.error) {
|
|
20
|
+
throw result.error;
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
23
|
+
if ((result.status ?? 1) !== 0) {
|
|
24
|
+
process.exit(result.status ?? 1);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
26
27
|
|
|
27
28
|
run("oxfmt", "oxfmt", ["--check", ...resolvedTargets]);
|
|
28
29
|
run("oxlint", "oxlint", [...oxlintOptions, ...resolvedTargets]);
|
package/bin/howells-ox-fix.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { inheritedEnv } from "./env.mjs";
|
|
4
5
|
import { resolvePackageBin } from "./run-package-bin.mjs";
|
|
5
6
|
|
|
6
7
|
const args = process.argv.slice(2);
|
|
@@ -10,25 +11,25 @@ const targets = filteredArgs.filter((arg) => !arg.startsWith("-"));
|
|
|
10
11
|
const oxlintOptions = filteredArgs.filter((arg) => arg.startsWith("-"));
|
|
11
12
|
const resolvedTargets = targets.length > 0 ? targets : ["."];
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const run = (packageName, binName, commandArgs) => {
|
|
15
|
+
const binPath = resolvePackageBin(packageName, binName);
|
|
16
|
+
const result = spawnSync(binPath, commandArgs, {
|
|
17
|
+
env: inheritedEnv(),
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
});
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
if (result.error) {
|
|
22
|
+
throw result.error;
|
|
23
|
+
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
25
|
+
if ((result.status ?? 1) !== 0) {
|
|
26
|
+
process.exit(result.status ?? 1);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
28
29
|
|
|
29
30
|
run("oxfmt", "oxfmt", ["--write", ...resolvedTargets]);
|
|
30
31
|
run("oxlint", "oxlint", [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
useDangerousFixes ? "--fix-dangerously" : "--fix",
|
|
33
|
+
...oxlintOptions,
|
|
34
|
+
...resolvedTargets,
|
|
34
35
|
]);
|
|
@@ -7,19 +7,19 @@ import { runWorkspacePreflight } from "./workspace-preflight.mjs";
|
|
|
7
7
|
const args = process.argv.slice(2);
|
|
8
8
|
|
|
9
9
|
if (handleManypkgMetadataCommand("check", args)) {
|
|
10
|
-
|
|
10
|
+
process.exit(0);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const errors = runWorkspacePreflight();
|
|
14
14
|
|
|
15
15
|
if (errors.length > 0) {
|
|
16
|
-
|
|
16
|
+
console.error("Workspace hygiene check failed:");
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
for (const error of errors) {
|
|
19
|
+
console.error(`- ${error}`);
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
runPackageBin("@manypkg/cli", "manypkg", ["check", ...args]);
|
|
@@ -7,34 +7,34 @@ const require = createRequire(import.meta.url);
|
|
|
7
7
|
const helpOptions = new Set(["--help", "-h"]);
|
|
8
8
|
const versionOptions = new Set(["--version", "-V"]);
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
10
|
+
export const printManypkgCommandHelp = (command) => {
|
|
11
|
+
console.log(`Usage: howells-workspace-${command} [options]\n`);
|
|
12
|
+
console.log(`Runs: manypkg ${command} [options]`);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const printManypkgCliVersion = () => {
|
|
16
|
+
const { version } = require("@manypkg/cli/package.json");
|
|
17
|
+
console.log(version);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const handleManypkgMetadataCommand = (command, args) => {
|
|
21
|
+
if (helpOptions.has(args[0])) {
|
|
22
|
+
printManypkgCommandHelp(command);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (versionOptions.has(args[0])) {
|
|
27
|
+
printManypkgCliVersion();
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return false;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const runManypkgCommand = (command, args) => {
|
|
35
|
+
if (handleManypkgMetadataCommand(command, args)) {
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
runPackageBin("@manypkg/cli", "manypkg", [command, ...args]);
|
|
40
|
+
};
|
package/bin/run-package-bin.mjs
CHANGED
|
@@ -4,75 +4,65 @@ import { spawnSync } from "node:child_process";
|
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
|
-
import {
|
|
7
|
+
import { inheritedEnv } from "./env.mjs";
|
|
8
8
|
|
|
9
9
|
const require = createRequire(import.meta.url);
|
|
10
|
-
const currentDir =
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
stdio: "inherit",
|
|
70
|
-
env: process.env,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (result.error) {
|
|
74
|
-
throw result.error;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
process.exit(result.status ?? 1);
|
|
78
|
-
}
|
|
10
|
+
const currentDir = import.meta.dirname;
|
|
11
|
+
|
|
12
|
+
const resolvePackageJsonPath = (packageName) => {
|
|
13
|
+
try {
|
|
14
|
+
return require.resolve(`${packageName}/package.json`);
|
|
15
|
+
} catch {
|
|
16
|
+
const packageSegments = packageName.split("/");
|
|
17
|
+
let searchDir = currentDir;
|
|
18
|
+
|
|
19
|
+
while (true) {
|
|
20
|
+
const candidate = join(searchDir, "..", "node_modules", ...packageSegments, "package.json");
|
|
21
|
+
|
|
22
|
+
if (existsSync(candidate)) {
|
|
23
|
+
return candidate;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const parentDir = dirname(searchDir);
|
|
27
|
+
|
|
28
|
+
if (parentDir === searchDir) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
searchDir = parentDir;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
throw new Error(`Could not resolve package.json for package '${packageName}'.`);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const resolvePackageBin = (packageName, binName) => {
|
|
40
|
+
const packageJsonPath = resolvePackageJsonPath(packageName);
|
|
41
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
42
|
+
const packageDir = dirname(packageJsonPath);
|
|
43
|
+
const binField = packageJson.bin;
|
|
44
|
+
|
|
45
|
+
if (typeof binField === "string") {
|
|
46
|
+
return join(packageDir, binField);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (binField && typeof binField === "object" && binField[binName]) {
|
|
50
|
+
return join(packageDir, binField[binName]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(`Could not resolve bin '${binName}' for package '${packageName}'.`);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const runPackageBin = (packageName, binName, args) => {
|
|
57
|
+
const binPath = resolvePackageBin(packageName, binName);
|
|
58
|
+
const result = spawnSync(binPath, args, {
|
|
59
|
+
env: inheritedEnv(),
|
|
60
|
+
stdio: "inherit",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (result.error) {
|
|
64
|
+
throw result.error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
process.exit(result.status ?? 1);
|
|
68
|
+
};
|