@antelopejs/tooling-configs 0.0.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 ADDED
@@ -0,0 +1,225 @@
1
+ # @antelopejs/tooling-configs
2
+
3
+ Shared tooling presets for AntelopeJS repositories: [oxlint](https://oxc.rs) (including
4
+ the vendored [anti-slop](https://github.com/dmmulroy/anti-slop) rules), [oxfmt](https://oxc.rs)
5
+ and [Knip](https://knip.dev).
6
+
7
+ Together they replace Biome outright — lint, format and import order — and Prettier in the
8
+ Nuxt layers, since oxfmt formats `.vue` and sorts Tailwind classes natively. The front end
9
+ keeps ESLint for linting, from a shared config here.
10
+
11
+ ## Requirements
12
+
13
+ Node **24** (or at least 22.18). oxlint reads `oxlint.config.ts` and loads JS plugins by
14
+ executing TypeScript, which earlier runtimes cannot do.
15
+
16
+ ## Install
17
+
18
+ The package is not on npm yet. Until it is, consume it straight from this repository:
19
+
20
+ ```bash
21
+ pnpm add -D oxlint@1.81.0 oxfmt "github:AntelopeJS/tooling-configs"
22
+ ```
23
+
24
+ Add `eslint-plugin-perfectionist` too unless you turn import sorting off — it is an
25
+ optional peer dependency because it pulls ESLint and typescript-eslint along with it:
26
+
27
+ ```bash
28
+ pnpm add -D eslint-plugin-perfectionist
29
+ ```
30
+
31
+ `dist/` is committed for exactly as long as that install path lasts: pnpm 11 refuses to
32
+ run a git dependency's build unless every consumer allowlists it by commit hash, which
33
+ would break on every push here. CI fails if the committed build is stale. When the
34
+ package reaches npm, delete `dist/` from git, restore the `prepare` script, and change
35
+ consumers from `github:AntelopeJS/tooling-configs` to a version range — the import
36
+ specifiers never change.
37
+
38
+ Pin `oxlint` to the exact version this package depends on for `@oxlint/plugins`
39
+ (currently **1.81.0**). The JS plugin API is still alpha and the two packages must move
40
+ together. Once the package is published, this becomes a normal version range.
41
+
42
+ ## oxlint
43
+
44
+ ```ts
45
+ // oxlint.config.ts
46
+ import { defineConfig } from "oxlint";
47
+ import { antelopePreset } from "@antelopejs/tooling-configs/oxc/lint";
48
+
49
+ export default defineConfig({
50
+ extends: [antelopePreset()],
51
+ // Only read from the root config, never from a preset.
52
+ options: { typeAware: true },
53
+ });
54
+ ```
55
+
56
+ ```jsonc
57
+ // package.json
58
+ {
59
+ "scripts": {
60
+ "lint": "oxlint",
61
+ "lint:fix": "oxlint --fix",
62
+ },
63
+ }
64
+ ```
65
+
66
+ Type-aware rules (`typescript/no-floating-promises`) also need the tsgolint binary:
67
+
68
+ ```bash
69
+ pnpm add -D oxlint-tsgolint
70
+ ```
71
+
72
+ ### What the preset turns on
73
+
74
+ | Group | Contents |
75
+ | -------------- | ----------------------------------------------------------------------------------------------------------- |
76
+ | oxc defaults | the `correctness` category, plus the `eslint`, `typescript`, `node`, `oxc`, `import` and `promise` plugins |
77
+ | imports | `import/no-cycle`, `import/no-self-import`, `import/no-duplicates` as errors |
78
+ | | (a repository with existing cycles starts with `importCycles: "warn"`) |
79
+ | import sorting | `perfectionist/sort-imports`, autofixed by `oxlint --fix` |
80
+ | complexity | `max-params` 5, `max-lines-per-function` 120, `max-lines` 500, `max-depth` 4, `complexity` 20 — as warnings |
81
+ | anti-slop | all 15 generic rules, as warnings |
82
+ | type-aware | `typescript/no-floating-promises` as an error |
83
+
84
+ ### Options
85
+
86
+ ```ts
87
+ antelopePreset({
88
+ antiSlop: "error", // "warn" (default) | "error" | false
89
+ importCycles: "warn", // "error" (default) | "warn" | false
90
+ complexity: { maxParams: 8 }, // override thresholds, or false
91
+ importSorting: false, // leave imports alone
92
+ typeAware: false, // no tsgolint in this repository
93
+ });
94
+ ```
95
+
96
+ `correctness` is raised to error deliberately. oxlint reports it as warnings, and warnings
97
+ do not fail the command — a preset that left it alone could never fail a pipeline, while
98
+ Biome was failing the build on the same rules.
99
+
100
+ `antiSlop` and `complexity` default to warnings on purpose: a repository adopting them
101
+ has findings to work through, and a red pipeline on day one teaches the team to ignore
102
+ the pipeline. Raise them to `"error"` once the backlog is clear.
103
+
104
+ ### Adding your own ignore patterns
105
+
106
+ oxlint does not merge `ignorePatterns` across `extends`. A root config that declares its
107
+ own **replaces** the preset's, so spread them:
108
+
109
+ ```ts
110
+ import {
111
+ ANTELOPE_IGNORE_PATTERNS,
112
+ antelopePreset,
113
+ } from "@antelopejs/tooling-configs/oxc/lint";
114
+
115
+ export default defineConfig({
116
+ extends: [antelopePreset()],
117
+ ignorePatterns: [...ANTELOPE_IGNORE_PATTERNS, "playground/**"],
118
+ });
119
+ ```
120
+
121
+ ## oxfmt
122
+
123
+ ```ts
124
+ // oxfmt.config.ts
125
+ import { antelopeFmtPreset } from "@antelopejs/tooling-configs/oxc/fmt";
126
+
127
+ export default antelopeFmtPreset();
128
+ ```
129
+
130
+ ```jsonc
131
+ // package.json
132
+ {
133
+ "scripts": {
134
+ "format": "oxfmt .",
135
+ "format:check": "oxfmt --check .",
136
+ },
137
+ }
138
+ ```
139
+
140
+ ### The house style
141
+
142
+ | Option | Value |
143
+ | ---------------------- | ---------- |
144
+ | `printWidth` | 80 |
145
+ | `tabWidth` / `useTabs` | 2 / spaces |
146
+ | `semi` | true |
147
+ | `singleQuote` | false |
148
+ | `trailingComma` | `"all"` |
149
+
150
+ Every field is set explicitly, including those that already match oxfmt's defaults.
151
+ Repositories carry `.editorconfig` files that contradict each other — five declare tabs,
152
+ two declare spaces, which is why the codebases drifted apart in the first place — and
153
+ oxfmt falls back to `.editorconfig` for any field a config leaves unset. Stating all of
154
+ them is what makes the style identical everywhere. Fix the repository's `.editorconfig`
155
+ to match anyway, so editors agree with the formatter.
156
+
157
+ Import order is not part of it: `perfectionist/sort-imports` owns that on the lint side.
158
+ Enabling oxfmt's `sortImports` as well would have two tools sorting the same imports
159
+ differently, each undoing the other on every save.
160
+
161
+ Extra ignores are **added** to the shared ones rather than replacing them, so a repository
162
+ adopting oxfmt in stages can park a directory without losing the rest:
163
+
164
+ ```ts
165
+ export default antelopeFmtPreset({ ignorePatterns: ["nuxt-layer/**"] });
166
+ ```
167
+
168
+ In a Nuxt layer, pass the theme stylesheet to replace `prettier-plugin-tailwindcss`:
169
+
170
+ ```ts
171
+ export default antelopeFmtPreset({
172
+ tailwindStylesheet: "./assets/css/main.css",
173
+ });
174
+ ```
175
+
176
+ ### Replacing Biome
177
+
178
+ Per repository: delete `biome.json` and the `@biomejs/biome` dependency, point `lint`,
179
+ `lint:fix` and `format` at oxlint and oxfmt, and port anything the Biome config carried
180
+ that the preset does not (`noRestrictedImports` guards become `import/no-cycle`). Reformat
181
+ in its own commit and record it in `.git-blame-ignore-revs`, which GitHub honours, so the
182
+ pass does not bury the history.
183
+
184
+ ## ESLint (Nuxt layers)
185
+
186
+ oxlint does not lint `.vue` files yet, so the front end stays on ESLint:
187
+
188
+ ```js
189
+ // nuxt-layer/eslint.config.mjs
190
+ import { antelopeNuxtConfig } from "@antelopejs/tooling-configs/eslint";
191
+
192
+ export default antelopeNuxtConfig({ dirs: ["./playground"] });
193
+ ```
194
+
195
+ `@nuxt/eslint` supplies the Vue and TypeScript rules; the preset only records where
196
+ AntelopeJS layers differ from it — single-word page components, `any` and `{}` as
197
+ load-bearing types in the interface layer, `_`-prefixed unused bindings, and import order
198
+ left to the formatter.
199
+
200
+ Formatting rules are deliberately absent, `vue/max-attributes-per-line` included: oxfmt
201
+ formats `.vue`, and a lint rule that also reflows markup would fight it on every save.
202
+ Declare `@nuxt/eslint-config` explicitly — nine layers use it today through
203
+ `@nuxt/eslint` without declaring it.
204
+
205
+ ## Knip
206
+
207
+ ```ts
208
+ // knip.config.ts
209
+ import { antelopeKnipConfig } from "@antelopejs/tooling-configs/knip";
210
+
211
+ export default antelopeKnipConfig();
212
+ ```
213
+
214
+ The defaults declare what Knip cannot infer for an AntelopeJS module: `src/index.ts` and
215
+ the interface subpaths as public API, and `src/test/**/*.test.ts` as entry points, since
216
+ `ajs module test` is not a runner Knip has a plugin for. Without them, Knip reports the
217
+ whole test suite as dead code.
218
+
219
+ ## Vendored anti-slop
220
+
221
+ `src/oxc/anti-slop/` is a copy of upstream, kept byte-identical so it can be diffed
222
+ against new releases. See `src/oxc/anti-slop/VENDORED.md` for the commit it came from and
223
+ any local edits. It is excluded from this repository's own formatting and linting.
224
+
225
+ Upstream is MIT (`src/oxc/anti-slop/LICENSE`); the rest of this repository is Apache-2.0.
@@ -0,0 +1,34 @@
1
+ import { createConfigForNuxt } from "@nuxt/eslint-config/flat";
2
+ //#region src/eslint.d.ts
3
+ /** The rule map `overrideRules` accepts, derived so no transitive type is named. */
4
+ type NuxtRules = Parameters<ReturnType<typeof createConfigForNuxt>["overrideRules"]>[0];
5
+ interface AntelopeNuxtConfigOptions {
6
+ /** Extra source directories for the Nuxt config, e.g. `["./playground"]`. */
7
+ dirs?: string[];
8
+ /** Rules layered on top of the shared set. */
9
+ rules?: NuxtRules;
10
+ }
11
+ /**
12
+ * The shared rules for a Nuxt layer. oxlint does not lint `.vue` files yet, so
13
+ * the front end stays on ESLint, and `@nuxt/eslint` supplies the Vue and
14
+ * TypeScript rules; this only records where AntelopeJS layers differ from it.
15
+ *
16
+ * Formatting rules are deliberately absent. oxfmt formats `.vue`, and a lint
17
+ * rule that also reflows markup (`vue/max-attributes-per-line`, which eight
18
+ * layers carry today) would fight it on every save.
19
+ */
20
+ declare const ANTELOPE_NUXT_RULES: NuxtRules;
21
+ /**
22
+ * ESLint config for an AntelopeJS Nuxt layer.
23
+ *
24
+ * @example
25
+ * ```js
26
+ * // eslint.config.mjs
27
+ * import { antelopeNuxtConfig } from "@antelopejs/tooling-configs/eslint";
28
+ *
29
+ * export default antelopeNuxtConfig({ dirs: ["./playground"] });
30
+ * ```
31
+ */
32
+ declare function antelopeNuxtConfig(options?: AntelopeNuxtConfigOptions): ReturnType<typeof createConfigForNuxt>;
33
+ //#endregion
34
+ export { ANTELOPE_NUXT_RULES, AntelopeNuxtConfigOptions, antelopeNuxtConfig };
@@ -0,0 +1,46 @@
1
+ import { createConfigForNuxt } from "@nuxt/eslint-config/flat";
2
+ //#region src/eslint.ts
3
+ /**
4
+ * The shared rules for a Nuxt layer. oxlint does not lint `.vue` files yet, so
5
+ * the front end stays on ESLint, and `@nuxt/eslint` supplies the Vue and
6
+ * TypeScript rules; this only records where AntelopeJS layers differ from it.
7
+ *
8
+ * Formatting rules are deliberately absent. oxfmt formats `.vue`, and a lint
9
+ * rule that also reflows markup (`vue/max-attributes-per-line`, which eight
10
+ * layers carry today) would fight it on every save.
11
+ */
12
+ const ANTELOPE_NUXT_RULES = {
13
+ "import/first": "off",
14
+ "import/order": "off",
15
+ "vue/multi-word-component-names": "off",
16
+ "vue/require-default-prop": "off",
17
+ "@typescript-eslint/no-explicit-any": "off",
18
+ "@typescript-eslint/no-empty-object-type": "off",
19
+ "@typescript-eslint/no-unused-vars": ["error", {
20
+ argsIgnorePattern: "^_",
21
+ varsIgnorePattern: "^_",
22
+ caughtErrorsIgnorePattern: "^_"
23
+ }]
24
+ };
25
+ /**
26
+ * ESLint config for an AntelopeJS Nuxt layer.
27
+ *
28
+ * @example
29
+ * ```js
30
+ * // eslint.config.mjs
31
+ * import { antelopeNuxtConfig } from "@antelopejs/tooling-configs/eslint";
32
+ *
33
+ * export default antelopeNuxtConfig({ dirs: ["./playground"] });
34
+ * ```
35
+ */
36
+ function antelopeNuxtConfig(options = {}) {
37
+ return createConfigForNuxt({
38
+ features: { tooling: true },
39
+ dirs: { src: options.dirs ?? [] }
40
+ }).overrideRules({
41
+ ...ANTELOPE_NUXT_RULES,
42
+ ...options.rules
43
+ });
44
+ }
45
+ //#endregion
46
+ export { ANTELOPE_NUXT_RULES, antelopeNuxtConfig };
@@ -0,0 +1,25 @@
1
+ import { KnipConfig } from "knip";
2
+ //#region src/knip.d.ts
3
+ /**
4
+ * Knip finds files, exports and dependencies nothing reaches any more. Its
5
+ * defaults assume a conventional entry point, which an AntelopeJS module does
6
+ * not have: the runtime loads `src/index.ts` and every interface subpath, and
7
+ * `ajs module test` runs the compiled test tree rather than a test runner Knip
8
+ * knows about. Without those declared, Knip reports the whole test suite as
9
+ * dead code.
10
+ */
11
+ interface AntelopeKnipOptions {
12
+ /** Extra entry points, appended to the AntelopeJS defaults. */
13
+ entry?: string[];
14
+ /** Extra project files to analyse, appended to the AntelopeJS defaults. */
15
+ project?: string[];
16
+ /** Dependencies Knip cannot see (loaded by the runtime, a CLI, or a config). */
17
+ ignoreDependencies?: string[];
18
+ /** Paths to leave out entirely. */
19
+ ignore?: string[];
20
+ /** Binaries Knip cannot resolve, appended to the AntelopeJS defaults. */
21
+ ignoreBinaries?: string[];
22
+ }
23
+ declare function antelopeKnipConfig(options?: AntelopeKnipOptions): KnipConfig;
24
+ //#endregion
25
+ export { AntelopeKnipOptions, antelopeKnipConfig };
package/dist/knip.mjs ADDED
@@ -0,0 +1,30 @@
1
+ //#region src/knip.ts
2
+ const DEFAULT_ENTRY = [
3
+ "src/interfaces/**/*.ts",
4
+ "src/test/**/*.test.ts",
5
+ "scripts/**/*.{ts,mjs}"
6
+ ];
7
+ const DEFAULT_PROJECT = ["src/**/*.ts", "scripts/**/*.{ts,mjs}"];
8
+ const DEFAULT_IGNORE = [
9
+ "dist/**",
10
+ "nuxt-layer/**",
11
+ "playground/**",
12
+ ".antelope/**"
13
+ ];
14
+ /**
15
+ * Every layer's CI runs `cd nuxt-layer && pnpm typecheck`. Knip parses the
16
+ * workflow, sees a binary it cannot resolve, and reports it: the script lives in
17
+ * the layer's own package.json, outside the analysed project.
18
+ */
19
+ const DEFAULT_IGNORE_BINARIES = ["typecheck"];
20
+ function antelopeKnipConfig(options = {}) {
21
+ return {
22
+ entry: [...DEFAULT_ENTRY, ...options.entry ?? []],
23
+ project: [...DEFAULT_PROJECT, ...options.project ?? []],
24
+ ignore: [...DEFAULT_IGNORE, ...options.ignore ?? []],
25
+ ignoreDependencies: options.ignoreDependencies ?? [],
26
+ ignoreBinaries: [...DEFAULT_IGNORE_BINARIES, ...options.ignoreBinaries ?? []]
27
+ };
28
+ }
29
+ //#endregion
30
+ export { antelopeKnipConfig };
@@ -0,0 +1,5 @@
1
+ //#region src/oxc/anti-slop/index.d.ts
2
+ /** Generic Oxlint rules that reject low-evidence and low-signal implementation patterns. */
3
+ declare const antiSlopPlugin: import("@oxlint/plugins").Plugin;
4
+ //#endregion
5
+ export { antiSlopPlugin as default };