@antelopejs/tooling-configs 0.0.9 → 0.2.1
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 +83 -18
- package/dist/bin/check-interface-ranges.mjs +10 -1
- package/dist/bin/check-size.d.mts +1 -0
- package/dist/bin/check-size.mjs +51 -0
- package/dist/bin/checks.d.mts +1 -0
- package/dist/bin/checks.mjs +51 -0
- package/dist/bin/ratchet.d.mts +1 -0
- package/dist/bin/ratchet.mjs +66 -0
- package/dist/bin/sync.d.mts +1 -0
- package/dist/bin/sync.mjs +485 -0
- package/dist/ceilings--pJmhSde.mjs +64 -0
- package/dist/eslint.mjs +24 -4
- package/dist/files-Bocv-qA8.mjs +103 -0
- package/dist/knip.mjs +5 -0
- package/dist/oxc/fmt.mjs +1 -1
- package/dist/oxc/guardrails/index.d.mts +6 -0
- package/dist/oxc/guardrails/index.mjs +318 -0
- package/dist/oxc/lint.d.mts +128 -151
- package/dist/oxc/lint.mjs +100 -125
- package/dist/package-B1aipTbn.mjs +115 -0
- package/dist/ratchet-B2-4XxAl.mjs +172 -0
- package/dist/shared-BG0Nkzh0.d.mts +8 -0
- package/dist/{shared-Ch-ZcMQb.mjs → shared-NC0T8ykm.mjs} +5 -1
- package/dist/shared-WC0z3y9F.mjs +63 -0
- package/dist/size-DJ7YSry_.mjs +17 -0
- package/package.json +22 -6
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
//#region src/guardrails/package.ts
|
|
5
|
+
function parseManifest(text) {
|
|
6
|
+
const manifest = JSON.parse(text);
|
|
7
|
+
if (manifest === null || Array.isArray(manifest)) throw new Error("package.json must contain an object.");
|
|
8
|
+
return manifest;
|
|
9
|
+
}
|
|
10
|
+
function packageManager(manifest) {
|
|
11
|
+
const manager = (manifest.packageManager ?? "pnpm").split("@")[0];
|
|
12
|
+
switch (manager) {
|
|
13
|
+
case "pnpm":
|
|
14
|
+
case "npm":
|
|
15
|
+
case "yarn":
|
|
16
|
+
case "bun": return manager;
|
|
17
|
+
default: throw new Error(`Unsupported package manager ${manager}. Expected pnpm, npm, yarn, or bun.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function execCommand(manager) {
|
|
21
|
+
if (manager === "npm") return "npm exec --";
|
|
22
|
+
if (manager === "bun") return "bun x --no-install";
|
|
23
|
+
return `${manager} exec`;
|
|
24
|
+
}
|
|
25
|
+
function toolingVersions() {
|
|
26
|
+
let directory = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
while (true) {
|
|
28
|
+
const path = join(directory, "package.json");
|
|
29
|
+
if (existsSync(path)) {
|
|
30
|
+
const manifest = parseManifest(readFileSync(path, "utf8"));
|
|
31
|
+
if (manifest.name === "@antelopejs/tooling-configs") {
|
|
32
|
+
const lefthook = manifest.devDependencies?.lefthook;
|
|
33
|
+
const knip = manifest.devDependencies?.knip;
|
|
34
|
+
const tsgolint = manifest.devDependencies?.["oxlint-tsgolint"];
|
|
35
|
+
if (!lefthook || !knip || !tsgolint) throw new Error("tooling-configs must declare lefthook, knip and oxlint-tsgolint devDependencies.");
|
|
36
|
+
return {
|
|
37
|
+
lefthook,
|
|
38
|
+
knip,
|
|
39
|
+
tsgolint
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const parent = dirname(directory);
|
|
44
|
+
if (parent === directory) throw new Error("Cannot find tooling-configs package.json.");
|
|
45
|
+
directory = parent;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function repositoryTools(manifest) {
|
|
49
|
+
return {
|
|
50
|
+
installed: {
|
|
51
|
+
commands: {
|
|
52
|
+
checks: "antelopejs-checks",
|
|
53
|
+
size: "antelopejs-check-size",
|
|
54
|
+
ratchet: "antelopejs-ratchet",
|
|
55
|
+
sync: "antelopejs-sync"
|
|
56
|
+
},
|
|
57
|
+
presets: {
|
|
58
|
+
"oxlint.config.ts": "@antelopejs/tooling-configs/oxc/lint",
|
|
59
|
+
"oxfmt.config.ts": "@antelopejs/tooling-configs/oxc/fmt",
|
|
60
|
+
"knip.config.ts": "@antelopejs/tooling-configs/knip"
|
|
61
|
+
},
|
|
62
|
+
hookPrefix: `${execCommand(packageManager(manifest))} `
|
|
63
|
+
},
|
|
64
|
+
source: {
|
|
65
|
+
commands: {
|
|
66
|
+
checks: "node src/bin/checks.ts",
|
|
67
|
+
size: "node src/bin/check-size.ts",
|
|
68
|
+
ratchet: "node src/bin/ratchet.ts",
|
|
69
|
+
sync: "node src/bin/sync.ts"
|
|
70
|
+
},
|
|
71
|
+
presets: {
|
|
72
|
+
"oxlint.config.ts": "./src/oxc/lint.ts",
|
|
73
|
+
"oxfmt.config.ts": "./src/oxc/fmt.ts",
|
|
74
|
+
"knip.config.ts": "./src/knip.ts"
|
|
75
|
+
},
|
|
76
|
+
hookPrefix: ""
|
|
77
|
+
}
|
|
78
|
+
}[manifest.name === "@antelopejs/tooling-configs" ? "source" : "installed"];
|
|
79
|
+
}
|
|
80
|
+
const PREPARE_INSTALL = "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi";
|
|
81
|
+
const DEFAULT_SCRIPTS = {
|
|
82
|
+
typecheck: "tsc --noEmit",
|
|
83
|
+
lint: "oxlint",
|
|
84
|
+
format: "oxfmt .",
|
|
85
|
+
"format:check": "oxfmt --check .",
|
|
86
|
+
knip: "knip"
|
|
87
|
+
};
|
|
88
|
+
function prepareScript(current = "") {
|
|
89
|
+
if (current.includes("if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi")) return current;
|
|
90
|
+
const legacy = /(^|&&\s*|;\s*)(?:(?:pnpm exec|npm exec --|yarn exec|bun x --no-install)\s+)?lefthook\s+install(?=\s*(?:$|&&|;))/;
|
|
91
|
+
if (legacy.test(current)) return current.replace(legacy, `$1${PREPARE_INSTALL}`);
|
|
92
|
+
return current ? `${current} && ${PREPARE_INSTALL}` : PREPARE_INSTALL;
|
|
93
|
+
}
|
|
94
|
+
function managedPackage(current, versions, options = {}) {
|
|
95
|
+
if (current === void 0) throw new Error("package.json is missing.");
|
|
96
|
+
const manifest = parseManifest(current);
|
|
97
|
+
const scripts = manifest.scripts ??= {};
|
|
98
|
+
scripts.prepare = prepareScript(scripts.prepare);
|
|
99
|
+
const tools = repositoryTools(manifest);
|
|
100
|
+
Object.assign(scripts, tools.commands, {
|
|
101
|
+
"ratchet:update": `${tools.commands.ratchet} --update`,
|
|
102
|
+
"sync:check": `${tools.commands.sync} --check`
|
|
103
|
+
});
|
|
104
|
+
for (const [name, command] of Object.entries(DEFAULT_SCRIPTS)) scripts[name] ??= name === "typecheck" && options.nuxt ? "nuxi typecheck" : command;
|
|
105
|
+
const dependencies = manifest.devDependencies ??= {};
|
|
106
|
+
dependencies.lefthook ??= versions.lefthook;
|
|
107
|
+
dependencies.knip ??= versions.knip;
|
|
108
|
+
if (options.typeAware) dependencies["oxlint-tsgolint"] ??= versions.tsgolint;
|
|
109
|
+
manifest.devDependencies = Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
110
|
+
const indent = /^([\t ]+)"/m.exec(current)?.[1] ?? " ";
|
|
111
|
+
const newline = current.includes("\r\n") ? "\r\n" : "\n";
|
|
112
|
+
return `${JSON.stringify(manifest, null, indent).replaceAll("\n", newline)}${newline}`;
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { repositoryTools as a, parseManifest as i, managedPackage as n, toolingVersions as o, packageManager as r, execCommand as t };
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { a as formatCeilings, n as MAX_FILE_BYTES, o as readCeilings, r as MEASURE_ENV, t as CEILINGS_FILE } from "./ceilings--pJmhSde.mjs";
|
|
2
|
+
import { i as repositoryPath, n as fileBytes, r as gitFiles, t as authoredSizes } from "./files-Bocv-qA8.mjs";
|
|
3
|
+
import { n as sizeFailure } from "./size-DJ7YSry_.mjs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { basename, dirname, join } from "node:path";
|
|
7
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
8
|
+
//#region src/guardrails/oxlint.ts
|
|
9
|
+
function resolveOxlint(root) {
|
|
10
|
+
try {
|
|
11
|
+
const require = createRequire(join(root, "package.json"));
|
|
12
|
+
return join(dirname(require.resolve("oxlint/package.json")), "bin/oxlint");
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error("oxlint is not installed in this repository. Install dependencies first.");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function ruleId(code) {
|
|
18
|
+
const match = /^([^()]+)\(([^()]+)\)$/.exec(code);
|
|
19
|
+
if (!match) throw new Error(`Cannot measure oxlint diagnostic code ${JSON.stringify(code)}. Fix parser or configuration errors before recording a baseline.`);
|
|
20
|
+
const [, plugin, rule] = match;
|
|
21
|
+
return `${plugin === "typescript-eslint" ? "typescript" : plugin}/${rule}`;
|
|
22
|
+
}
|
|
23
|
+
function parseDiagnostics(text, root) {
|
|
24
|
+
const report = JSON.parse(text);
|
|
25
|
+
if (!Array.isArray(report?.diagnostics)) throw new Error("oxlint did not return a JSON diagnostics array.");
|
|
26
|
+
return report.diagnostics.map((item) => {
|
|
27
|
+
const file = repositoryPath(root, item.filename);
|
|
28
|
+
const span = item.labels?.[0]?.span;
|
|
29
|
+
return {
|
|
30
|
+
rule: ruleId(item.code),
|
|
31
|
+
file,
|
|
32
|
+
location: span ? `${file}:${span.line}:${span.column}` : file,
|
|
33
|
+
message: item.message,
|
|
34
|
+
line: span?.line,
|
|
35
|
+
help: item.help
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function lintDiagnostics(root, config, files) {
|
|
40
|
+
const binary = resolveOxlint(root);
|
|
41
|
+
const temporary = config === void 0 ? void 0 : mkdtempSync(join(root, ".antelopejs-measure.generated."));
|
|
42
|
+
try {
|
|
43
|
+
const args = [
|
|
44
|
+
binary,
|
|
45
|
+
"-f",
|
|
46
|
+
"json"
|
|
47
|
+
];
|
|
48
|
+
if (temporary) {
|
|
49
|
+
const path = join(temporary, "oxlint.config.ts");
|
|
50
|
+
writeFileSync(path, config ?? "");
|
|
51
|
+
args.push("--config", path, "--ignore-pattern", `${basename(temporary)}/**`);
|
|
52
|
+
}
|
|
53
|
+
if (files) args.push("--", ...files);
|
|
54
|
+
const result = spawnSync(process.execPath, args, {
|
|
55
|
+
cwd: root,
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
env: {
|
|
58
|
+
...process.env,
|
|
59
|
+
[MEASURE_ENV]: "1"
|
|
60
|
+
},
|
|
61
|
+
maxBuffer: 67108864
|
|
62
|
+
});
|
|
63
|
+
if (result.error) throw result.error;
|
|
64
|
+
if (result.status !== 0 && result.status !== 1) throw new Error(`oxlint failed (${result.status ?? result.signal}): ${result.stderr || result.stdout}`);
|
|
65
|
+
const diagnostics = parseDiagnostics(result.stdout, root);
|
|
66
|
+
if (result.status !== 0 && diagnostics.length === 0) throw new Error(`oxlint failed without diagnostics: ${result.stderr || result.stdout}`);
|
|
67
|
+
return diagnostics;
|
|
68
|
+
} finally {
|
|
69
|
+
if (temporary) rmSync(temporary, {
|
|
70
|
+
recursive: true,
|
|
71
|
+
force: true
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/guardrails/ratchet.ts
|
|
77
|
+
function baseline(root, diagnostics) {
|
|
78
|
+
const rules = {};
|
|
79
|
+
const oversizeFiles = {};
|
|
80
|
+
for (const diagnostic of diagnostics) if (diagnostic.rule === "eslint/max-lines") {
|
|
81
|
+
const size = fileBytes(root, diagnostic.file);
|
|
82
|
+
if (size !== void 0) oversizeFiles[diagnostic.file] = size;
|
|
83
|
+
} else rules[diagnostic.rule] = (rules[diagnostic.rule] ?? 0) + 1;
|
|
84
|
+
for (const [file, size] of authoredSizes(root)) if (size > 2e4) oversizeFiles[file] = size;
|
|
85
|
+
return {
|
|
86
|
+
rules,
|
|
87
|
+
oversizeFiles
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function measureBaseline(root, config) {
|
|
91
|
+
return baseline(root, lintDiagnostics(root, config));
|
|
92
|
+
}
|
|
93
|
+
function changedFiles(root) {
|
|
94
|
+
if (spawnSync("git", [
|
|
95
|
+
"rev-parse",
|
|
96
|
+
"--verify",
|
|
97
|
+
"HEAD"
|
|
98
|
+
], {
|
|
99
|
+
cwd: root,
|
|
100
|
+
stdio: "ignore"
|
|
101
|
+
}).status !== 0) return new Set(gitFiles(root));
|
|
102
|
+
const tracked = execFileSync("git", [
|
|
103
|
+
"diff",
|
|
104
|
+
"HEAD",
|
|
105
|
+
"--name-only",
|
|
106
|
+
"-z"
|
|
107
|
+
], {
|
|
108
|
+
cwd: root,
|
|
109
|
+
encoding: "utf8"
|
|
110
|
+
});
|
|
111
|
+
const untracked = execFileSync("git", [
|
|
112
|
+
"ls-files",
|
|
113
|
+
"--others",
|
|
114
|
+
"--exclude-standard",
|
|
115
|
+
"-z"
|
|
116
|
+
], {
|
|
117
|
+
cwd: root,
|
|
118
|
+
encoding: "utf8"
|
|
119
|
+
});
|
|
120
|
+
return new Set(`${tracked}${untracked}`.split("\0").filter(Boolean));
|
|
121
|
+
}
|
|
122
|
+
function lowerCeilings(ceilings, current) {
|
|
123
|
+
const lowered = [];
|
|
124
|
+
for (const [rule, limit] of Object.entries(ceilings.rules)) {
|
|
125
|
+
const count = current.rules[rule] ?? 0;
|
|
126
|
+
if (count === 0) delete ceilings.rules[rule];
|
|
127
|
+
else if (count < limit) ceilings.rules[rule] = count;
|
|
128
|
+
if (count < limit) lowered.push(`${rule}: ${limit} -> ${count}`);
|
|
129
|
+
}
|
|
130
|
+
for (const [file, limit] of Object.entries(ceilings.oversizeFiles)) if (current.oversizeFiles[file] === void 0) {
|
|
131
|
+
delete ceilings.oversizeFiles[file];
|
|
132
|
+
lowered.push(`${file}: removed oversize allowance`);
|
|
133
|
+
} else if (current.oversizeFiles[file] < limit) {
|
|
134
|
+
ceilings.oversizeFiles[file] = current.oversizeFiles[file];
|
|
135
|
+
lowered.push(`${file}: ${limit} -> ${current.oversizeFiles[file]} bytes`);
|
|
136
|
+
}
|
|
137
|
+
return lowered;
|
|
138
|
+
}
|
|
139
|
+
function ratchet(root, update = false) {
|
|
140
|
+
if (!existsSync(join(root, "guardrails.ratchet.json"))) throw new Error(`${CEILINGS_FILE} is missing. Run antelopejs-sync first.`);
|
|
141
|
+
const ceilings = readCeilings(root);
|
|
142
|
+
const diagnostics = lintDiagnostics(root);
|
|
143
|
+
const current = baseline(root, diagnostics);
|
|
144
|
+
const changed = changedFiles(root);
|
|
145
|
+
const failures = [];
|
|
146
|
+
for (const [rule, count] of Object.entries(current.rules)) {
|
|
147
|
+
const limit = ceilings.rules[rule] ?? 0;
|
|
148
|
+
if (count <= limit) continue;
|
|
149
|
+
failures.push(`${rule}: ${count} diagnostics exceeds ceiling ${limit}.`);
|
|
150
|
+
const matches = diagnostics.filter((diagnostic) => diagnostic.rule === rule);
|
|
151
|
+
const inChanged = matches.filter(({ file }) => changed.has(file));
|
|
152
|
+
const shown = inChanged.length > 0 ? inChanged : matches;
|
|
153
|
+
for (const diagnostic of shown) failures.push(` ${diagnostic.location}: ${diagnostic.message}`);
|
|
154
|
+
if (shown.length < matches.length) failures.push(` ...and ${matches.length - shown.length} recorded in files this change does not touch.`);
|
|
155
|
+
}
|
|
156
|
+
for (const [file, size] of Object.entries(current.oversizeFiles)) if (ceilings.oversizeFiles[file] === void 0) failures.push(size > 2e4 ? sizeFailure(file, size, MAX_FILE_BYTES) : `${file}: ${size} bytes and an eslint/max-lines diagnostic; this file has no oversize allowance. Split by responsibility into sibling modules, or split tests by behaviour under test, one file per describe.`);
|
|
157
|
+
for (const [file, limit] of Object.entries(ceilings.oversizeFiles)) {
|
|
158
|
+
const size = fileBytes(root, file);
|
|
159
|
+
if (size !== void 0 && size > limit) failures.push(sizeFailure(file, size, limit));
|
|
160
|
+
}
|
|
161
|
+
const lowered = update ? lowerCeilings(ceilings, current) : [];
|
|
162
|
+
if (update) {
|
|
163
|
+
const path = join(root, CEILINGS_FILE);
|
|
164
|
+
writeFileSync(path, formatCeilings(ceilings));
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
failures,
|
|
168
|
+
lowered
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
export { ratchet as n, lintDiagnostics as r, measureBaseline as t };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "@oxlint/plugins";
|
|
2
|
+
//#region src/oxc/guardrails/shared.d.ts
|
|
3
|
+
/** Test locations shared by the preset's assertion and test-quality rules. */
|
|
4
|
+
declare const TEST_FILE_GLOBS: string[];
|
|
5
|
+
/** Assertion entry points recognized by the assertion and function-only rules. */
|
|
6
|
+
declare const ASSERTION_FUNCTION_NAMES: string[];
|
|
7
|
+
//#endregion
|
|
8
|
+
export { TEST_FILE_GLOBS as n, ASSERTION_FUNCTION_NAMES as t };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
//#region src/oxc/shared.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Paths every AntelopeJS project generates or vendors, and that no linter should read.
|
|
4
|
+
* `.github/ISSUE_TEMPLATE/**` holds GitHub's legacy issue templates: Markdown with
|
|
5
|
+
* YAML frontmatter saved under a .yml extension, which a YAML formatter cannot parse.
|
|
6
|
+
*/
|
|
3
7
|
const IGNORE_PATTERNS = [
|
|
4
8
|
"**/node_modules/**",
|
|
5
9
|
"**/dist/**",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/oxc/guardrails/shared.ts
|
|
2
|
+
/** Test locations shared by the preset's assertion and test-quality rules. */
|
|
3
|
+
const TEST_FILE_GLOBS = [
|
|
4
|
+
"**/*.test.*",
|
|
5
|
+
"**/*.spec.*",
|
|
6
|
+
"**/test/**",
|
|
7
|
+
"**/tests/**",
|
|
8
|
+
"**/__tests__/**"
|
|
9
|
+
];
|
|
10
|
+
/** Assertion entry points recognized by the assertion and function-only rules. */
|
|
11
|
+
const ASSERTION_FUNCTION_NAMES = [
|
|
12
|
+
"expect",
|
|
13
|
+
"assert",
|
|
14
|
+
"assert.*",
|
|
15
|
+
"assert*",
|
|
16
|
+
"expect*"
|
|
17
|
+
];
|
|
18
|
+
/** Static property names include quoted computed access, but never dynamic keys. */
|
|
19
|
+
function memberName(node) {
|
|
20
|
+
if (!node.computed) return node.property.type === "Identifier" ? node.property.name : null;
|
|
21
|
+
return node.property.type === "Literal" && !("regex" in node.property) ? String(node.property.value) : null;
|
|
22
|
+
}
|
|
23
|
+
/** A statically named callee, such as expect, assert.equal, or test.concurrent.only. */
|
|
24
|
+
function callName(node) {
|
|
25
|
+
if (node.type === "Identifier") return node.name;
|
|
26
|
+
if (node.type !== "MemberExpression") return null;
|
|
27
|
+
const owner = callName(node.object);
|
|
28
|
+
const name = memberName(node);
|
|
29
|
+
return owner !== null && name !== null ? `${owner}.${name}` : null;
|
|
30
|
+
}
|
|
31
|
+
/** The expect call and matcher path in expect(value).to.deep.equal(expected). */
|
|
32
|
+
function matcherChain(node) {
|
|
33
|
+
const chain = [];
|
|
34
|
+
let current = node.callee;
|
|
35
|
+
while (current.type === "MemberExpression") {
|
|
36
|
+
const name = memberName(current);
|
|
37
|
+
if (name === null) return null;
|
|
38
|
+
chain.unshift(name);
|
|
39
|
+
current = current.object;
|
|
40
|
+
}
|
|
41
|
+
if (current.type !== "CallExpression" || callName(current.callee) !== "expect") return null;
|
|
42
|
+
return {
|
|
43
|
+
expect: current,
|
|
44
|
+
matcher: chain.join(".")
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function isAssertionName(name) {
|
|
48
|
+
return ASSERTION_FUNCTION_NAMES.some((pattern) => pattern.endsWith("*") ? name.startsWith(pattern.slice(0, -1)) : name === pattern);
|
|
49
|
+
}
|
|
50
|
+
/** Assertion syntax shared by both test-quality rules, including fluent assertion libraries. */
|
|
51
|
+
function isAssertion(node) {
|
|
52
|
+
if (node.type === "MemberExpression") return memberName(node) === "should";
|
|
53
|
+
const name = callName(node.callee);
|
|
54
|
+
return name !== null && (isAssertionName(name) || name.startsWith("sinon.assert.") || name === "should") || node.callee.type === "MemberExpression" && memberName(node.callee) === "expect";
|
|
55
|
+
}
|
|
56
|
+
/** Executable inline callbacks for it/test, excluding skipped and todo registrations. */
|
|
57
|
+
function testCallback(node) {
|
|
58
|
+
const name = callName(node.callee);
|
|
59
|
+
if (name === null || !/^(?:it|test)(?:\.(?:only|concurrent|serial|fails|skip|todo))*$/u.test(name) || /\.(?:skip|todo)(?:\.|$)/u.test(name)) return void 0;
|
|
60
|
+
for (const argument of node.arguments) if (argument.type === "ArrowFunctionExpression" || argument.type === "FunctionExpression") return argument;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { matcherChain as a, isAssertion as i, TEST_FILE_GLOBS as n, memberName as o, callName as r, testCallback as s, ASSERTION_FUNCTION_NAMES as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { o as readCeilings } from "./ceilings--pJmhSde.mjs";
|
|
2
|
+
import { t as authoredSizes } from "./files-Bocv-qA8.mjs";
|
|
3
|
+
//#region src/guardrails/size.ts
|
|
4
|
+
function sizeFailure(file, size, limit) {
|
|
5
|
+
return `${file}: ${size} bytes, ${size - limit} bytes over the ${limit}-byte ceiling. Split by responsibility into sibling modules, one cohesive group of exports each; keep the original path re-exporting only if it is a public entry point. Split a test file by behaviour under test, one file per describe. Do not minify, strip whitespace, or move code under an excluded path to pass.`;
|
|
6
|
+
}
|
|
7
|
+
function checkSize(root, files, staged = false) {
|
|
8
|
+
const ceilings = readCeilings(root);
|
|
9
|
+
const failures = [];
|
|
10
|
+
for (const [file, size] of authoredSizes(root, files, staged)) {
|
|
11
|
+
const limit = ceilings.oversizeFiles[file] ?? 2e4;
|
|
12
|
+
if (size > limit) failures.push(sizeFailure(file, size, limit));
|
|
13
|
+
}
|
|
14
|
+
return failures;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { sizeFailure as n, checkSize as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/tooling-configs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Shared tooling presets for AntelopeJS repositories: oxlint (with the vendored anti-slop rules), oxfmt, ESLint for Nuxt layers, Knip, and an interface range check",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
"url": "git+https://github.com/AntelopeJS/tooling-configs.git"
|
|
22
22
|
},
|
|
23
23
|
"bin": {
|
|
24
|
-
"antelopejs-check-interface-ranges": "./dist/bin/check-interface-ranges.mjs"
|
|
24
|
+
"antelopejs-check-interface-ranges": "./dist/bin/check-interface-ranges.mjs",
|
|
25
|
+
"antelopejs-check-size": "./dist/bin/check-size.mjs",
|
|
26
|
+
"antelopejs-checks": "./dist/bin/checks.mjs",
|
|
27
|
+
"antelopejs-ratchet": "./dist/bin/ratchet.mjs",
|
|
28
|
+
"antelopejs-sync": "./dist/bin/sync.mjs"
|
|
25
29
|
},
|
|
26
30
|
"files": [
|
|
27
31
|
"dist"
|
|
@@ -31,6 +35,7 @@
|
|
|
31
35
|
"./oxc/lint": "./dist/oxc/lint.mjs",
|
|
32
36
|
"./oxc/fmt": "./dist/oxc/fmt.mjs",
|
|
33
37
|
"./oxc/anti-slop": "./dist/oxc/anti-slop/index.mjs",
|
|
38
|
+
"./oxc/guardrails": "./dist/oxc/guardrails/index.mjs",
|
|
34
39
|
"./knip": "./dist/knip.mjs",
|
|
35
40
|
"./eslint": "./dist/eslint.mjs"
|
|
36
41
|
},
|
|
@@ -46,19 +51,30 @@
|
|
|
46
51
|
"prepack": "pnpm run build",
|
|
47
52
|
"release": "pnpm run lint && pnpm run prepack && release-it",
|
|
48
53
|
"typecheck": "tsc --noEmit",
|
|
49
|
-
"test": "node --test \"src
|
|
50
|
-
"checks": "
|
|
51
|
-
"format:check": "oxfmt --check ."
|
|
54
|
+
"test": "node --test \"src/**/*.test.ts\"",
|
|
55
|
+
"checks": "node src/bin/checks.ts",
|
|
56
|
+
"format:check": "oxfmt --check .",
|
|
57
|
+
"size": "node src/bin/check-size.ts",
|
|
58
|
+
"ratchet": "node src/bin/ratchet.ts",
|
|
59
|
+
"prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
|
|
60
|
+
"sync": "node src/bin/sync.ts",
|
|
61
|
+
"ratchet:update": "node src/bin/ratchet.ts --update",
|
|
62
|
+
"sync:check": "node src/bin/sync.ts --check",
|
|
63
|
+
"knip": "knip"
|
|
52
64
|
},
|
|
53
65
|
"dependencies": {
|
|
54
|
-
"@oxlint/plugins": "1.81.0"
|
|
66
|
+
"@oxlint/plugins": "1.81.0",
|
|
67
|
+
"yaml": "^2.9.1"
|
|
55
68
|
},
|
|
56
69
|
"devDependencies": {
|
|
57
70
|
"@nuxt/eslint-config": "^1.17.0",
|
|
58
71
|
"@types/node": "^24.13.3",
|
|
59
72
|
"eslint-plugin-perfectionist": "^5.11.0",
|
|
73
|
+
"knip": "^6.34.0",
|
|
74
|
+
"lefthook": "^2.2.2",
|
|
60
75
|
"oxfmt": "^0.66.0",
|
|
61
76
|
"oxlint": "1.81.0",
|
|
77
|
+
"oxlint-tsgolint": "^7.0.2001",
|
|
62
78
|
"release-it": "^19.2.4",
|
|
63
79
|
"release-it-changelogen": "^0.1.0",
|
|
64
80
|
"tsdown": "^0.22.14",
|