@antelopejs/tooling-configs 0.0.8 → 0.2.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.
@@ -0,0 +1,98 @@
1
+ import { n as IGNORE_PATTERNS, t as AGENT_IGNORE_PATTERNS } from "./shared-NC0T8ykm.mjs";
2
+ import { existsSync, lstatSync } from "node:fs";
3
+ import { join, matchesGlob, relative, resolve } from "node:path";
4
+ import { execFileSync, spawnSync } from "node:child_process";
5
+ //#region src/guardrails/cli.ts
6
+ function runCli(main) {
7
+ try {
8
+ process.exitCode = main();
9
+ } catch (error) {
10
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
11
+ process.exitCode = 1;
12
+ }
13
+ }
14
+ //#endregion
15
+ //#region src/guardrails/files.ts
16
+ const SIZE_EXCLUSIONS = [
17
+ {
18
+ kind: "generated",
19
+ patterns: [
20
+ ...IGNORE_PATTERNS,
21
+ ...AGENT_IGNORE_PATTERNS,
22
+ "**/*.generated.*",
23
+ "**/generated/**",
24
+ "**/__snapshots__/**"
25
+ ]
26
+ },
27
+ {
28
+ kind: "fixtures",
29
+ patterns: ["**/fixtures/**", "**/__fixtures__/**"]
30
+ },
31
+ {
32
+ kind: "lockfiles",
33
+ patterns: [
34
+ "**/{pnpm-lock.yaml,package-lock.json,npm-shrinkwrap.json,yarn.lock,bun.lock,bun.lockb,Cargo.lock,poetry.lock,uv.lock,Gemfile.lock,composer.lock,go.sum}",
35
+ "**/*.lock",
36
+ "**/*.lockb"
37
+ ]
38
+ },
39
+ {
40
+ kind: "documentation",
41
+ patterns: ["**/CHANGELOG.md", "**/*.{md,mdx,markdown,MD}"]
42
+ },
43
+ {
44
+ kind: "assets",
45
+ patterns: ["**/*.{png,jpg,jpeg,gif,webp,avif,ico,svg,bmp,tiff,pdf,mp3,wav,ogg,flac,mp4,webm,mov,avi,woff,woff2,ttf,otf,eot,zip,gz,tgz,bz2,xz,7z,tar,rar,wasm,bin,exe,dll,so,dylib,db,sqlite,sqlite3,icns}"]
46
+ }
47
+ ];
48
+ function gitFiles(root) {
49
+ return [...new Set(execFileSync("git", [
50
+ "ls-files",
51
+ "--cached",
52
+ "--others",
53
+ "--exclude-standard",
54
+ "-z"
55
+ ], {
56
+ cwd: root,
57
+ encoding: "utf8"
58
+ }).split("\0").filter(Boolean))];
59
+ }
60
+ function repositoryRoot(cwd = process.cwd()) {
61
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
62
+ cwd,
63
+ encoding: "utf8"
64
+ }).trim();
65
+ }
66
+ function repositoryPath(root, file) {
67
+ return relative(root, resolve(root, file)).split("\\").join("/");
68
+ }
69
+ function fileBytes(root, file, staged = false) {
70
+ if (staged) {
71
+ const result = spawnSync("git", [
72
+ "cat-file",
73
+ "-s",
74
+ `:${file}`
75
+ ], {
76
+ cwd: root,
77
+ encoding: "utf8"
78
+ });
79
+ if (result.error) throw result.error;
80
+ return result.status === 0 ? Number(result.stdout.trim()) : void 0;
81
+ }
82
+ const path = join(root, file);
83
+ if (!existsSync(path)) return void 0;
84
+ const stat = lstatSync(path);
85
+ return stat.isFile() ? stat.size : void 0;
86
+ }
87
+ function authoredSizes(root, files = gitFiles(root), staged = false) {
88
+ const sizes = /* @__PURE__ */ new Map();
89
+ for (const input of files) {
90
+ const file = repositoryPath(root, input);
91
+ if (SIZE_EXCLUSIONS.some(({ patterns }) => patterns.some((pattern) => matchesGlob(file, pattern)))) continue;
92
+ const size = fileBytes(root, file, staged);
93
+ if (size !== void 0) sizes.set(file, size);
94
+ }
95
+ return sizes;
96
+ }
97
+ //#endregion
98
+ export { repositoryRoot as a, repositoryPath as i, fileBytes as n, runCli as o, gitFiles as r, authoredSizes as t };
package/dist/knip.mjs CHANGED
@@ -9,6 +9,11 @@
9
9
  * The front end is absent too: {@link DEFAULT_PROJECT} only reaches `src/`, so
10
10
  * `frontend-vue/` is outside the analysis unless a repository opts it in
11
11
  * through Knip's `workspaces`, which an ignore here would then override.
12
+ *
13
+ * - `src/interfaces/**`: the public API, imported through the package's `exports` subpaths.
14
+ * - `src/test/**`: the test tree run by `ajs module test`, which Knip has no plugin for.
15
+ * The whole directory, because the runner loads the harness and its fixtures; no test imports them.
16
+ * - `antelope.test.ts`: the `ajs module test` harness manifest, when a module keeps one at its root.
12
17
  */
13
18
  const DEFAULT_ENTRY = [
14
19
  "src/interfaces/**/*.ts",
package/dist/oxc/fmt.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as IGNORE_PATTERNS, t as AGENT_IGNORE_PATTERNS } from "../shared-Ch-ZcMQb.mjs";
1
+ import { n as IGNORE_PATTERNS, t as AGENT_IGNORE_PATTERNS } from "../shared-NC0T8ykm.mjs";
2
2
  import { defineConfig } from "oxfmt";
3
3
  //#region src/oxc/fmt.ts
4
4
  /**
@@ -0,0 +1,6 @@
1
+ import { n as TEST_FILE_GLOBS, t as ASSERTION_FUNCTION_NAMES } from "../../shared-BG0Nkzh0.mjs";
2
+ //#region src/oxc/guardrails/index.d.ts
3
+ /** AntelopeJS rules that reject comments and low-evidence tests. */
4
+ declare const guardrailsPlugin: import("@oxlint/plugins").Plugin;
5
+ //#endregion
6
+ export { ASSERTION_FUNCTION_NAMES, TEST_FILE_GLOBS, guardrailsPlugin as default };
@@ -0,0 +1,318 @@
1
+ import { a as matcherChain, i as isAssertion, n as TEST_FILE_GLOBS, o as memberName, r as callName, s as testCallback, t as ASSERTION_FUNCTION_NAMES } from "../../shared-WC0z3y9F.mjs";
2
+ import { defineRule, eslintCompatPlugin } from "@oxlint/plugins";
3
+ //#region src/oxc/guardrails/rules/no-comments.ts
4
+ const allowedCommentForms = [
5
+ {
6
+ type: "Shebang",
7
+ pattern: /^/u
8
+ },
9
+ {
10
+ type: "Block",
11
+ pattern: /^[*!]/u,
12
+ raw: true
13
+ },
14
+ {
15
+ type: "Line",
16
+ pattern: /^\/\s*<reference\s[^>]*\/\s*>\s*$/u
17
+ },
18
+ {
19
+ type: null,
20
+ pattern: /^(?:oxlint|eslint)-enable(?:\s|$)/u
21
+ },
22
+ {
23
+ type: null,
24
+ pattern: /^(?:oxlint|eslint)-disable(?:-line|-next-line)?\s+(?!-)[\w@/]/u
25
+ },
26
+ {
27
+ type: null,
28
+ pattern: /^@ts-expect-error(?:\s*:\s*|\s+)(?!:)\S/u
29
+ },
30
+ {
31
+ type: null,
32
+ pattern: /^SAFETY:/u,
33
+ safety: true
34
+ },
35
+ {
36
+ type: null,
37
+ pattern: /^(?:#|@)__PURE__\s*$/u
38
+ },
39
+ {
40
+ type: null,
41
+ pattern: /^@vite-ignore\b/u
42
+ },
43
+ {
44
+ type: null,
45
+ pattern: /^webpack(?:ChunkName|Prefetch|Preload|Mode|Ignore|Exports|Include|Exclude)\s*:/u
46
+ },
47
+ {
48
+ type: null,
49
+ pattern: /^(?:prettier|oxfmt)-ignore\s*$/u
50
+ }
51
+ ];
52
+ const disableDirective = /^(?:oxlint|eslint)-disable(?:-line|-next-line)?(?:\s|$)/u;
53
+ /** Ordinary comments must become executable constraints or documentation on a public item. */
54
+ const noCommentsRule = defineRule({
55
+ meta: {
56
+ type: "problem",
57
+ docs: { description: "Disallow comments except documentation and explicit compiler or safety directives." },
58
+ messages: {
59
+ comment: "Comments rot. Encode this constraint in a type, a lint rule, or a test, or write TSDoc (`/** */`) on the public item it documents.",
60
+ unnamedDisable: "Name the rule(s) to disable and give a reason, for example: oxlint-disable-next-line no-debugger -- debugging this operation."
61
+ }
62
+ },
63
+ createOnce(context) {
64
+ return { Program() {
65
+ let safetyLine;
66
+ for (const comment of context.sourceCode.getAllComments()) {
67
+ const text = comment.value.replace(/^\s*\*?\s*/u, "").trimEnd();
68
+ const form = allowedCommentForms.find((allowed) => (allowed.type === null || allowed.type === comment.type) && allowed.pattern.test("raw" in allowed ? comment.value : text));
69
+ const continuesSafety = comment.type === "Line" && safetyLine !== void 0 && comment.loc.start.line === safetyLine.loc.end.line + 1 && /^\s*$/u.test(context.sourceCode.text.slice(safetyLine.end, comment.start));
70
+ safetyLine = comment.type === "Line" && (continuesSafety || form !== void 0 && "safety" in form) ? comment : void 0;
71
+ const unnamedDisable = disableDirective.test(text) && form === void 0;
72
+ if (!unnamedDisable && (form !== void 0 || continuesSafety)) continue;
73
+ context.report({
74
+ loc: comment.loc,
75
+ messageId: unnamedDisable ? "unnamedDisable" : "comment"
76
+ });
77
+ }
78
+ } };
79
+ }
80
+ });
81
+ //#endregion
82
+ //#region src/oxc/guardrails/rules/no-constant-value-assertion.ts
83
+ const equalityMatchers = /* @__PURE__ */ new Set([
84
+ "toBe",
85
+ "toEqual",
86
+ "toStrictEqual",
87
+ "to.equal",
88
+ "to.eq",
89
+ "eql",
90
+ "to.eql",
91
+ "to.deep.eq",
92
+ "to.deep.equal",
93
+ "to.be.equal"
94
+ ]);
95
+ const constantName = /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/u;
96
+ function isConstant(node) {
97
+ if (node?.type === "Identifier") return constantName.test(node.name);
98
+ if (node?.type !== "MemberExpression") return false;
99
+ return isConstant(node.object);
100
+ }
101
+ function isLiteralValue(node) {
102
+ if (node.type === "Literal") return !("regex" in node) && !("bigint" in node);
103
+ if (node.type === "TemplateLiteral") return node.expressions.length === 0;
104
+ if (node.type === "UnaryExpression") return node.operator === "-" && node.argument.type === "Literal" && Number.isFinite(node.argument.value);
105
+ if (node.type === "ArrayExpression") return node.elements.every((element) => element !== null && isLiteralValue(element));
106
+ if (node.type === "ObjectExpression") return node.properties.every((property) => property.type === "Property" && !property.computed && !property.method && !property.shorthand && property.kind === "init" && isLiteralValue(property.value));
107
+ return false;
108
+ }
109
+ /** Constant declarations already specify their values; tests must assert the behavior they drive. */
110
+ const noConstantValueAssertionRule = defineRule({
111
+ meta: {
112
+ type: "problem",
113
+ docs: { description: "Disallow assertions that only restate a constant's literal value." },
114
+ messages: { constant: "A test that restates a constant fails only when someone edits the constant on purpose. Assert the behaviour the constant drives." }
115
+ },
116
+ createOnce(context) {
117
+ return { CallExpression(node) {
118
+ const chain = matcherChain(node);
119
+ const expected = node.arguments[0];
120
+ if (chain !== null && equalityMatchers.has(chain.matcher) && isConstant(chain.expect.arguments[0]) && expected !== void 0 && isLiteralValue(expected)) context.report({
121
+ node,
122
+ messageId: "constant"
123
+ });
124
+ } };
125
+ }
126
+ });
127
+ //#endregion
128
+ //#region src/oxc/guardrails/rules/no-fs-read-in-tests.ts
129
+ const fsModules = /* @__PURE__ */ new Set(["fs", "node:fs"]);
130
+ const readMethods = /* @__PURE__ */ new Set(["readFileSync", "readdirSync"]);
131
+ function isFsRequire(node) {
132
+ return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments[0]?.type === "Literal" && fsModules.has(String(node.arguments[0].value));
133
+ }
134
+ function isFsNamespace(source, node) {
135
+ if (isFsRequire(node)) return true;
136
+ if (node.type !== "Identifier") return false;
137
+ let scope = source.getScope(node);
138
+ while (scope !== null) {
139
+ const binding = scope.set.get(node.name);
140
+ if (binding !== void 0) return binding.defs.some((definition) => {
141
+ if (definition.node.type === "TSImportEqualsDeclaration") {
142
+ const reference = definition.node.moduleReference;
143
+ return reference.type === "TSExternalModuleReference" && fsModules.has(reference.expression.value);
144
+ }
145
+ if (definition.type === "ImportBinding") return (definition.node.type !== "ImportSpecifier" || definition.node.imported.type === "Identifier" && definition.node.imported.name === "default") && definition.parent?.type === "ImportDeclaration" && fsModules.has(definition.parent.source.value);
146
+ return definition.node.type === "VariableDeclarator" && definition.node.init !== null && isFsRequire(definition.node.init);
147
+ });
148
+ scope = scope.upper;
149
+ }
150
+ return false;
151
+ }
152
+ /** Tests exercise behavior through public interfaces rather than reading implementation text. */
153
+ const noFsReadInTestsRule = defineRule({
154
+ meta: {
155
+ type: "problem",
156
+ docs: { description: "Move synchronous filesystem reads out of tests into fixture helpers." },
157
+ messages: { fsRead: "Tests exercise behaviour through the public interface. A test that reads source files asserts text, not behaviour. Move fixture reads into a helper under fixtures/, helpers/ or locales/." }
158
+ },
159
+ createOnce(context) {
160
+ return {
161
+ ImportDeclaration(node) {
162
+ if (!fsModules.has(node.source.value) || node.importKind === "type") return;
163
+ for (const specifier of node.specifiers) {
164
+ if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") continue;
165
+ const name = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
166
+ if (readMethods.has(name)) context.report({
167
+ node: specifier,
168
+ messageId: "fsRead"
169
+ });
170
+ }
171
+ },
172
+ MemberExpression(node) {
173
+ const name = memberName(node);
174
+ if (name !== null && readMethods.has(name) && isFsNamespace(context.sourceCode, node.object)) context.report({
175
+ node,
176
+ messageId: "fsRead"
177
+ });
178
+ },
179
+ VariableDeclarator(node) {
180
+ if (node.id.type !== "ObjectPattern" || node.init === null || !isFsNamespace(context.sourceCode, node.init)) return;
181
+ for (const property of node.id.properties) {
182
+ if (property.type !== "Property") continue;
183
+ const name = property.key.type === "Identifier" && !property.computed ? property.key.name : property.key.type === "Literal" ? String(property.key.value) : null;
184
+ if (name !== null && readMethods.has(name)) context.report({
185
+ node: property,
186
+ messageId: "fsRead"
187
+ });
188
+ }
189
+ }
190
+ };
191
+ }
192
+ });
193
+ //#endregion
194
+ //#region src/oxc/guardrails/rules/no-typeof-function-only-test.ts
195
+ const functionMatchers = /* @__PURE__ */ new Set([
196
+ "toBeTypeOf",
197
+ "to.be.a",
198
+ "to.be.an"
199
+ ]);
200
+ const typeofMatchers = /* @__PURE__ */ new Set(["toBe", "to.equal"]);
201
+ const assertTypeofMatchers = /* @__PURE__ */ new Set(["assert.equal", "assert.strictEqual"]);
202
+ function isTypeof(node) {
203
+ return node?.type === "UnaryExpression" && node.operator === "typeof";
204
+ }
205
+ function isFunctionString(node) {
206
+ return node?.type === "Literal" && node.value === "function";
207
+ }
208
+ function isFunctionCheck(node) {
209
+ const name = callName(node.callee);
210
+ if (name === "assert.isFunction") return true;
211
+ if (name !== null && assertTypeofMatchers.has(name)) return isTypeof(node.arguments[0]) && isFunctionString(node.arguments[1]);
212
+ let outer = node;
213
+ while (outer.parent.type === "MemberExpression" && outer.parent.object === outer) {
214
+ let member = outer.parent;
215
+ while (member.parent.type === "MemberExpression" && member.parent.object === member) member = member.parent;
216
+ if (member.parent.type !== "CallExpression" || member.parent.callee !== member) break;
217
+ outer = member.parent;
218
+ }
219
+ const chain = matcherChain(outer);
220
+ if (chain === null || !isFunctionString(outer.arguments[0])) return false;
221
+ return functionMatchers.has(chain.matcher) || typeofMatchers.has(chain.matcher) && isTypeof(chain.expect.arguments[0]);
222
+ }
223
+ /** Function existence alone is already a type-checker guarantee, not a behavioral test. */
224
+ const noTypeofFunctionOnlyTestRule = defineRule({
225
+ meta: {
226
+ type: "problem",
227
+ docs: { description: "Require tests to do more than assert that functions exist." },
228
+ messages: { functionOnly: "The type checker already proves this. Call the function and assert what it returns or does." }
229
+ },
230
+ createOnce(context) {
231
+ const frames = [];
232
+ function recordAssertion(node) {
233
+ const frame = frames.at(-1);
234
+ if (frame === void 0 || node.start < frame.callback.start || node.end > frame.callback.end || !isAssertion(node)) return;
235
+ frame.assertions.push(node.type === "CallExpression" && isFunctionCheck(node));
236
+ }
237
+ return {
238
+ before() {
239
+ frames.length = 0;
240
+ },
241
+ CallExpression(node) {
242
+ const callback = testCallback(node);
243
+ if (callback !== void 0) frames.push({
244
+ node,
245
+ callback,
246
+ assertions: []
247
+ });
248
+ else recordAssertion(node);
249
+ },
250
+ MemberExpression: recordAssertion,
251
+ "CallExpression:exit"(node) {
252
+ const frame = frames.at(-1);
253
+ if (frame?.node !== node) return;
254
+ frames.pop();
255
+ if (frame.assertions.length > 0 && frame.assertions.every(Boolean)) context.report({
256
+ node,
257
+ messageId: "functionOnly"
258
+ });
259
+ }
260
+ };
261
+ }
262
+ });
263
+ //#endregion
264
+ //#region src/oxc/guardrails/rules/require-assertion.ts
265
+ /** Test callbacks must contain an assertion regardless of their framework or filename. */
266
+ const requireAssertionRule = defineRule({
267
+ meta: {
268
+ type: "problem",
269
+ docs: { description: "Require an assertion in every executable test callback." },
270
+ messages: { missingAssertion: "Call an assertion in this test to verify its behavior." }
271
+ },
272
+ createOnce(context) {
273
+ const frames = [];
274
+ function recordAssertion(node) {
275
+ const frame = frames.at(-1);
276
+ if (frame !== void 0 && node.start >= frame.callback.start && node.end <= frame.callback.end && isAssertion(node)) frame.hasAssertion = true;
277
+ }
278
+ return {
279
+ before() {
280
+ frames.length = 0;
281
+ },
282
+ CallExpression(node) {
283
+ const callback = testCallback(node);
284
+ if (callback !== void 0) frames.push({
285
+ node,
286
+ callback,
287
+ hasAssertion: false
288
+ });
289
+ else recordAssertion(node);
290
+ },
291
+ MemberExpression: recordAssertion,
292
+ "CallExpression:exit"(node) {
293
+ const frame = frames.at(-1);
294
+ if (frame?.node !== node) return;
295
+ frames.pop();
296
+ if (!frame.hasAssertion) context.report({
297
+ node,
298
+ messageId: "missingAssertion"
299
+ });
300
+ }
301
+ };
302
+ }
303
+ });
304
+ //#endregion
305
+ //#region src/oxc/guardrails/index.ts
306
+ /** AntelopeJS rules that reject comments and low-evidence tests. */
307
+ const guardrailsPlugin = eslintCompatPlugin({
308
+ meta: { name: "antelopejs" },
309
+ rules: {
310
+ "no-comments": noCommentsRule,
311
+ "no-fs-read-in-tests": noFsReadInTestsRule,
312
+ "no-constant-value-assertion": noConstantValueAssertionRule,
313
+ "no-typeof-function-only-test": noTypeofFunctionOnlyTestRule,
314
+ "require-assertion": requireAssertionRule
315
+ }
316
+ });
317
+ //#endregion
318
+ export { ASSERTION_FUNCTION_NAMES, TEST_FILE_GLOBS, guardrailsPlugin as default };