@howells/lint 0.1.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/MIGRATIONS.md +73 -0
- package/README.md +103 -0
- package/bin/howells-biome.mjs +5 -0
- package/bin/howells-format.mjs +11 -0
- package/bin/howells-lint.mjs +36 -0
- package/bin/howells-ultracite.mjs +5 -0
- package/bin/run-package-bin.mjs +68 -0
- package/biome/core.json +23 -0
- package/biome/next.json +4 -0
- package/biome/react.json +4 -0
- package/package.json +28 -0
package/MIGRATIONS.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Adoption Notes
|
|
2
|
+
|
|
3
|
+
Use these notes when replacing an existing ESLint, Prettier, or ad hoc Biome setup with `@howells/lint`.
|
|
4
|
+
|
|
5
|
+
## Primary rule
|
|
6
|
+
|
|
7
|
+
Do not migrate an old local lint philosophy into a new local Biome override.
|
|
8
|
+
|
|
9
|
+
Pick the closest shared preset first. Only add local config after you can explain why the repo is a real exception.
|
|
10
|
+
|
|
11
|
+
## Preset selection
|
|
12
|
+
|
|
13
|
+
- Node or non-React TypeScript: `@howells/lint/biome/core`
|
|
14
|
+
- React package or app without Next.js specifics: `@howells/lint/biome/react`
|
|
15
|
+
- Next.js app: `@howells/lint/biome/next`
|
|
16
|
+
|
|
17
|
+
If none of these fit cleanly, the likely answer is a new shared preset here, not a repo-specific fork.
|
|
18
|
+
|
|
19
|
+
## Migration steps
|
|
20
|
+
|
|
21
|
+
1. Add `@howells/lint` as a dev dependency.
|
|
22
|
+
2. Replace `eslint`, `next lint`, `prettier`, or direct `biome` scripts with `howells-lint` and `howells-format`.
|
|
23
|
+
3. Replace the project `biome.json` or `biome.jsonc` with a minimal file that only extends one shared preset.
|
|
24
|
+
4. Remove direct `eslint`, `eslint-config-*`, `eslint-plugin-*`, `prettier`, `@biomejs/biome`, and `ultracite` dependencies once the project is green.
|
|
25
|
+
|
|
26
|
+
## Keep local config thin
|
|
27
|
+
|
|
28
|
+
The normal local config should look like this:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"extends": ["@howells/lint/biome/next"]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Acceptable local additions:
|
|
37
|
+
|
|
38
|
+
- repo-specific file includes or force-ignores that cannot be expressed better in scripts
|
|
39
|
+
- one-off rule changes tied to a genuine platform constraint
|
|
40
|
+
- temporary compatibility shims during migration
|
|
41
|
+
|
|
42
|
+
Avoid:
|
|
43
|
+
|
|
44
|
+
- copying old ESLint rule customizations into Biome
|
|
45
|
+
- broad `linter.rules` sections to preserve team habit
|
|
46
|
+
- local wrapper configs like `base.json` or `library.json`
|
|
47
|
+
- repeating the same override across multiple repos
|
|
48
|
+
|
|
49
|
+
## Promote shared patterns quickly
|
|
50
|
+
|
|
51
|
+
If the same override or ignore pattern appears in more than one active project, treat that as pressure on `@howells/lint`.
|
|
52
|
+
|
|
53
|
+
Either:
|
|
54
|
+
|
|
55
|
+
- move the shared behavior into an existing preset, or
|
|
56
|
+
- add a new preset with a name that describes the actual environment
|
|
57
|
+
|
|
58
|
+
Do not normalize repeated local exceptions.
|
|
59
|
+
|
|
60
|
+
## Prefer scope in scripts
|
|
61
|
+
|
|
62
|
+
If one repo only needs a narrower target, prefer script-level scope:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"scripts": {
|
|
67
|
+
"lint": "howells-lint apps/web packages/ui",
|
|
68
|
+
"lint:fix": "howells-format apps/web packages/ui"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
That is usually better than teaching the config about the repo layout.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# `@howells/lint`
|
|
2
|
+
|
|
3
|
+
Pinned Biome and Ultracite presets for Howells projects.
|
|
4
|
+
|
|
5
|
+
The goal is not to invent a second lint philosophy. The goal is to:
|
|
6
|
+
|
|
7
|
+
- pin a single `@biomejs/biome` version
|
|
8
|
+
- pin a single `ultracite` version
|
|
9
|
+
- give every consumer the same small preset matrix
|
|
10
|
+
- discourage repo-local overrides unless the project has a genuinely unique constraint
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add -D @howells/lint
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Presets
|
|
19
|
+
|
|
20
|
+
Choose the closest preset instead of starting from a generic base and patching it locally:
|
|
21
|
+
|
|
22
|
+
- `@howells/lint/biome/core`
|
|
23
|
+
- `@howells/lint/biome/react`
|
|
24
|
+
- `@howells/lint/biome/next`
|
|
25
|
+
|
|
26
|
+
These presets already:
|
|
27
|
+
|
|
28
|
+
- pin Biome and Ultracite transitively
|
|
29
|
+
- enable VCS ignore file support
|
|
30
|
+
- ignore common build output directories
|
|
31
|
+
- keep `ignoreUnknown` on so mixed repos do not need defensive local config
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
Node or non-React TypeScript package:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"extends": ["@howells/lint/biome/core"]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
React package:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"extends": ["@howells/lint/biome/react"]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Next.js app:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"extends": ["@howells/lint/biome/next"]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Binaries
|
|
60
|
+
|
|
61
|
+
Installers only need `@howells/lint` as a direct dependency. Use the package binaries instead of adding `@biomejs/biome` or `ultracite` separately:
|
|
62
|
+
|
|
63
|
+
- `howells-biome` proxies to the pinned Biome binary
|
|
64
|
+
- `howells-ultracite` proxies to the pinned Ultracite binary
|
|
65
|
+
- `howells-lint` defaults to `biome check .`
|
|
66
|
+
- `howells-format` defaults to `biome check . --write`
|
|
67
|
+
|
|
68
|
+
Example scripts:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"scripts": {
|
|
73
|
+
"lint": "howells-lint",
|
|
74
|
+
"lint:fix": "howells-format"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Prefer explicit script targets over config churn when the only difference is scope:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"scripts": {
|
|
84
|
+
"lint": "howells-lint apps/web packages/ui",
|
|
85
|
+
"lint:fix": "howells-format apps/web packages/ui"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Rules
|
|
91
|
+
|
|
92
|
+
- Do not add local overrides just to preserve old ESLint behavior.
|
|
93
|
+
- Do not create local `base`, `shared`, or `custom` Biome wrappers.
|
|
94
|
+
- If multiple repos need the same exception, add or adjust a preset here.
|
|
95
|
+
- If a repo needs framework-specific linting, choose the matching preset instead of layering rules manually.
|
|
96
|
+
- Prefer inline `biome-ignore` comments for truly isolated exceptions over broad config overrides.
|
|
97
|
+
|
|
98
|
+
## Upstream
|
|
99
|
+
|
|
100
|
+
This package wraps:
|
|
101
|
+
|
|
102
|
+
- [Biome configuration docs](https://biomejs.dev/reference/configuration/)
|
|
103
|
+
- [Ultracite configuration docs](https://www.ultracite.ai/configuration)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { runPackageBin } from "./run-package-bin.mjs";
|
|
4
|
+
|
|
5
|
+
const targets = process.argv.slice(2);
|
|
6
|
+
const args =
|
|
7
|
+
targets.length > 0
|
|
8
|
+
? ["check", "--write", ...targets]
|
|
9
|
+
: ["check", "--write", "."];
|
|
10
|
+
|
|
11
|
+
runPackageBin("@biomejs/biome", "biome", args);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { runPackageBin } from "./run-package-bin.mjs";
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const biomeCommands = new Set([
|
|
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
|
+
]);
|
|
23
|
+
const passthroughOptions = new Set(["--help", "-h", "--version", "-V"]);
|
|
24
|
+
|
|
25
|
+
const resolvedArgs =
|
|
26
|
+
args.length === 0
|
|
27
|
+
? ["check", "."]
|
|
28
|
+
: biomeCommands.has(args[0])
|
|
29
|
+
? args
|
|
30
|
+
: passthroughOptions.has(args[0])
|
|
31
|
+
? args
|
|
32
|
+
: args[0].startsWith("-")
|
|
33
|
+
? ["check", ".", ...args]
|
|
34
|
+
: ["check", ...args];
|
|
35
|
+
|
|
36
|
+
runPackageBin("@biomejs/biome", "biome", resolvedArgs);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
function 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
|
+
function resolvePackageBin(packageName, binName) {
|
|
40
|
+
const packageJsonPath = resolvePackageJsonPath(packageName);
|
|
41
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
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 function runPackageBin(packageName, binName, args) {
|
|
57
|
+
const binPath = resolvePackageBin(packageName, binName);
|
|
58
|
+
const result = spawnSync(binPath, args, {
|
|
59
|
+
stdio: "inherit",
|
|
60
|
+
env: process.env
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (result.error) {
|
|
64
|
+
throw result.error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
process.exit(result.status ?? 1);
|
|
68
|
+
}
|
package/biome/core.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
|
|
3
|
+
"extends": ["ultracite/biome/core"],
|
|
4
|
+
"files": {
|
|
5
|
+
"ignoreUnknown": true,
|
|
6
|
+
"includes": [
|
|
7
|
+
"**",
|
|
8
|
+
"!!**/dist",
|
|
9
|
+
"!!**/build",
|
|
10
|
+
"!!**/.next",
|
|
11
|
+
"!!**/.turbo",
|
|
12
|
+
"!!**/coverage",
|
|
13
|
+
"!!**/storybook-static",
|
|
14
|
+
"!!**/.vercel/output",
|
|
15
|
+
"!!**/out"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"vcs": {
|
|
19
|
+
"enabled": true,
|
|
20
|
+
"clientKind": "git",
|
|
21
|
+
"useIgnoreFile": true
|
|
22
|
+
}
|
|
23
|
+
}
|
package/biome/next.json
ADDED
package/biome/react.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@howells/lint",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pinned Biome and Ultracite presets for Howells projects.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"biome/*.json",
|
|
8
|
+
"bin/*.mjs",
|
|
9
|
+
"README.md",
|
|
10
|
+
"MIGRATIONS.md"
|
|
11
|
+
],
|
|
12
|
+
"bin": {
|
|
13
|
+
"howells-biome": "bin/howells-biome.mjs",
|
|
14
|
+
"howells-lint": "bin/howells-lint.mjs",
|
|
15
|
+
"howells-format": "bin/howells-format.mjs",
|
|
16
|
+
"howells-ultracite": "bin/howells-ultracite.mjs"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@biomejs/biome": "2.4.10",
|
|
20
|
+
"ultracite": "7.4.3"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
"./package.json": "./package.json",
|
|
24
|
+
"./biome/core": "./biome/core.json",
|
|
25
|
+
"./biome/react": "./biome/react.json",
|
|
26
|
+
"./biome/next": "./biome/next.json"
|
|
27
|
+
}
|
|
28
|
+
}
|