56 lines
2.5 KiB
Markdown
56 lines
2.5 KiB
Markdown
---
|
|
description: scripts/project-lint.ts is the repo-local plugin "project" holding the repo's rule list (typed DummyRuleMap) and repo-only rules; never published, no tests
|
|
---
|
|
|
|
Every repo has `scripts/project-lint.ts`, copied from
|
|
`cunarist/eslint-plugin-explicit-types` and extended. It is a repo-local
|
|
Oxlint plugin with `meta.name` `"project"`, loaded through `jsPlugins` in
|
|
`vite.config.ts`. It holds:
|
|
|
|
- **`configs.project.rules`**: the repo's whole rule list beyond the published
|
|
plugin configs: Deno parity rules, extras, per-rule options, and opt-outs,
|
|
built-in Oxlint ids included (`curly`, `id-match`, `import/no-cycle`, ...).
|
|
- **Custom rules**, enabled as `project/<rule>`. Every repo has
|
|
`no-disable-comments` and `no-non-ts-imports`. Apps also port the rules of
|
|
their old `scripts/deno-lint-plugin.ts` here under the same names and
|
|
behavior (`no-self-sync`, `app-module-conventions`, `restricted-types`, ...).
|
|
|
|
Rules and plugin stay in this one file; don't add `scripts/rules/`.
|
|
|
|
```ts
|
|
import type { DummyRuleMap } from "vite-plus/lint";
|
|
|
|
const rules: DummyRuleMap = {
|
|
"id-match": ["error", "^_*[^_]*_*$|^[^a-z]*$", {/* ... */}],
|
|
curly: "error",
|
|
"project/no-disable-comments": "error",
|
|
};
|
|
|
|
const plugin = {
|
|
meta: { name: "project" },
|
|
rules: { "no-disable-comments": noDisableCommentsRule /* ... */ },
|
|
configs: { project: { rules } },
|
|
};
|
|
export default plugin;
|
|
```
|
|
|
|
## Why `DummyRuleMap` and an unannotated plugin (decided 2026-10-15)
|
|
|
|
- `DummyRuleMap` (re-exported by `vite-plus/lint`) is Oxlint's type for
|
|
`OxlintConfig.rules`. Despite the name it lists every built-in rule with its
|
|
options, so a typo in a rule id or option is a type error, not just a CI
|
|
failure. Without it, TypeScript widens `"error"` to `string`; `as const`
|
|
doesn't help because Oxlint wants mutable tuples.
|
|
- `@eslint/core`'s `Plugin` is too loose to read back: `configs` is optional,
|
|
any key may be missing, and a config may be an array, so
|
|
`projectLint.configs.project.rules` fails in `vite.config.ts` (and `!` is
|
|
banned). Optional chaining still leaves the array case.
|
|
- Oxlint's own `Plugin` type (`vite-plus/lint/plugins`) has no `configs`.
|
|
- Two hand-written interfaces worked but were dropped as noise; a plain
|
|
config object was rejected because custom rules need the plugin form anyway
|
|
and every repo should look the same.
|
|
|
|
`scripts/` is tooling, not production code: never packed or published, and
|
|
no tests. Narrow `overrides` for `scripts/**` are allowed when a script truly
|
|
needs one (e.g. `no-console`); see [[lint-overrides-policy]].
|