@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.
- package/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/eslint/configs/browser.d.ts +12 -0
- package/dist/eslint/configs/browser.js +40 -0
- package/dist/eslint/configs/html.d.ts +10 -0
- package/dist/eslint/configs/html.js +38 -0
- package/dist/eslint/configs/json.d.ts +13 -0
- package/dist/eslint/configs/json.js +39 -0
- package/dist/eslint/configs/shared.d.ts +11 -0
- package/dist/eslint/configs/shared.js +19 -0
- package/dist/eslint/configs/tests.d.ts +8 -0
- package/dist/eslint/configs/tests.js +20 -0
- package/dist/eslint/configs/typescript.d.ts +18 -0
- package/dist/eslint/configs/typescript.js +104 -0
- package/dist/eslint/index.d.ts +27 -0
- package/dist/eslint/index.js +19 -0
- package/dist/eslint/plugin.d.ts +8 -0
- package/dist/eslint/plugin.js +36 -0
- package/dist/eslint/rules/consistent-error-messages.d.ts +10 -0
- package/dist/eslint/rules/consistent-error-messages.js +127 -0
- package/dist/eslint/rules/no-dead-code.d.ts +16 -0
- package/dist/eslint/rules/no-dead-code.js +110 -0
- package/dist/eslint/rules/no-deep-class-inheritance.d.ts +19 -0
- package/dist/eslint/rules/no-deep-class-inheritance.js +132 -0
- package/dist/eslint/rules/no-reexport-barrels.d.ts +10 -0
- package/dist/eslint/rules/no-reexport-barrels.js +55 -0
- package/dist/eslint/rules/no-single-consumer-abstraction.d.ts +3 -0
- package/dist/eslint/rules/no-single-consumer-abstraction.js +364 -0
- package/dist/eslint/rules/no-unjustified-disable.d.ts +11 -0
- package/dist/eslint/rules/no-unjustified-disable.js +75 -0
- package/dist/eslint/rules/no-unpinned-dependency-ranges.d.ts +46 -0
- package/dist/eslint/rules/no-unpinned-dependency-ranges.js +79 -0
- package/dist/eslint/rules/require-listener-cleanup.d.ts +8 -0
- package/dist/eslint/rules/require-listener-cleanup.js +202 -0
- package/dist/eslint/rules/require-observer-cleanup.d.ts +8 -0
- package/dist/eslint/rules/require-observer-cleanup.js +69 -0
- package/dist/eslint/rules/require-timer-cleanup.d.ts +10 -0
- package/dist/eslint/rules/require-timer-cleanup.js +142 -0
- package/dist/eslint/rules/utils.d.ts +45 -0
- package/dist/eslint/rules/utils.js +109 -0
- package/dist/prettier/index.d.ts +15 -0
- package/dist/prettier/index.js +26 -0
- package/dist/stylelint/index.d.ts +10 -0
- package/dist/stylelint/index.js +50 -0
- package/dist/vale/styles/config/vocabularies/Tools/accept.txt +80 -0
- package/dist/vale/styles/config/vocabularies/Tools/reject.txt +5 -0
- package/dist/vale/vale.ini +27 -0
- package/dist/vite/index.d.ts +37 -0
- package/dist/vite/index.js +61 -0
- package/dist/vite/plugins/dts.d.ts +26 -0
- package/dist/vite/plugins/dts.js +106 -0
- package/dist/vite/plugins/write-if-changed.d.ts +20 -0
- package/dist/vite/plugins/write-if-changed.js +33 -0
- package/dist/vitest/browser.d.ts +31 -0
- package/dist/vitest/browser.js +105 -0
- package/dist/vitest/index.d.ts +28 -0
- package/dist/vitest/index.js +34 -0
- 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;
|