@akanjs/devkit 3.0.0-alpha.84 → 3.0.0-alpha.85
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/biome.base.json +6 -4
- package/biome.domains.json +9 -0
- package/biomeBase.ts +10 -3
- package/biomeStrictConfig.test.ts +54 -0
- package/biomeStrictConfig.ts +58 -0
- package/package.json +3 -2
package/biome.base.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.5.
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.12/schema.json",
|
|
3
3
|
"vcs": {
|
|
4
4
|
"enabled": true,
|
|
5
5
|
"clientKind": "git",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
},
|
|
25
25
|
"noArrayIndexKey": "off",
|
|
26
26
|
"noShadowRestrictedNames": "off",
|
|
27
|
-
"noUnnecessaryConditions":
|
|
27
|
+
"noUnnecessaryConditions": {
|
|
28
|
+
"level": "warn"
|
|
29
|
+
}
|
|
28
30
|
},
|
|
29
31
|
"correctness": {
|
|
30
32
|
"noUnusedFunctionParameters": "off",
|
|
@@ -58,10 +60,10 @@
|
|
|
58
60
|
}
|
|
59
61
|
},
|
|
60
62
|
"domains": {
|
|
63
|
+
"project": "none",
|
|
61
64
|
"react": "recommended",
|
|
62
65
|
"test": "recommended",
|
|
63
|
-
"types": "none"
|
|
64
|
-
"project": "none"
|
|
66
|
+
"types": "none"
|
|
65
67
|
}
|
|
66
68
|
},
|
|
67
69
|
"javascript": {
|
package/biomeBase.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/** `extends` target for a workspace `biome.json`; Biome resolves it through node_modules. */
|
|
2
2
|
export const biomeBaseConfig = "@akanjs/devkit/biome.base.json";
|
|
3
3
|
|
|
4
|
+
// The `types` and `project` domains are the two that make Biome build a type and a module graph, so `biome.base.json`
|
|
5
|
+
// leaves them `none` and the editor never pays for them on save. They live here instead, applied on top of the base
|
|
6
|
+
// by the batch runners through `BiomeStrictConfig`. Omitting a domain is not the same as `none` — Biome infers an
|
|
7
|
+
// unspecified domain from the project's dependencies and turns it on — so both files spell every domain out.
|
|
8
|
+
export const biomeDomainsConfig = "@akanjs/devkit/biome.domains.json";
|
|
9
|
+
|
|
4
10
|
// Biome moves rules between groups across minors — `noUnnecessaryConditions` sat in `nursery` at 2.4 and moved to
|
|
5
11
|
// `suspicious` at 2.5 — and the stale position is a hard "unknown key" error, not a warning. A workspace whose
|
|
6
12
|
// Biome disagrees with the shipped base config therefore fails to load it at all, which is why the version is
|
|
7
|
-
// pinned here instead of resolved to latest at create time. Bump this
|
|
8
|
-
// `biome migrate --write` in the workspace root and in `pkgs/@akanjs/devkit` so
|
|
9
|
-
|
|
13
|
+
// pinned here instead of resolved to latest at create time. Bump this, `biome.base.json` and `biome.domains.json`
|
|
14
|
+
// in one commit, and run `biome migrate --write` in the workspace root and in `pkgs/@akanjs/devkit` so every config
|
|
15
|
+
// moves together.
|
|
16
|
+
export const biomeVersion = "2.5.12";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { BiomeStrictConfig } from "./biomeStrictConfig";
|
|
6
|
+
|
|
7
|
+
const workspace = async () => await mkdtemp(path.join(tmpdir(), "akan-biome-strict-"));
|
|
8
|
+
|
|
9
|
+
describe("BiomeStrictConfig", () => {
|
|
10
|
+
test("writes nothing when the workspace has no biome config", async () => {
|
|
11
|
+
const root = await workspace();
|
|
12
|
+
const strictConfig = new BiomeStrictConfig(root);
|
|
13
|
+
|
|
14
|
+
expect(await strictConfig.write()).toBeNull();
|
|
15
|
+
expect(await Bun.file(strictConfig.filePath).exists()).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("layers the domains over the workspace config, base first", async () => {
|
|
19
|
+
const root = await workspace();
|
|
20
|
+
await Bun.write(path.join(root, "biome.json"), "{}\n");
|
|
21
|
+
const strictConfig = new BiomeStrictConfig(root, { id: "test" });
|
|
22
|
+
|
|
23
|
+
const filePath = await strictConfig.write();
|
|
24
|
+
|
|
25
|
+
expect(filePath).toBe(path.join(root, ".biome.strict.test.json"));
|
|
26
|
+
expect(await Bun.file(path.join(root, ".biome.strict.test.json")).json()).toEqual({
|
|
27
|
+
extends: ["@akanjs/devkit/biome.base.json", "./biome.json", "@akanjs/devkit/biome.domains.json"],
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("prefers biome.json over biome.jsonc, mirroring Biome's own precedence", async () => {
|
|
32
|
+
const root = await workspace();
|
|
33
|
+
await Bun.write(path.join(root, "biome.json"), "{}\n");
|
|
34
|
+
await Bun.write(path.join(root, "biome.jsonc"), "{}\n");
|
|
35
|
+
const strictConfig = new BiomeStrictConfig(root, { id: "test" });
|
|
36
|
+
|
|
37
|
+
await strictConfig.write();
|
|
38
|
+
|
|
39
|
+
const written = (await Bun.file(strictConfig.filePath).json()) as { extends: string[] };
|
|
40
|
+
expect(written.extends[1]).toBe("./biome.json");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("removes the copy, and stays quiet when there is nothing to remove", async () => {
|
|
44
|
+
const root = await workspace();
|
|
45
|
+
await Bun.write(path.join(root, "biome.jsonc"), "{}\n");
|
|
46
|
+
const strictConfig = new BiomeStrictConfig(root, { id: "test" });
|
|
47
|
+
await strictConfig.write();
|
|
48
|
+
|
|
49
|
+
await strictConfig.remove();
|
|
50
|
+
await strictConfig.remove();
|
|
51
|
+
|
|
52
|
+
expect(await Bun.file(strictConfig.filePath).exists()).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { biomeBaseConfig, biomeDomainsConfig } from "./biomeBase";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The editor and the batch runs want different `linter.domains`, and Biome has no per-invocation switch for them:
|
|
7
|
+
* `--only` / `--skip` pick which rules run, not whether the type and module graphs are built, and `--only=<domain>`
|
|
8
|
+
* additionally force-enables every rule of that domain, including the ones the shared config deliberately leaves
|
|
9
|
+
* off. So the domains are a configuration difference, and this class is the batch half of it — a throwaway config
|
|
10
|
+
* that re-applies `biome.domains.json` over the workspace's own, used by `akan lint` and the pre-commit hook.
|
|
11
|
+
*
|
|
12
|
+
* Two details fix its shape. It has to sit in the workspace root, because Biome resolves `plugins` paths from the
|
|
13
|
+
* entry configuration's own directory — a config under `.husky/` loads none of the grit rules and reports only
|
|
14
|
+
* "Cannot read file". And it has to name `biome.base.json` itself rather than lean on the workspace config's own
|
|
15
|
+
* `extends`, because `extends` is one level deep: a config extending `./biome.json` inherits `biome.json`'s own
|
|
16
|
+
* keys and nothing that `biome.json` in turn extends. Listing the base first keeps `overrides` in the order a
|
|
17
|
+
* plain run has them, workspace entries after the framework's.
|
|
18
|
+
*/
|
|
19
|
+
export class BiomeStrictConfig {
|
|
20
|
+
static readonly configFileNames = ["biome.json", "biome.jsonc"] as const;
|
|
21
|
+
|
|
22
|
+
/** `biome.json` first, mirroring Biome's own precedence; `biome.jsonc` is the one that may carry comments. */
|
|
23
|
+
static async resolveConfigName(workspaceRoot: string): Promise<string | null> {
|
|
24
|
+
for (const fileName of BiomeStrictConfig.configFileNames) {
|
|
25
|
+
if (await Bun.file(path.join(workspaceRoot, fileName)).exists()) return fileName;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
readonly #filePath: string;
|
|
31
|
+
readonly #workspaceRoot: string;
|
|
32
|
+
|
|
33
|
+
constructor(workspaceRoot: string, { id = process.pid }: { id?: number | string } = {}) {
|
|
34
|
+
this.#workspaceRoot = workspaceRoot;
|
|
35
|
+
this.#filePath = path.join(workspaceRoot, `.biome.strict.${id}.json`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get filePath() {
|
|
39
|
+
return this.#filePath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async write(): Promise<string | null> {
|
|
43
|
+
const configName = await BiomeStrictConfig.resolveConfigName(this.#workspaceRoot);
|
|
44
|
+
if (!configName) return null;
|
|
45
|
+
const config = { extends: [biomeBaseConfig, `./${configName}`, biomeDomainsConfig] };
|
|
46
|
+
await Bun.write(this.#filePath, `${JSON.stringify(config, null, 2)}\n`);
|
|
47
|
+
return this.#filePath;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async remove() {
|
|
51
|
+
await rm(this.#filePath, { force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (import.meta.main) {
|
|
56
|
+
const filePath = await new BiomeStrictConfig(process.cwd()).write();
|
|
57
|
+
if (filePath) process.stdout.write(path.relative(process.cwd(), filePath));
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/devkit",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.85",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"./package.json": "./package.json",
|
|
26
26
|
"./biome.base.json": "./biome.base.json",
|
|
27
|
+
"./biome.domains.json": "./biome.domains.json",
|
|
27
28
|
"./akanApp": "./akanApp/index.ts",
|
|
28
29
|
"./akanConfig": "./akanConfig/index.ts",
|
|
29
30
|
"./artifact": "./artifact/index.ts",
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
"@langchain/openai": "^1.4.6",
|
|
46
47
|
"@tailwindcss/node": "^4.3.0",
|
|
47
48
|
"@trapezedev/project": "^7.1.4",
|
|
48
|
-
"akanjs": "3.0.0-alpha.
|
|
49
|
+
"akanjs": "3.0.0-alpha.85",
|
|
49
50
|
"chalk": "^5.6.2",
|
|
50
51
|
"commander": "^14.0.3",
|
|
51
52
|
"dayjs": "^1.11.20",
|