Prune migration notes and align the notes with the current lint setup
Check / check (pull_request) Successful in 39s

This commit is contained in:
Danny Kim
2026-10-16 23:41:04 +09:00
parent 662e946fad
commit 15fe481181
12 changed files with 47 additions and 174 deletions
@@ -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]]).
+8 -7
View File
@@ -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`,
-45
View File
@@ -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.
+4 -3
View File
@@ -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`).
+4 -5
View File
@@ -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.
+4 -3
View File
@@ -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.
+6 -25
View File
@@ -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.
+6 -2
View File
@@ -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";
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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.
+9 -6
View File
@@ -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]]).
-74
View File
@@ -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.