@coryrylan/tools 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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/dist/eslint/configs/browser.d.ts +12 -0
  4. package/dist/eslint/configs/browser.js +40 -0
  5. package/dist/eslint/configs/html.d.ts +10 -0
  6. package/dist/eslint/configs/html.js +38 -0
  7. package/dist/eslint/configs/json.d.ts +13 -0
  8. package/dist/eslint/configs/json.js +39 -0
  9. package/dist/eslint/configs/shared.d.ts +11 -0
  10. package/dist/eslint/configs/shared.js +19 -0
  11. package/dist/eslint/configs/tests.d.ts +8 -0
  12. package/dist/eslint/configs/tests.js +20 -0
  13. package/dist/eslint/configs/typescript.d.ts +18 -0
  14. package/dist/eslint/configs/typescript.js +104 -0
  15. package/dist/eslint/index.d.ts +27 -0
  16. package/dist/eslint/index.js +19 -0
  17. package/dist/eslint/plugin.d.ts +8 -0
  18. package/dist/eslint/plugin.js +36 -0
  19. package/dist/eslint/rules/consistent-error-messages.d.ts +10 -0
  20. package/dist/eslint/rules/consistent-error-messages.js +127 -0
  21. package/dist/eslint/rules/no-dead-code.d.ts +16 -0
  22. package/dist/eslint/rules/no-dead-code.js +110 -0
  23. package/dist/eslint/rules/no-deep-class-inheritance.d.ts +19 -0
  24. package/dist/eslint/rules/no-deep-class-inheritance.js +132 -0
  25. package/dist/eslint/rules/no-reexport-barrels.d.ts +10 -0
  26. package/dist/eslint/rules/no-reexport-barrels.js +55 -0
  27. package/dist/eslint/rules/no-single-consumer-abstraction.d.ts +3 -0
  28. package/dist/eslint/rules/no-single-consumer-abstraction.js +364 -0
  29. package/dist/eslint/rules/no-unjustified-disable.d.ts +11 -0
  30. package/dist/eslint/rules/no-unjustified-disable.js +75 -0
  31. package/dist/eslint/rules/no-unpinned-dependency-ranges.d.ts +46 -0
  32. package/dist/eslint/rules/no-unpinned-dependency-ranges.js +79 -0
  33. package/dist/eslint/rules/require-listener-cleanup.d.ts +8 -0
  34. package/dist/eslint/rules/require-listener-cleanup.js +202 -0
  35. package/dist/eslint/rules/require-observer-cleanup.d.ts +8 -0
  36. package/dist/eslint/rules/require-observer-cleanup.js +69 -0
  37. package/dist/eslint/rules/require-timer-cleanup.d.ts +10 -0
  38. package/dist/eslint/rules/require-timer-cleanup.js +142 -0
  39. package/dist/eslint/rules/utils.d.ts +45 -0
  40. package/dist/eslint/rules/utils.js +109 -0
  41. package/dist/prettier/index.d.ts +15 -0
  42. package/dist/prettier/index.js +26 -0
  43. package/dist/stylelint/index.d.ts +10 -0
  44. package/dist/stylelint/index.js +50 -0
  45. package/dist/vale/styles/config/vocabularies/Tools/accept.txt +80 -0
  46. package/dist/vale/styles/config/vocabularies/Tools/reject.txt +5 -0
  47. package/dist/vale/vale.ini +27 -0
  48. package/dist/vite/index.d.ts +37 -0
  49. package/dist/vite/index.js +61 -0
  50. package/dist/vite/plugins/dts.d.ts +26 -0
  51. package/dist/vite/plugins/dts.js +106 -0
  52. package/dist/vite/plugins/write-if-changed.d.ts +20 -0
  53. package/dist/vite/plugins/write-if-changed.js +33 -0
  54. package/dist/vitest/browser.d.ts +31 -0
  55. package/dist/vitest/browser.js +105 -0
  56. package/dist/vitest/index.d.ts +28 -0
  57. package/dist/vitest/index.js +34 -0
  58. package/package.json +200 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cory Rylan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # @coryrylan/tools
2
+
3
+ Shared tooling configs - ESLint, Prettier, Stylelint, Vale, Vite, and
4
+ Vitest - plus custom ESLint rules for keeping codebases maintainable when
5
+ coding agents are the primary contributors.
6
+
7
+ ## Philosophy
8
+
9
+ When an agent is the one writing most of a diff, prose instructions living in
10
+ an `AGENTS.md` file are advisory at best. They have to be loaded into
11
+ context, correctly interpreted, and remembered turns later, and none of that
12
+ is guaranteed on any given run. Prose decays: it drifts out of context, gets
13
+ skimmed instead of read, or is outweighed by whatever the agent is focused on
14
+ in the moment. A lint rule has none of these failure modes: it runs on every
15
+ diff, every time, and fails the build instead of hoping to be noticed. Every
16
+ rule and every shared config in this package exists to replace a paragraph an
17
+ agent would otherwise have to read (and might ignore, forget, or misapply)
18
+ with a deterministic check or preset that gates the diff instead.
19
+
20
+ ## Surfaces
21
+
22
+ | Entry | What it ships |
23
+ | ------------------------------------------------ | -------------------------------------------------------------------------------- |
24
+ | `@coryrylan/tools/eslint` | Flat configs plus the custom `tools/*` rule plugin. |
25
+ | `@coryrylan/tools/prettier` | The shared Prettier config. |
26
+ | `@coryrylan/tools/stylelint` | Shared Stylelint config (standard + logical properties + design-token checks). |
27
+ | `@coryrylan/tools/vite` | `createNodeLibraryBuildConfig` / `createBrowserLibraryBuildConfig` factories. |
28
+ | `@coryrylan/tools/vite/plugins/write-if-changed` | Vite plugin that skips writing chunks whose content didn't change. |
29
+ | `@coryrylan/tools/vite/plugins/dts` | Vite plugin that runs the consumer's TypeScript compiler for declaration output. |
30
+ | `@coryrylan/tools/vitest` | `nodeTestConfig` preset for node-environment unit tests. |
31
+ | `@coryrylan/tools/vitest/browser` | `browserTestConfig` preset for browser-mode (Playwright Chromium) tests. |
32
+ | `dist/vale/` | Vale starter kit: ini template plus shared accept/reject vocabulary. |
33
+
34
+ ## ESLint
35
+
36
+ ```sh
37
+ pnpm add -D eslint @coryrylan/tools typescript typescript-eslint
38
+ ```
39
+
40
+ The remaining peers (`@eslint/js`, `@eslint/json`, `eslint-plugin-jsdoc`)
41
+ install automatically as regular peer dependencies under pnpm 10 or npm.
42
+ Yarn users should add them to `devDependencies` explicitly.
43
+
44
+ ```js
45
+ // eslint.config.js
46
+ import { typescriptConfig, jsonConfig } from '@coryrylan/tools/eslint';
47
+
48
+ export default [...typescriptConfig, ...jsonConfig];
49
+ ```
50
+
51
+ A default export is also available if you'd rather reach into the plugin and
52
+ config map directly: `import agentLintRules from '@coryrylan/tools/eslint'`
53
+ exposes `{ plugin, configs }`, where `configs` is keyed by `typescript`, `tests`, `browser`, `html`, and `json`.
54
+
55
+ ### Configs
56
+
57
+ | Config | What it enables | Requirements |
58
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
59
+ | `typescriptConfig` | On all JS/TS files: size/complexity budgets (`complexity` 8, `max-depth` 3, `max-params` 3, `max-lines` 500, `max-statements` 15, `max-lines-per-function` 50, `max-nested-callbacks` 3) plus `eqeqeq`, `prefer-const`, `no-shadow`, `no-warning-comments`, `no-param-reassign`, and friends, and 5 syntax-only `tools/*` rules. On TS files: type-aware `strictTypeChecked`, plus `@typescript-eslint/no-floating-promises`, `only-throw-error`, `consistent-type-imports`, `switch-exhaustiveness-check`, `explicit-member-accessibility` (`no-public`), a `#private`-over-`private` selector, `no-explicit-any`, `no-unnecessary-boolean-literal-compare`, `jsdoc` checks (`informative-docs`, `no-types`, `valid-types`, `check-tag-names`), and the type-aware `tools/no-deep-class-inheritance` rule. The default starting point for a new agent-maintained project - relax specific rules per-project rather than swapping this config out. | `typescript-eslint`, `eslint-plugin-jsdoc`, `typescript >=5.5.0 <6.1.0`, `parserOptions.projectService` |
60
+ | `testsConfig` | Relaxes size/complexity budgets (`max-lines`, `max-lines-per-function`, `max-statements`, `max-nested-callbacks`, unused-expression checks) for `*.test.*` / `*.spec.*` files. | combine with another config |
61
+ | `browserConfig` | `globalThis`-over-`window`/`document`/`location` restrictions, plus 3 cleanup rules (`require-listener-cleanup`, `require-observer-cleanup`, `require-timer-cleanup`). | none extra |
62
+ | `htmlConfig` | `@html-eslint` recommended structural rules for `*.html` files (duplicate ids/attrs, obsolete tags, `lang`/`alt` requirements, baseline feature checks), with pure-formatting rules off in favor of Prettier and frontmatter-aware parsing for static-site templates. | `@html-eslint/eslint-plugin`, `@html-eslint/parser` |
63
+ | `jsonConfig` | `@eslint/json` structural rules for `*.json` (JSONC variant for `tsconfig*.json`), plus `no-unpinned-dependency-ranges` scoped to `package.json`. | `@eslint/json` |
64
+
65
+ ### Rules
66
+
67
+ | Rule | What it catches | Enabled by |
68
+ | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------ |
69
+ | [`tools/no-dead-code`](./src/eslint/docs/rules/no-dead-code.md) | Disallow commented-out source code left behind in comments. | `typescript` |
70
+ | [`tools/no-deep-class-inheritance`](./src/eslint/docs/rules/no-deep-class-inheritance.md) | Disallow class inheritance chains deeper than a configured maximum. | `typescript` |
71
+ | [`tools/no-single-consumer-abstraction`](./src/eslint/docs/rules/no-single-consumer-abstraction.md) | Disallow exported abstractions (base classes) with fewer than two implementation consumers. | `typescript` |
72
+ | [`tools/no-unjustified-disable`](./src/eslint/docs/rules/no-unjustified-disable.md) | Require every `eslint-disable*` directive to carry a reason (and, by default, an explicit rule list). | `typescript` |
73
+ | [`tools/no-reexport-barrels`](./src/eslint/docs/rules/no-reexport-barrels.md) | Flag files that re-export from more than a configured number of modules. | `typescript` |
74
+ | [`tools/consistent-error-messages`](./src/eslint/docs/rules/consistent-error-messages.md) | Require thrown/constructed `Error`s to carry a non-empty, informative message. | `typescript` |
75
+ | [`tools/require-listener-cleanup`](./src/eslint/docs/rules/require-listener-cleanup.md) | Require a matching `removeEventListener` for every `addEventListener` added in a setup lifecycle method; forbid `addEventListener` in the constructor. | `browser` |
76
+ | [`tools/require-observer-cleanup`](./src/eslint/docs/rules/require-observer-cleanup.md) | Flag `ResizeObserver`/`MutationObserver`/`IntersectionObserver`/`PerformanceObserver` instances created without storing a reference. | `browser` |
77
+ | [`tools/require-timer-cleanup`](./src/eslint/docs/rules/require-timer-cleanup.md) | Flag `setInterval` handles that are never stored or never cleared. | `browser` |
78
+ | [`tools/no-unpinned-dependency-ranges`](./src/eslint/docs/rules/no-unpinned-dependency-ranges.md) | Require `package.json` dependency version specifiers appropriate to the package's publish status and dependency kind. | `json` |
79
+
80
+ ## Prettier
81
+
82
+ ```sh
83
+ pnpm add -D @coryrylan/tools prettier
84
+ ```
85
+
86
+ Reference the config straight from `package.json`:
87
+
88
+ ```json
89
+ { "prettier": "@coryrylan/tools/prettier" }
90
+ ```
91
+
92
+ Or import and spread it in a `prettier.config.js` to override options.
93
+ Embedded template literals stay hand-formatted
94
+ (`embeddedLanguageFormatting: 'off'`). See
95
+ [src/prettier/docs](./src/prettier/docs/index.md).
96
+
97
+ ## Stylelint
98
+
99
+ ```sh
100
+ pnpm add -D @coryrylan/tools stylelint stylelint-config-standard
101
+ ```
102
+
103
+ ```js
104
+ // stylelint.config.js
105
+ export { default } from '@coryrylan/tools/stylelint';
106
+ ```
107
+
108
+ Layers `stylelint-config-standard` with logical-property enforcement (no
109
+ physical `margin-*`/`padding-*`) and design-token checks (no hardcoded pixel
110
+ values in spacing properties). See
111
+ [src/stylelint/docs](./src/stylelint/docs/index.md).
112
+
113
+ ## Vale
114
+
115
+ Install the Vale binary (`mise use vale`, or brew), then point a `.vale.ini`
116
+ at the styles this package ships:
117
+
118
+ ```ini
119
+ StylesPath = node_modules/@coryrylan/tools/dist/vale/styles
120
+ Vocab = Tools
121
+ Packages = Google, write-good
122
+ ```
123
+
124
+ Run `vale sync` once to download the style packages. The full template with
125
+ per-filetype sections lives at [src/vale/vale.ini](./src/vale/vale.ini). See
126
+ [src/vale/docs](./src/vale/docs/index.md).
127
+
128
+ ## Vite
129
+
130
+ ```sh
131
+ pnpm add -D @coryrylan/tools vite
132
+ ```
133
+
134
+ ```ts
135
+ // vite.config.ts
136
+ import { defineConfig, mergeConfig } from 'vite';
137
+ import { createNodeLibraryBuildConfig } from '@coryrylan/tools/vite';
138
+
139
+ export default defineConfig(mergeConfig(createNodeLibraryBuildConfig({ entry: { index: 'src/index.ts' } }), {}));
140
+ ```
141
+
142
+ ESM-only, every bare specifier externalized, `preserveModules` for deep
143
+ imports, no minification. The `write-if-changed` and `dts` plugins ship as
144
+ deep imports under `@coryrylan/tools/vite/plugins/`. See
145
+ [src/vite/docs](./src/vite/docs/index.md).
146
+
147
+ ## Vitest
148
+
149
+ ```sh
150
+ pnpm add -D @coryrylan/tools vitest
151
+ ```
152
+
153
+ ```ts
154
+ // vitest.config.ts
155
+ import { defineConfig } from 'vitest/config';
156
+ import { nodeTestConfig } from '@coryrylan/tools/vitest';
157
+
158
+ export default defineConfig(nodeTestConfig);
159
+ ```
160
+
161
+ `@coryrylan/tools/vitest/browser` exports `browserTestConfig` for real-Chromium
162
+ browser-mode tests (requires the optional `@vitest/browser-playwright`,
163
+ `@vitest/coverage-istanbul`, and `playwright` peers) with istanbul coverage
164
+ gated at 90% across lines, branches, functions, and statements. See
165
+ [src/vitest/docs](./src/vitest/docs/index.md).
166
+
167
+ ## Peer dependencies
168
+
169
+ Every peer beyond `eslint` is scoped to the surface that needs it; the
170
+ non-ESLint peers are marked optional, so install them with the surface you
171
+ use.
172
+
173
+ - `typescript` must satisfy `>=5.5.0 <6.1.0` for type-aware linting
174
+ (`typescriptConfig`) - this is the range `typescript-eslint`
175
+ supports; newer `typescript` majors are not yet compatible with type-aware
176
+ rules here. Note that a bare `pnpm add -D typescript` resolves to 7.x
177
+ (the native compiler, which no longer ships the JS compiler API
178
+ `typescript-eslint` needs), so install an explicit `typescript@6` until
179
+ `typescript-eslint` supports 7.
180
+ - `typescript-eslint >=8.40.0` and `eslint-plugin-jsdoc >=60.0.0` are only
181
+ needed if you use `typescriptConfig`.
182
+ - `@eslint/json >=0.13.0` is only needed if you use `jsonConfig`.
183
+ - `@html-eslint/eslint-plugin >=0.61.0` and `@html-eslint/parser >=0.61.0` are
184
+ only needed if you use `htmlConfig`.
185
+ - `prettier >=3` for the Prettier config; `stylelint >=17` and
186
+ `stylelint-config-standard >=40` for the Stylelint config.
187
+ - `vite >=8` for the Vite factories and plugins; `vitest >=4` for the test
188
+ presets, plus `@vitest/browser-playwright >=4`, `@vitest/coverage-istanbul >=4`,
189
+ and `playwright >=1.50` for `browserTestConfig`.
190
+
191
+ ## License
192
+
193
+ MIT - see [LICENSE](./LICENSE). Copyright (c) 2026 Cory Rylan.
@@ -0,0 +1,12 @@
1
+ import type { Linter } from 'eslint';
2
+ /**
3
+ * Flags direct references to browser globals that silently assume a
4
+ * `window`-backed environment. Agents reach for `window`/`document` out of
5
+ * habit; the `globalThis`-qualified form works in browser, SSR, and worker
6
+ * contexts alike, so there is nothing to rewrite later.
7
+ *
8
+ * Also wires in the `tools/*` cleanup rules - listener, observer, and timer
9
+ * teardown are all browser/DOM-lifecycle concerns, so they live alongside
10
+ * the SSR-safety check rather than in `typescriptConfig`.
11
+ */
12
+ export declare const browserConfig: Linter.Config[];
@@ -0,0 +1,40 @@
1
+ import { plugin } from "../plugin.js";
2
+ import { globalIgnores, jsTsFiles } from "./shared.js";
3
+ //#region src/eslint/configs/browser.ts
4
+ var ssrSafeMessage = (globalName) => `Use ${globalName} instead of the bare global so this code stays safe to run outside a full browser window (SSR, workers, tests).`;
5
+ /**
6
+ * Flags direct references to browser globals that silently assume a
7
+ * `window`-backed environment. Agents reach for `window`/`document` out of
8
+ * habit; the `globalThis`-qualified form works in browser, SSR, and worker
9
+ * contexts alike, so there is nothing to rewrite later.
10
+ *
11
+ * Also wires in the `tools/*` cleanup rules - listener, observer, and timer
12
+ * teardown are all browser/DOM-lifecycle concerns, so they live alongside
13
+ * the SSR-safety check rather than in `typescriptConfig`.
14
+ */
15
+ var browserConfig = [globalIgnores, {
16
+ files: jsTsFiles,
17
+ plugins: { tools: plugin },
18
+ rules: {
19
+ "no-restricted-globals": [
20
+ "error",
21
+ {
22
+ name: "window",
23
+ message: ssrSafeMessage("globalThis")
24
+ },
25
+ {
26
+ name: "location",
27
+ message: ssrSafeMessage("globalThis.location")
28
+ },
29
+ {
30
+ name: "document",
31
+ message: ssrSafeMessage("globalThis.document")
32
+ }
33
+ ],
34
+ "tools/require-listener-cleanup": "error",
35
+ "tools/require-observer-cleanup": "error",
36
+ "tools/require-timer-cleanup": "error"
37
+ }
38
+ }];
39
+ //#endregion
40
+ export { browserConfig };
@@ -0,0 +1,10 @@
1
+ import type { Linter } from 'eslint';
2
+ /**
3
+ * Structural and correctness rules for HTML files: `@html-eslint`
4
+ * recommended (duplicate ids/attrs, obsolete tags, `lang`/`alt`
5
+ * requirements, baseline feature checks, ...) with the pure-formatting
6
+ * rules turned off - Prettier owns formatting. `frontmatter: true` lets
7
+ * static-site templates (11ty and similar) carry YAML frontmatter without
8
+ * tripping the parser.
9
+ */
10
+ export declare const htmlConfig: Linter.Config[];
@@ -0,0 +1,38 @@
1
+ import { globalIgnores } from "./shared.js";
2
+ import html from "@html-eslint/eslint-plugin";
3
+ import htmlParser from "@html-eslint/parser";
4
+ //#region src/eslint/configs/html.ts
5
+ /**
6
+ * Structural and correctness rules for HTML files: `@html-eslint`
7
+ * recommended (duplicate ids/attrs, obsolete tags, `lang`/`alt`
8
+ * requirements, baseline feature checks, ...) with the pure-formatting
9
+ * rules turned off - Prettier owns formatting. `frontmatter: true` lets
10
+ * static-site templates (11ty and similar) carry YAML frontmatter without
11
+ * tripping the parser.
12
+ */
13
+ var htmlConfig = [globalIgnores, {
14
+ files: ["**/*.html"],
15
+ languageOptions: {
16
+ parser: htmlParser,
17
+ parserOptions: { frontmatter: true }
18
+ },
19
+ plugins: { html },
20
+ rules: {
21
+ ...html.configs.recommended.rules,
22
+ "html/require-doctype": "off",
23
+ "html/require-title": "off",
24
+ "html/no-extra-spacing-text": "error",
25
+ "html/quotes": "off",
26
+ "html/no-extra-spacing-attrs": "off",
27
+ "html/indent": "off",
28
+ "html/require-closing-tags": "off",
29
+ "html/element-newline": "off",
30
+ "html/no-extra-spacing-tags": "off",
31
+ "html/attrs-newline": ["error", {
32
+ closeStyle: "sameline",
33
+ ifAttrsMoreThan: 10
34
+ }]
35
+ }
36
+ }];
37
+ //#endregion
38
+ export { htmlConfig };
@@ -0,0 +1,13 @@
1
+ import type { Linter } from 'eslint';
2
+ /**
3
+ * Structural correctness for JSON files (duplicate keys, unsafe numeric
4
+ * values, ...) - the same class of "syntactically valid but semantically
5
+ * broken" failure the JS/TS configs guard against, just for manifests and
6
+ * config files. `tsconfig*.json` gets the JSONC variant since it commonly
7
+ * carries comments.
8
+ *
9
+ * `package.json` additionally gets the `tools/no-unpinned-dependency-ranges`
10
+ * check, scoped separately since it only applies to that one filename, not
11
+ * JSON generally.
12
+ */
13
+ export declare const jsonConfig: Linter.Config[];
@@ -0,0 +1,39 @@
1
+ import { plugin } from "../plugin.js";
2
+ import { globalIgnores } from "./shared.js";
3
+ import json from "@eslint/json";
4
+ //#region src/eslint/configs/json.ts
5
+ /**
6
+ * Structural correctness for JSON files (duplicate keys, unsafe numeric
7
+ * values, ...) - the same class of "syntactically valid but semantically
8
+ * broken" failure the JS/TS configs guard against, just for manifests and
9
+ * config files. `tsconfig*.json` gets the JSONC variant since it commonly
10
+ * carries comments.
11
+ *
12
+ * `package.json` additionally gets the `tools/no-unpinned-dependency-ranges`
13
+ * check, scoped separately since it only applies to that one filename, not
14
+ * JSON generally.
15
+ */
16
+ var jsonConfig = [
17
+ globalIgnores,
18
+ {
19
+ files: ["**/*.json"],
20
+ ignores: ["**/tsconfig*.json", "**/tsconfig.*.json"],
21
+ language: "json/json",
22
+ plugins: { json },
23
+ rules: { ...json.configs.recommended.rules }
24
+ },
25
+ {
26
+ files: ["**/tsconfig*.json", "**/tsconfig.*.json"],
27
+ language: "json/jsonc",
28
+ plugins: { json },
29
+ rules: { ...json.configs.recommended.rules }
30
+ },
31
+ {
32
+ files: ["**/package.json"],
33
+ language: "json/json",
34
+ plugins: { tools: plugin },
35
+ rules: { "tools/no-unpinned-dependency-ranges": "error" }
36
+ }
37
+ ];
38
+ //#endregion
39
+ export { jsonConfig };
@@ -0,0 +1,11 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare const jsTsFiles: string[];
3
+ export declare const tsFiles: string[];
4
+ /**
5
+ * A standalone `ignores`-only config entry, meant to be the first element of
6
+ * every exported config array. ESLint only treats an `ignores` entry as a
7
+ * *global* ignore when no other keys (`files`, `rules`, ...) share the same
8
+ * object - merging them in turns it into a file-scoped ignore instead of a
9
+ * global one. https://github.com/eslint/eslint/discussions/18304
10
+ */
11
+ export declare const globalIgnores: Linter.Config;
@@ -0,0 +1,19 @@
1
+ //#region src/eslint/configs/shared.ts
2
+ var jsTsFiles = ["**/*.{js,ts}"];
3
+ var tsFiles = ["**/*.ts"];
4
+ /**
5
+ * A standalone `ignores`-only config entry, meant to be the first element of
6
+ * every exported config array. ESLint only treats an `ignores` entry as a
7
+ * *global* ignore when no other keys (`files`, `rules`, ...) share the same
8
+ * object - merging them in turns it into a file-scoped ignore instead of a
9
+ * global one. https://github.com/eslint/eslint/discussions/18304
10
+ */
11
+ var globalIgnores = { ignores: [
12
+ "**/node_modules/**",
13
+ "**/dist/**",
14
+ "**/build/**",
15
+ "**/coverage/**",
16
+ "**/.wireit/**"
17
+ ] };
18
+ //#endregion
19
+ export { globalIgnores, jsTsFiles, tsFiles };
@@ -0,0 +1,8 @@
1
+ import type { Linter } from 'eslint';
2
+ /**
3
+ * Relaxes size/complexity limits that make sense for source files but are
4
+ * routinely and legitimately exceeded by thorough tests (long `describe`
5
+ * blocks, many assertions, `expect(x).to.be.true` style unused-expression
6
+ * patterns).
7
+ */
8
+ export declare const testsConfig: Linter.Config[];
@@ -0,0 +1,20 @@
1
+ import { globalIgnores } from "./shared.js";
2
+ /**
3
+ * Relaxes size/complexity limits that make sense for source files but are
4
+ * routinely and legitimately exceeded by thorough tests (long `describe`
5
+ * blocks, many assertions, `expect(x).to.be.true` style unused-expression
6
+ * patterns).
7
+ */
8
+ var testsConfig = [globalIgnores, {
9
+ files: ["**/*.test.*", "**/*.spec.*"],
10
+ rules: {
11
+ "max-lines": "off",
12
+ "max-lines-per-function": "off",
13
+ "max-statements": "off",
14
+ "max-nested-callbacks": "off",
15
+ "no-unused-expressions": "off",
16
+ "@typescript-eslint/no-unused-expressions": "off"
17
+ }
18
+ }];
19
+ //#endregion
20
+ export { testsConfig };
@@ -0,0 +1,18 @@
1
+ import type { Linter } from 'eslint';
2
+ /**
3
+ * The full, no-exceptions config, in two entries. The first is non-type-aware
4
+ * and applies to all JS/TS files: `@eslint/js` recommended plus size and
5
+ * complexity limits that keep any single generated file or function within a
6
+ * range a reviewer (human or agent) can actually hold in their head, and the
7
+ * syntax-only `tools/*` rules. The second is type-aware and scoped to TS
8
+ * files: `typescript-eslint` `strictTypeChecked`, JSDoc checks, and the
9
+ * `tools/*` rules that need type information - currently just
10
+ * `no-deep-class-inheritance`, which resolves each superclass's own
11
+ * declaration via the type checker. It requires
12
+ * `parserOptions.projectService` to resolve type information, which makes it
13
+ * comparatively slow (e.g. pre-commit, CI).
14
+ *
15
+ * Meant as the default for new agent-maintained projects - relax specific
16
+ * rules per-project rather than swapping this config out wholesale.
17
+ */
18
+ export declare const typescriptConfig: Linter.Config[];
@@ -0,0 +1,104 @@
1
+ import { plugin } from "../plugin.js";
2
+ import { globalIgnores, jsTsFiles, tsFiles } from "./shared.js";
3
+ import js from "@eslint/js";
4
+ import tseslint from "typescript-eslint";
5
+ import jsdoc from "eslint-plugin-jsdoc";
6
+ //#region src/eslint/configs/typescript.ts
7
+ /**
8
+ * Flattens `strictTypeChecked`'s config array (base + eslint-recommended +
9
+ * strict-type-checked) into a single rules record, the same way a project
10
+ * would when folding it into one flat-config entry alongside custom rules.
11
+ */
12
+ var strictTypeCheckedRules = tseslint.configs.strictTypeChecked.reduce((rules, config) => ({
13
+ ...rules,
14
+ ...config.rules
15
+ }), {});
16
+ /**
17
+ * The full, no-exceptions config, in two entries. The first is non-type-aware
18
+ * and applies to all JS/TS files: `@eslint/js` recommended plus size and
19
+ * complexity limits that keep any single generated file or function within a
20
+ * range a reviewer (human or agent) can actually hold in their head, and the
21
+ * syntax-only `tools/*` rules. The second is type-aware and scoped to TS
22
+ * files: `typescript-eslint` `strictTypeChecked`, JSDoc checks, and the
23
+ * `tools/*` rules that need type information - currently just
24
+ * `no-deep-class-inheritance`, which resolves each superclass's own
25
+ * declaration via the type checker. It requires
26
+ * `parserOptions.projectService` to resolve type information, which makes it
27
+ * comparatively slow (e.g. pre-commit, CI).
28
+ *
29
+ * Meant as the default for new agent-maintained projects - relax specific
30
+ * rules per-project rather than swapping this config out wholesale.
31
+ */
32
+ var typescriptConfig = [
33
+ globalIgnores,
34
+ {
35
+ files: jsTsFiles,
36
+ plugins: { tools: plugin },
37
+ rules: {
38
+ ...js.configs.recommended.rules,
39
+ complexity: ["error", { max: 8 }],
40
+ "max-depth": ["error", 3],
41
+ "max-params": ["error", 3],
42
+ "max-lines": ["error", 500],
43
+ "max-statements": ["error", 15],
44
+ "max-lines-per-function": ["error", 50],
45
+ "max-nested-callbacks": ["error", 3],
46
+ "max-statements-per-line": ["error", { max: 1 }],
47
+ eqeqeq: "error",
48
+ "prefer-const": "error",
49
+ "no-param-reassign": "error",
50
+ "no-useless-return": "error",
51
+ "no-useless-catch": "error",
52
+ "no-warning-comments": "error",
53
+ "no-shadow": "error",
54
+ "no-implicit-globals": "error",
55
+ "no-restricted-imports": ["error", { patterns: ["**/dist/**", "**/node_modules/**"] }],
56
+ "id-length": ["error", {
57
+ min: 2,
58
+ exceptions: ["_"]
59
+ }],
60
+ "tools/no-dead-code": "error",
61
+ "tools/no-single-consumer-abstraction": "error",
62
+ "tools/no-unjustified-disable": "error",
63
+ "tools/no-reexport-barrels": "error",
64
+ "tools/consistent-error-messages": "error"
65
+ }
66
+ },
67
+ {
68
+ files: tsFiles,
69
+ plugins: {
70
+ "@typescript-eslint": tseslint.plugin,
71
+ jsdoc,
72
+ tools: plugin
73
+ },
74
+ languageOptions: {
75
+ parser: tseslint.parser,
76
+ parserOptions: { projectService: true }
77
+ },
78
+ rules: {
79
+ ...strictTypeCheckedRules,
80
+ "tools/no-deep-class-inheritance": "error",
81
+ "no-shadow": "off",
82
+ "@typescript-eslint/no-shadow": "error",
83
+ "@typescript-eslint/no-explicit-any": "error",
84
+ "@typescript-eslint/no-floating-promises": "error",
85
+ "@typescript-eslint/only-throw-error": "error",
86
+ "@typescript-eslint/consistent-type-imports": "error",
87
+ "@typescript-eslint/switch-exhaustiveness-check": "error",
88
+ "@typescript-eslint/explicit-member-accessibility": ["error", { accessibility: "no-public" }],
89
+ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
90
+ "@typescript-eslint/prefer-readonly": "error",
91
+ "@typescript-eslint/explicit-module-boundary-types": "error",
92
+ "no-restricted-syntax": ["error", {
93
+ selector: ":matches(PropertyDefinition, MethodDefinition)[accessibility=\"private\"]:not(:has(Decorator)):not([kind=\"constructor\"])",
94
+ message: "Use #private instead of the `private` keyword - it is enforced at runtime, not just by the type checker."
95
+ }],
96
+ "jsdoc/no-types": "error",
97
+ "jsdoc/valid-types": "error",
98
+ "jsdoc/check-tag-names": "error",
99
+ "jsdoc/informative-docs": "error"
100
+ }
101
+ }
102
+ ];
103
+ //#endregion
104
+ export { typescriptConfig };
@@ -0,0 +1,27 @@
1
+ import type { ESLint, Linter } from 'eslint';
2
+ import { plugin } from './plugin.js';
3
+ export { typescriptConfig } from './configs/typescript.js';
4
+ export { testsConfig } from './configs/tests.js';
5
+ export { htmlConfig } from './configs/html.js';
6
+ export { jsonConfig } from './configs/json.js';
7
+ export { browserConfig } from './configs/browser.js';
8
+ /**
9
+ * The `tools/*` rule registry every config below wires in under the
10
+ * `tools` namespace (alongside `@typescript-eslint`/`jsdoc`/etc.). Defined
11
+ * in `./plugin.js` - a separate module - so the configs can import the same
12
+ * plugin instance directly without a circular import through this file.
13
+ */
14
+ export { plugin };
15
+ interface ConfigsMap {
16
+ readonly typescript: Linter.Config[];
17
+ readonly tests: Linter.Config[];
18
+ readonly html: Linter.Config[];
19
+ readonly json: Linter.Config[];
20
+ readonly browser: Linter.Config[];
21
+ }
22
+ interface AgentLintRules {
23
+ readonly plugin: ESLint.Plugin;
24
+ readonly configs: ConfigsMap;
25
+ }
26
+ declare const agentLintRules: AgentLintRules;
27
+ export default agentLintRules;
@@ -0,0 +1,19 @@
1
+ import { plugin } from "./plugin.js";
2
+ import { typescriptConfig } from "./configs/typescript.js";
3
+ import { testsConfig } from "./configs/tests.js";
4
+ import { htmlConfig } from "./configs/html.js";
5
+ import { jsonConfig } from "./configs/json.js";
6
+ import { browserConfig } from "./configs/browser.js";
7
+ //#region src/eslint/index.ts
8
+ var agentLintRules = {
9
+ plugin,
10
+ configs: {
11
+ typescript: typescriptConfig,
12
+ tests: testsConfig,
13
+ html: htmlConfig,
14
+ json: jsonConfig,
15
+ browser: browserConfig
16
+ }
17
+ };
18
+ //#endregion
19
+ export { browserConfig, agentLintRules as default, htmlConfig, jsonConfig, plugin, testsConfig, typescriptConfig };
@@ -0,0 +1,8 @@
1
+ import type { ESLint } from 'eslint';
2
+ /**
3
+ * The `tools/*` rule registry, shared by every config in `./configs/*.ts`
4
+ * under the `tools` namespace. Lives in its own module (rather than
5
+ * `index.ts`, where it was previously declared empty) so the configs can
6
+ * import it directly without a circular import back through `index.ts`.
7
+ */
8
+ export declare const plugin: ESLint.Plugin;