Compare commits
3 Commits
2e09945a6a
...
15fe481181
| Author | SHA1 | Date | |
|---|---|---|---|
| 15fe481181 | |||
| 662e946fad | |||
| ffb7cb9a75 |
@@ -12,7 +12,9 @@ Node + Vite+ migration (PR #20, `node-migration`, 2026-10-15/16):
|
||||
consumer (`uses: .../commons/bot-agents@<branch>`) on a throwaway PR.
|
||||
- **Profiles are JSON.** `profiles.yml` became `profiles.json` with the same
|
||||
content (encrypted `v1:` values unchanged), read with `JSON.parse`. That
|
||||
removed the only runtime dependency (`@std/yaml`); keep it dependency-free.
|
||||
removed the only runtime dependency (`@std/yaml`); keep it free of runtime
|
||||
dependencies (`@eslint/core` and the other devDependencies serve only the
|
||||
tooling).
|
||||
- **Node 24.2+**, not 22: `src/post.ts` uses `import.meta.main`. `engines.node`
|
||||
is `>=24.2`, which the user accepted for this repo
|
||||
([[bot-agents-runtime-no-build]]).
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
---
|
||||
description: bot-agents lint has prefer-readonly-parameter-types off (Node/web types can't satisfy it) and one scripts/** override for no-unsafe-enum-comparison; nothing else
|
||||
description: bot-agents lint has prefer-readonly-parameter-types off (Node/web types can't satisfy it) and no overrides; nothing else
|
||||
---
|
||||
|
||||
The repo follows [[lint-config-shape]] with two recorded exceptions, both
|
||||
present when the migration PR was approved:
|
||||
The repo follows [[lint-config-shape]] with one recorded exception, present
|
||||
when the migration PR was approved:
|
||||
|
||||
- `typescript/prefer-readonly-parameter-types` is `"off"` in
|
||||
`scripts/project-lint.ts`, as in the explicit-types template, because
|
||||
Node/web types (`Response`, `Stats`, `RequestInit`) can never satisfy it.
|
||||
- `vite.config.ts` has one `overrides` entry, for `scripts/**` only:
|
||||
`typescript/no-unsafe-enum-comparison` off, because the lint plugin compares
|
||||
AST node `type`s, which the AST types declare as an enum. This is the
|
||||
tooling exception [[lint-overrides-policy]] allows.
|
||||
- `vite.config.ts` has no `overrides`. The `scripts/**` override for
|
||||
`typescript/no-unsafe-enum-comparison` was removed (2026-10-16):
|
||||
`scripts/project-lint.ts` compares node types with `AST_NODE_TYPES` members
|
||||
from `@typescript-eslint/types` instead of string literals
|
||||
([[lint-overrides-policy]]).
|
||||
|
||||
Oxlint's default size limits apply to `src/`; the migration split `run.ts`
|
||||
into `run-context.ts`, `agents.ts`, and `agent-{claude,codex,opencode}.ts`,
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
description: Every repo moved from Deno to Node + Vite+ in Oct 2026 because Cloudflare bought Deno; nothing goes back to Deno
|
||||
---
|
||||
|
||||
On 2026-10-12 the user moved every project from Deno to Node.js + Vite+
|
||||
(`vp`). Cloudflare bought Deno on 2026-10-09; the runtime gets roughly 12
|
||||
months of maintenance and Deno Deploy shuts down around April 2027. None of the
|
||||
repos used Deploy or KV, so the move is about the toolchain, not hosting.
|
||||
|
||||
Do not reintroduce `deno.json`, `deno.lock`, `Deno.*` APIs, `jsr:` or `npm:`
|
||||
specifiers, `mod.ts` entries, or `deno task` anywhere, docs and scripts
|
||||
included.
|
||||
|
||||
## Order the migration followed
|
||||
|
||||
1. The custom Deno lint plugins were ported to npm first, as
|
||||
`@cunarist/eslint-plugin-explicit-types`, `-import-check`, and `-lit`.
|
||||
`cunarist/eslint-plugin-explicit-types` is the template every other repo
|
||||
copies (config shape, `scripts/project-lint.ts`, AGENTS.md, notes).
|
||||
2. Then `commons/bot-agents` (six repos use it as an action), then the apps:
|
||||
home, eximium, stockvibe, brain, memona, memona-home.
|
||||
3. deno-practice, interview-practice, web-landing, and vemium were left out
|
||||
(archivable). Old `cunarist/deno-*` repos are archived one at a time, and
|
||||
only after their `eslint-plugin-*` replacement is published to npm.
|
||||
|
||||
## What "migrated" means
|
||||
|
||||
- Same behavior. A migration PR changes tooling, not features.
|
||||
- Lint coverage is at least what Deno enforced: [[lint-parity-never-shrinks]].
|
||||
- Only three config files and only `.ts`: [[toolchain-node-vite-plus]],
|
||||
[[ts-only-files-and-imports]].
|
||||
- Deno.test suites became Vitest: [[tests-layout-vitest]].
|
||||
|
||||
The custom plugins' Deno names map like this:
|
||||
|
||||
| Deno | npm |
|
||||
| ----------------------------------------------- | ---------------------------------------- |
|
||||
| `jsr:@cunarist/deno-lint-plugin-explicit-types` | `@cunarist/eslint-plugin-explicit-types` |
|
||||
| `jsr:@cunarist/deno-import-check` (`./lint`) | `@cunarist/eslint-plugin-import-check` |
|
||||
| `jsr:@cunarist/deno-lint-plugin-lit` | `@cunarist/eslint-plugin-lit` |
|
||||
| `scripts/deno-lint-plugin.ts` (per app) | `scripts/project-lint.ts` (per app) |
|
||||
|
||||
Until those packages are on npm, a repo that depends on them fails `npm ci` in
|
||||
CI; that is expected and stated in the PR, never worked around by committing
|
||||
tarballs or `file:` dependencies.
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: Shared code comes through # aliases in package.json imports, each directory's entry is index.ts, barrels keep imports few; never ../ out of a directory
|
||||
description: Shared code comes through # aliases in package.json imports, each src/ subfolder's entry is index.ts, barrels keep imports few; never ../ out of a directory
|
||||
---
|
||||
|
||||
## `#` aliases
|
||||
@@ -44,8 +44,9 @@ so they never reach package consumers.
|
||||
|
||||
## `index.ts` entries and barrels
|
||||
|
||||
- Every directory's entry file is `index.ts`, never `mod.ts` (decided
|
||||
2026-10-12; import-check's `enforce-mod-file` now requires `index.ts`).
|
||||
- Every `src/` subfolder's entry file is `index.ts`, never `mod.ts` (decided
|
||||
2026-10-12; import-check's `enforce-mod-file` requires it only in folders a
|
||||
`#` alias owns). Folders outside `src/`, such as `scripts/`, have none.
|
||||
- A directory used from outside exposes its API through its `index.ts`
|
||||
barrel, and callers import the barrel (`#ui`), not files inside it
|
||||
(`no-barrel-bypass`).
|
||||
|
||||
@@ -22,11 +22,10 @@ lint: {
|
||||
- **Default limits stay** (2026-10-16): pedantic's `max-lines` 300,
|
||||
`max-lines-per-function` 50, `max-classes-per-file` 1,
|
||||
`max-dependencies` 10, `max-params` 3, `complexity` 20. Don't raise them to
|
||||
make code pass; refactor (see [[lint-overrides-policy]]). Apps follow this
|
||||
strictly. The plugin template already turns off `max-lines`,
|
||||
`max-lines-per-function`, `typescript/prefer-readonly-parameter-types`, and
|
||||
`typescript/no-unsafe-enum-comparison` because AST visitors fight them; do
|
||||
not add new opt-outs anywhere without asking the user.
|
||||
make code pass; refactor (see [[lint-overrides-policy]]). `max-lines` stays
|
||||
at 300 everywhere, `scripts/` included, with no override (2026-10-16): split
|
||||
a long file by theme instead. Do not add new opt-outs anywhere without
|
||||
asking the user.
|
||||
- **`vite.config.ts` names no rule.** Every rule id, built-in ones included,
|
||||
lives in `configs.project` of `scripts/project-lint.ts`
|
||||
([[project-lint-plugin]]). `vite.config.ts` only spreads configs.
|
||||
|
||||
@@ -29,9 +29,10 @@ registerHooks({
|
||||
```
|
||||
|
||||
- Node runs the `.ts` script directly (type stripping), so no build step.
|
||||
- **Gotcha:** the obvious `await import("lit-analyzer/cli.js")` fails
|
||||
`project/no-non-ts-imports` (a `.js` subpath). Import the package entry, or
|
||||
spawn the package's bin with `node:child_process`, instead.
|
||||
- Import the package entry, or spawn the package's bin with
|
||||
`node:child_process`, rather than `await import("lit-analyzer/cli.js")`.
|
||||
`project/no-asset-imports` allows `.js` subpaths, but the CLI file runs on
|
||||
import and bypasses the package's public entry.
|
||||
- CI runs `npm run lit-check` as its own step.
|
||||
- It is tooling under `scripts/`: no tests; a narrow `overrides` entry for it
|
||||
(e.g. `no-console`) is allowed if truly needed.
|
||||
|
||||
@@ -1,32 +1,13 @@
|
||||
---
|
||||
description: package.json key order is metadata, scripts, exports, imports, devDependencies, dependencies; standard fields only; no peerDependencies or packageManager
|
||||
description: package.json keys follow Oxfmt's standard order (vp check --fix); standard fields only; no peerDependencies or packageManager
|
||||
---
|
||||
|
||||
## Key order (2026-10-14)
|
||||
## Key order (2026-10-16)
|
||||
|
||||
Metadata first, then `scripts`, `exports`, `imports`, `devDependencies`,
|
||||
`dependencies`. Oxfmt would re-sort the file, so `vite.config.ts` sets
|
||||
`fmt: { sortPackageJson: false }`; keep it, and keep the order by hand.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "@cunarist/eslint-plugin-x",
|
||||
"version": "0.1.0",
|
||||
"description": "...",
|
||||
"keywords": ["eslint", "eslint-plugin", "eslintplugin", "oxlint"],
|
||||
"license": "MIT",
|
||||
"repository": { "type": "git", "url": "git+https://github.com/..." },
|
||||
"type": "module",
|
||||
"files": ["dist"],
|
||||
"engines": { "node": ">=22" },
|
||||
"eslint-doc-generator": {/* tool options that can only live here */},
|
||||
"scripts": { "docs": "eslint-doc-generator" },
|
||||
"exports": { ".": "./dist/index.js" },
|
||||
"imports": { "#helpers": "./src/helpers/index.ts" },
|
||||
"devDependencies": { "vite-plus": "^1.1.0" },
|
||||
"dependencies": {},
|
||||
}
|
||||
```
|
||||
Oxfmt's standard `package.json` sorting is accepted: there is no
|
||||
`fmt: { sortPackageJson: false }`, and `vp check --fix` sets the order. This
|
||||
replaces the earlier hand-written order (metadata, `scripts`, `exports`,
|
||||
`imports`, `devDependencies`, `dependencies`).
|
||||
|
||||
The `imports` key order can carry meaning: import-check's
|
||||
`enforce-layer-order` reads it as the layer order. Never reorder it casually.
|
||||
|
||||
@@ -11,11 +11,15 @@ Oxlint plugin with `meta.name` `"project"`, loaded through `jsPlugins` in
|
||||
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
|
||||
`no-disable-comments` and `no-asset-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/`.
|
||||
`scripts/` stays flat; don't add `scripts/rules/`. A file that would pass
|
||||
`max-lines` (300) splits into flat themed files sharing the prefix:
|
||||
`scripts/project-lint.ts` keeps the plugin object and the rule list and
|
||||
imports the others (`project-lint-modules.ts`, `project-lint-types.ts`,
|
||||
`project-lint-lit.ts`, ...).
|
||||
|
||||
```ts
|
||||
import type { DummyRuleMap } from "vite-plus/lint";
|
||||
|
||||
@@ -23,7 +23,7 @@ Standing convention (2026-10-12), applied to every package:
|
||||
(`--generate-notes`, edited there). There is no `CHANGELOG.md`; never add
|
||||
one.
|
||||
- **The version lives in two places in plugin packages:** `package.json` and
|
||||
`meta.version` in `src/index.ts`; `tests/package-meta.test.ts` fails if they
|
||||
`meta.version` in `src/plugin/index.ts`; `tests/package-meta.test.ts` fails if they
|
||||
differ.
|
||||
- **Before 1.0**, a change that breaks users (renamed rule id, a rule that
|
||||
fires on more code, removed behavior) is a minor bump.
|
||||
|
||||
@@ -44,8 +44,8 @@ before any push. CI installs the toolchain with `voidzero-dev/setup-vp`.
|
||||
|
||||
- `vp check` does **not** report `isolatedDeclarations` errors (TS9010); only
|
||||
`vp pack` does, so packages run `vp pack` in CI on every PR.
|
||||
- Oxfmt sorts `package.json` keys by default, which destroys the required key
|
||||
order. Set `fmt: { sortPackageJson: false }` ([[package-json-shape]]).
|
||||
- Oxfmt sorts `package.json` keys by default; that order is accepted, so
|
||||
there is no `fmt` option for it ([[package-json-shape]]).
|
||||
- Oxlint and Oxfmt skip what `.gitignore` lists; that is the only reason
|
||||
`dist/` and `node_modules/` are not linted. Keep build output gitignored.
|
||||
- Vite+ bundles `vite.config.ts` itself and resolves `#` imports in it.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: Every file is .ts (no js/mjs/cjs/jsx/tsx, configs included) and source imports only .ts; CSS comes from HTML, JSON/SVG become .ts modules
|
||||
description: Every file is .ts (no js/mjs/cjs/jsx/tsx, configs included); imports name only .ts files or .js package subpaths; CSS comes from HTML, JSON/SVG become .ts modules
|
||||
---
|
||||
|
||||
The user's rule, from the Deno days and restated for Node (2026-10-12): "No
|
||||
@@ -12,12 +12,14 @@ plain `.ts`. Build output in a gitignored folder is the only JS.
|
||||
`noUncheckedIndexedAccess`). If a tool or framework seems to need a JS or TSX
|
||||
file, stop and flag it instead of adding one.
|
||||
|
||||
## Only `.ts` imports (2026-10-14)
|
||||
## No asset imports (2026-10-14, widened 2026-10-16)
|
||||
|
||||
`project/no-non-ts-imports` (in `scripts/project-lint.ts`) fails:
|
||||
`.ts` and `.js` imports are allowed: our own files stay `.ts`, and `.js`
|
||||
covers package subpaths such as `lit/decorators.js`.
|
||||
`project/no-asset-imports` (in `scripts/project-lint.ts`) fails:
|
||||
|
||||
- a relative, absolute, `#`, or package-subpath specifier whose last segment
|
||||
has a non-`.ts` extension: `.css`, `.json`, `.svg`, `.js`, `.mjs`, `.tsx`;
|
||||
has any other extension: `.css`, `.json`, `.svg`, `.mjs`, `.tsx`;
|
||||
- any `?query` suffix (`?raw`, `?url`, `?inline`);
|
||||
- any import attributes (`with { type: "json" }`, `with { type: "text" }`).
|
||||
|
||||
@@ -44,5 +46,6 @@ import { scenes } from "#data/scenes"; // yes: scenes.ts
|
||||
and fails on drift (the plugin repos do this for `meta.name`/`meta.version`).
|
||||
- **Reading a file is not importing it.** `readFileSync` + `JSON.parse` is
|
||||
fine in tooling and tests.
|
||||
- **A tool's `.js` entry:** don't `import("pkg/cli.js")`; use the package
|
||||
entry or spawn the package's bin ([[lit-analyzer-ts6]]).
|
||||
- **A tool's CLI file:** prefer the package entry or spawning the package's
|
||||
bin over importing `pkg/cli.js`, which runs the CLI as a side effect
|
||||
([[lit-analyzer-ts6]]).
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
name: migrate-deno-repo
|
||||
description: Checklist for moving a Deno repo (or a leftover Deno part of one) to Node 22+ + Vite+ with npm, same behavior, full lint parity, one PR
|
||||
---
|
||||
|
||||
Template: `cunarist/eslint-plugin-explicit-types` on `main`. Read its
|
||||
`AGENTS.md`, `vite.config.ts`, `scripts/project-lint.ts`, `package.json`, and
|
||||
CI before starting, and copy their shape. Never ask the user mid-way when
|
||||
they're away: decide by the memories, record the choice in the PR.
|
||||
|
||||
Branch `node-migration` (apps) or `initial-port` (new plugin repos); one PR
|
||||
with everything.
|
||||
|
||||
**Inventory first**
|
||||
|
||||
- [ ] List every `deno.json` task, import-map entry, lint rule (tags,
|
||||
includes, `@hugoalh`, custom plugins), test, `Deno.*` call, JSON/CSS/SVG
|
||||
import, and every place the output is consumed (Rust, HTML,
|
||||
Containerfile, CI).
|
||||
|
||||
**Toolchain**
|
||||
|
||||
- [ ] `package.json` in the required key order, `engines.node ">=22"`, no
|
||||
`packageManager`, no `peerDependencies`; `npm install`; commit
|
||||
`package-lock.json`.
|
||||
- [ ] `tsconfig.json` from the template; `vite.config.ts` with `fmt:
|
||||
{ sortPackageJson: false }` and the category-based `lint` block.
|
||||
- [ ] Delete `deno.json`, `deno.lock`, and Deno-only files. `mod.ts` →
|
||||
`index.ts`; `deno.json` imports → `package.json` `imports` (`#x/*`
|
||||
patterns for trailing-slash mappings; aliases may target only direct
|
||||
subfolders of `src/`, anything else becomes a relative import);
|
||||
`jsr:`/`npm:` → npm packages.
|
||||
|
||||
**Code**
|
||||
|
||||
- [ ] Only `.ts` files; no non-`.ts` imports: CSS via `<link>`, JSON and SVG
|
||||
as `.ts` modules, icons in memona's style.
|
||||
- [ ] `Deno.*` → `node:*` / `process`. Generated JS written as `.ts`, built,
|
||||
gitignored.
|
||||
- [ ] Decorators stay standard with `accessor`; add `scripts/decorators.ts`
|
||||
if the repo has any. Lit repos: `scripts/lit-check.ts` + `npm run
|
||||
lit-check`.
|
||||
- [ ] `scripts/` is flat: no subfolders, no `index.ts` (barrels live only in
|
||||
`src/`); `vite.config.ts` imports `./scripts/<name>.ts` directly. A
|
||||
scripts subfolder that is really an app moves under `src/`.
|
||||
|
||||
**Lint**
|
||||
|
||||
- [ ] `scripts/project-lint.ts` copied; old local Deno lint rules ported into
|
||||
it; parity mapping for every former rule; `id-match`; no disable
|
||||
comments; no source `overrides`; refactor size-rule hits.
|
||||
|
||||
**Tests**
|
||||
|
||||
- [ ] `Deno.test` → Vitest (codemod, then finish TODOs); unit tests next to
|
||||
code, integration in `tests/`; no case dropped.
|
||||
|
||||
**Build, ship, CI**
|
||||
|
||||
- [ ] `vp build` in app mode with the same output paths, or `vp pack` for
|
||||
packages. Containerfile node stage on `node:24-alpine`. CI with
|
||||
`setup-vp`: `vp check`, build, `vp test` (+ lit-check). Packages:
|
||||
release-triggered `publish.yml`.
|
||||
- [ ] `AGENTS.md` with the damem header, `.agents/` notes from the damem kit,
|
||||
README updated (Node.js 22+, no Deno instructions). `damem doctor`
|
||||
clean.
|
||||
|
||||
**Finish**
|
||||
|
||||
- [ ] `vp check`, `vp test`, build all pass locally; the app runs.
|
||||
- [ ] Commit in plain imperative sentences; open the PR (summary, changes,
|
||||
parity mapping, deviations/risks, "CI fails until @cunarist/eslint-plugin-*
|
||||
are published" if relevant, verification, footer line).
|
||||
- [ ] Run `review-cycle`. Never merge, publish, or archive.
|
||||
+4
-4
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "bot-agents",
|
||||
"description": "A Gitea composite action that runs a coding agent for approved issue and pull request automation.",
|
||||
"private": true,
|
||||
"description": "A Gitea composite action that runs a coding agent for approved issue and pull request automation.",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/core": "^0.17.0",
|
||||
"@types/node": "^24.19.2",
|
||||
"@typescript-eslint/types": "^8.71.2",
|
||||
"vite-plus": "^1.2.0",
|
||||
"yaml": "^2.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.2"
|
||||
}
|
||||
}
|
||||
|
||||
+24
-19
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* `project` — lint rules this repository holds its own source to, on top of
|
||||
* the Oxlint categories in `vite.config.ts`. Its rules of its own are `no-disable-comments` and `no-non-ts-imports`.
|
||||
* the Oxlint categories in `vite.config.ts`. Its rules of its own are `no-disable-comments` and `no-asset-imports`.
|
||||
*
|
||||
* Loaded by `vite.config.ts` as an Oxlint `jsPlugins` entry.
|
||||
*
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import type { LanguageOptions, RuleDefinition, RuleVisitor, TextSourceCode } from "@eslint/core";
|
||||
import type { TSESTree } from "@typescript-eslint/types";
|
||||
import { AST_NODE_TYPES, type TSESTree } from "@typescript-eslint/types";
|
||||
import type { DummyRuleMap } from "vite-plus/lint";
|
||||
|
||||
/** The source code both hosts hand a rule, with the comment list. */
|
||||
@@ -70,12 +70,16 @@ const noDisableCommentsRule: ProjectRule = {
|
||||
/** A module specifier read as a path: relative, absolute, or a `#` alias. */
|
||||
const PATH_SPECIFIER_PATTERN: RegExp = /^[./#]/u;
|
||||
|
||||
/** Script module extensions an import may name: our `.ts` files and `.js` package subpaths. */
|
||||
const SCRIPT_EXTENSIONS: readonly string[] = [".ts", ".js"];
|
||||
|
||||
/**
|
||||
* Whether `source` names something other than a `.ts` module: a query string
|
||||
* such as `?raw`, or a final path segment with any other extension. Bare
|
||||
* package names are left alone, dots and all, as are extensionless paths.
|
||||
* Whether `source` names an asset rather than a script module: a query string
|
||||
* such as `?raw`, or a final path segment with an extension other than `.ts`
|
||||
* or `.js`. Bare package names are left alone, dots and all, as are
|
||||
* extensionless paths.
|
||||
*/
|
||||
function isNonTsSource(source: string): boolean {
|
||||
function isAssetSource(source: string): boolean {
|
||||
if (source.includes("?")) {
|
||||
return true;
|
||||
}
|
||||
@@ -90,30 +94,31 @@ function isNonTsSource(source: string): boolean {
|
||||
if (lastSegment === "." || lastSegment === ".." || !lastSegment.includes(".")) {
|
||||
return false;
|
||||
}
|
||||
return !lastSegment.endsWith(".ts");
|
||||
return !SCRIPT_EXTENSIONS.some((extension: string): boolean => lastSegment.endsWith(extension));
|
||||
}
|
||||
|
||||
/**
|
||||
* `no-non-ts-imports` — rejects importing anything but a `.ts` module: a
|
||||
* `no-asset-imports` — rejects importing anything but a script module: a
|
||||
* source path, static or a dynamic `import()` with a string literal, ending in
|
||||
* another extension (`.css`, `.json`, `.svg`, `.js`, `.tsx`) or carrying a
|
||||
* query string (`?raw`, `?url`), and any import or export with import
|
||||
* attributes. Bare package names, `#` aliases, and `node:` builtins without an
|
||||
* extension pass; package subpaths are checked like paths.
|
||||
* an extension other than `.ts` or `.js` (`.css`, `.json`, `.svg`, `.tsx`) or
|
||||
* carrying a query string (`?raw`, `?url`), and any import or export with
|
||||
* import attributes. `.js` covers package subpaths such as
|
||||
* `lit/decorators.js`. Bare package names, `#` aliases, and `node:` builtins
|
||||
* without an extension pass; package subpaths are checked like paths.
|
||||
*/
|
||||
const noNonTsImportsRule: ProjectRule = {
|
||||
const noAssetImportsRule: ProjectRule = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
docs: { description: "Disallow importing modules other than .ts files" },
|
||||
docs: { description: "Disallow importing assets instead of .ts or .js modules" },
|
||||
schema: [],
|
||||
messages: {
|
||||
extension: "Only .ts modules may be imported.",
|
||||
extension: "Only .ts and .js modules may be imported.",
|
||||
attributes: "Import attributes are not allowed.",
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
function checkSource(source: TSESTree.StringLiteral): void {
|
||||
if (isNonTsSource(source.value)) {
|
||||
if (isAssetSource(source.value)) {
|
||||
context.report({ node: source, messageId: "extension" });
|
||||
}
|
||||
}
|
||||
@@ -136,7 +141,7 @@ const noNonTsImportsRule: ProjectRule = {
|
||||
ExportNamedDeclaration: checkDeclaration,
|
||||
ExportAllDeclaration: checkDeclaration,
|
||||
ImportExpression(node: TSESTree.ImportExpression): void {
|
||||
if (node.source.type === "Literal" && typeof node.source.value === "string") {
|
||||
if (node.source.type === AST_NODE_TYPES.Literal && typeof node.source.value === "string") {
|
||||
checkSource(node.source);
|
||||
}
|
||||
if (node.options !== null) {
|
||||
@@ -203,7 +208,7 @@ const rules: DummyRuleMap = {
|
||||
"typescript/no-non-null-assertion": "error",
|
||||
// No disable comments at all.
|
||||
"project/no-disable-comments": "error",
|
||||
"project/no-non-ts-imports": "error",
|
||||
"project/no-asset-imports": "error",
|
||||
"unicorn/no-abusive-eslint-disable": "error",
|
||||
// Pedantic, but Node's and the web's own types (`Response`, `Stats`,
|
||||
// `RequestInit`) are mutable, so no parameter of theirs can pass; the
|
||||
@@ -219,7 +224,7 @@ const plugin = {
|
||||
meta: { name: "project" },
|
||||
rules: {
|
||||
"no-disable-comments": noDisableCommentsRule,
|
||||
"no-non-ts-imports": noNonTsImportsRule,
|
||||
"no-asset-imports": noAssetImportsRule,
|
||||
},
|
||||
configs: { project: { rules } },
|
||||
};
|
||||
|
||||
@@ -3,18 +3,11 @@ import { defineConfig } from "vite-plus";
|
||||
import projectLint from "./scripts/project-lint.ts";
|
||||
|
||||
export default defineConfig({
|
||||
// Keep package.json in its hand-written order.
|
||||
fmt: { sortPackageJson: false },
|
||||
lint: {
|
||||
plugins: ["typescript", "unicorn", "oxc", "import"],
|
||||
jsPlugins: ["./scripts/project-lint.ts"],
|
||||
categories: { correctness: "error", suspicious: "error", pedantic: "error", perf: "error" },
|
||||
rules: projectLint.configs.project.rules,
|
||||
// Repo tooling, not app code: the lint plugin compares AST node `type`s,
|
||||
// which the AST types declare as an enum.
|
||||
overrides: [
|
||||
{ files: ["scripts/**"], rules: { "typescript/no-unsafe-enum-comparison": "off" } },
|
||||
],
|
||||
options: {
|
||||
typeAware: true,
|
||||
typeCheck: true,
|
||||
|
||||
Reference in New Issue
Block a user