From 619565d07f85549b3d22edd8abd5792577f03663 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 30 May 2026 21:01:58 +0100 Subject: [PATCH] =?UTF-8?q?docs(migrations):=20canonical=20npm=E2=86=92Den?= =?UTF-8?q?o=20template=20+=202026-05-30=20re-inventory=20(closes=20#262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STEP 2 of campaign #253. Adds: - docs/migrations/npm-to-deno-template/deno.json — canonical shape derived from echidna + svalinn + oikos Phase 5 follow-ups. Class A (pure-Deno port) defaults to nodeModulesDir: "none"; Class B (npm wrapper via Deno) sets "auto" per task. - docs/migrations/npm-to-deno-template/MIGRATION.md — per-repo recipe: class triage, scaffolding deletion, CI workflow swaps, commit pattern, PR + auto-merge convention. - docs/migrations/npm-to-deno-template/INVENTORY-2026-05-30.md — re-run with documented excludes returns 162 manifests across 63 repos. Drift from umbrella baseline (172) is -5.8%, within tolerance. No STEP re-ordering required. Source TSV at ~/Documents/npm-to-deno-inventory-2026-05-30.tsv. Closes #262. STEPs 3-7 unblocked. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../INVENTORY-2026-05-30.md | 67 ++++++++ .../npm-to-deno-template/MIGRATION.md | 153 ++++++++++++++++++ .../migrations/npm-to-deno-template/deno.json | 57 +++++++ 3 files changed, 277 insertions(+) create mode 100644 docs/migrations/npm-to-deno-template/INVENTORY-2026-05-30.md create mode 100644 docs/migrations/npm-to-deno-template/MIGRATION.md create mode 100644 docs/migrations/npm-to-deno-template/deno.json diff --git a/docs/migrations/npm-to-deno-template/INVENTORY-2026-05-30.md b/docs/migrations/npm-to-deno-template/INVENTORY-2026-05-30.md new file mode 100644 index 00000000..39de241e --- /dev/null +++ b/docs/migrations/npm-to-deno-template/INVENTORY-2026-05-30.md @@ -0,0 +1,67 @@ +# npm → Deno estate inventory — 2026-05-30 re-run + +Re-inventory per `hyperpolymath/standards#262` acceptance criterion. The +umbrella body (`#253`) cited **172** `package.json` manifests as of +2026-05-28; a looser `find` on 2026-05-30 returned **437** before excludes. + +Re-running with the umbrella's documented exclude set produces **162** +manifests across **63** repositories — within 6 % of the planning baseline, +no STEP re-sizing required. + +## Exclude set applied + +Parallel to `hypatia/lib/rules/cicd_rules.ex :nodejs_detected +path_allow_prefixes`: + +- `**/node_modules/**`, `**/deps/**` (vendored) +- `rescript/`, `servers/`, `repos-monorepo/`, `linguist/` (upstream forks) +- `hyperpolymath-archive/**` (archived) +- `**/vscode/**` (VSCode extension host-required) +- `affinescript-deno-test/`, `affinescript-cli/` (bootstrap shims) +- `**/example/**`, `**/examples/**`, `**/test-fixtures/**`, `**/fixtures/**` (fixtures) +- `**/.git/**` + +## Per-repo manifest count (top 25) + +| Manifests | Repo | +|---|---| +| 28 | developer-ecosystem | +| 14 | ssg-collection | +| 10 | affinescript | +| 9 | accessibility-everywhere | +| 7 | burble | +| 7 | affinescript-stdlib-pr | +| 5 | stapeln | +| 5 | boj-server | +| 4 | standards | +| 4 | reposystem | +| 4 | flat-mate | +| 3 | wordpress-tools | +| 3 | julia-the-viper | +| 3 | idaptik | +| 2 | zotero-tools, typed-wasm, proven, patallm-gallery, my-lang, kaldor-iiot, claude-integrations | + +## STEP sizing (refreshed) + +| STEP | Tier | Repos | Manifests | +|---|---|---|---| +| 3 | ≤2 manifest, smallest-first | ~45 | ~50 | +| 4 | 3-7 manifest, mid | ~13 | ~57 | +| 5 | 8+ manifest, larger | 3 | 27 | +| 6 | developer-ecosystem only | 1 | 28 | +| 7 | workspace finalisation | multi-repo wrap-up | — | + +162 = 50 + 57 + 27 + 28. STEP-7 wrap-up captures any post-batch hygiene. + +## Drift from umbrella + +| Source | Count | Note | +|---|---|---| +| Umbrella `#253` (2026-05-28) | 172 | Planning baseline | +| Loose `find` (2026-05-30) | 437 | Without excludes | +| **This re-run (2026-05-30)** | **162** | Documented excludes; canonical | + +Drift -10 (-5.8 %) vs umbrella. Within tolerance; no STEP re-ordering. + +Source TSV: `~/Documents/npm-to-deno-inventory-2026-05-30.tsv` +(`\t` per row, 162 rows). diff --git a/docs/migrations/npm-to-deno-template/MIGRATION.md b/docs/migrations/npm-to-deno-template/MIGRATION.md new file mode 100644 index 00000000..e1f7bf76 --- /dev/null +++ b/docs/migrations/npm-to-deno-template/MIGRATION.md @@ -0,0 +1,153 @@ +# npm → Deno per-repo migration recipe + +Canonical procedure for migrating a hyperpolymath estate repository from +`package.json` + npm/Node to `deno.json` + Deno. + +Policy: `docs/JS-RUNTIME-POLICY.adoc` (Deno > Bun > pnpm > npm). +Campaign tracker: hyperpolymath/standards#253. +Rule enforcement: hypatia `cicd_rules/nodejs_detected` + `npx_or_npm_run_in_ci`. + +## 0. Decide which class the repo is in + +Before touching anything, decide which of the migration classes applies. +Each class has a different end-state. + +| Class | Signal | End-state | +|---|---|---| +| **A. Pure-Deno port** | Repo's `package.json` only lists dev-only Node-compatible tools (`typescript`, `vitest`, build helpers). No host contract requires Node. | Delete `package.json` + `package-lock.json`. Author `deno.json`. CI workflows swap to `deno test`/`deno task`. | +| **B. npm wrapper via Deno** | Repo wraps an npm-published tool that does not yet have a Deno-native fork (e.g., `rescript`, `vite`, `tailwindcss`). | Keep dependency expressed as `npm:pkg@semver` inside `deno.json`'s `imports`. Tasks call `deno run -A --node-modules-dir=auto npm:pkg`. No `package.json`. | +| **C. Carve-out** | One of the six classes in `cicd_rules/nodejs_detected` `path_allow_prefixes` (VSCode extension, bootstrap shim, upstream fork, archived, vendored, example/fixture). | **Skip migration.** File stays on npm; no PR. | + +A given repo with multiple `package.json` files can split across classes — handle each manifest on its own merit. + +## 1. Inventory the current `package.json` + +```bash +# Capture starting point. +cat package.json +ls -la package-lock.json bun.lockb yarn.lock pnpm-lock.yaml 2>/dev/null +``` + +Record: + +- Direct deps (`dependencies` + `devDependencies`). +- Scripts (`scripts.*`). +- `engines.node`, `engines.npm` — note for replacement by `engines.deno`. +- `private`, `type`, `exports` — preserved as needed. + +## 2. Author `deno.json` from the canonical template + +Copy `deno.json` from this directory. Adjust: + +- `name` — `@hyperpolymath/`. +- `version` — preserve from `package.json`. +- `license` — `MPL-2.0-or-later` (estate default) unless repo policy differs. +- `compilerOptions` — preserve `strict` and friends from `tsconfig.json` if present. +- `imports` — populate from `dependencies`: + - Deno-native: `"@std/": "https://deno.land/std@0.224.0/"` (and similar). + - JSR: `"@scope/pkg": "jsr:@scope/pkg@^1.2.3"`. + - npm fallback: `"pkg": "npm:pkg@^1.2.3"` (Class B only). +- `tasks` — port from `scripts`: + - `"build": "rescript"` → `"build": "deno run -A --node-modules-dir=auto npm:rescript"`. + - `"test": "vitest"` → `"test": "deno test -A src/"` (port tests to Deno test API where reachable; if not yet portable, `"test": "deno run -A --node-modules-dir=auto npm:vitest"`). +- `nodeModulesDir`: + - Default `"none"` (Class A — pure Deno). + - Set to `"auto"` only when an npm package's lifecycle requires it (Class B; rescript and most ESM-shipped npm packages are fine without it). + +## 3. Delete the npm scaffolding + +```bash +git rm package.json package-lock.json +# Also remove bun.lockb / yarn.lock / pnpm-lock.yaml / .npmrc if present. +git rm -f bun.lockb yarn.lock pnpm-lock.yaml .npmrc 2>/dev/null || true + +# node_modules/ should already be in .gitignore. +rm -rf node_modules +``` + +## 4. Update `.gitignore` + +Confirm these entries are present (RSR canonical template propagates them — see `docs/JS-RUNTIME-POLICY.adoc §Canonical .gitignore Entries`): + +``` +# npm-avoidant (standards#67): estate JS-runtime policy is Deno>Bun>pnpm>npm. +package-lock.json +**/package-lock.json +node_modules/ +**/node_modules/ +bun.lockb +yarn.lock +pnpm-lock.yaml +``` + +## 5. Migrate CI workflows + +Search for any of these and replace: + +| Before | After | +|---|---| +| `actions/setup-node@` | `denoland/setup-deno@` (or remove if no JS step remains) | +| `npm ci` / `npm install` | `deno cache ` (often unnecessary — Deno caches at first run) | +| `npm test` / `npm run test` | `deno task test` (or `deno test -A src/`) | +| `npx ` | `deno run -A --node-modules-dir=auto npm:` (Class B) or Deno-native equivalent (Class A) | + +Note: hypatia `cicd_rules/npx_or_npm_run_in_ci` blocks `npx` and `npm run` in CI run-blocks (added 2026-05-28). Don't leave any. + +## 6. Verify locally + +```bash +deno check src/ +deno lint src/ +deno fmt --check src/ +deno test -A src/ +``` + +Class B (npm wrapper) — exercise the wrapped tool end-to-end: + +```bash +deno task build +deno task test +``` + +## 7. Commit pattern + +```bash +git add deno.json .gitignore .github/workflows/ +git rm package.json package-lock.json +git commit -m "feat(deno): migrate npm → Deno (standards#253) + + + +Class: A | B (per docs/migrations/npm-to-deno-template/MIGRATION.md) +Carry-forward: \">" +``` + +## 8. PR + auto-merge + +Per estate convention: auto-merge with squash. + +```bash +gh pr create --title "feat(deno): npm → Deno (standards#253)" \ + --body "" +gh pr merge --auto --squash --delete-branch +``` + +## Carry-forward patterns observed in oikos Phase 5 + 5 follow-ups (memory) + +- **ReScript wrapping** (canonical Class B): `deno run -A --node-modules-dir=auto npm:rescript@^12.0.0`. `--allow-scripts=npm:rescript` when the install lifecycle requires it. +- **Tailwind / vite / esbuild** — same pattern as rescript: `npm:@`, `--node-modules-dir=auto`. +- **`type: "module"` repos** — Deno is ESM-native, no extra step. +- **`exports` field** — preserve in `deno.json` if the package is published; otherwise drop. + +## Anti-patterns + +- ❌ Don't keep `package.json` "for tooling only" — `deno.json` covers fmt/lint/test/tasks. +- ❌ Don't fall back to `npm:` specifiers when a JSR or Deno-native equivalent exists (use `deno info ` to check). +- ❌ Don't commit `node_modules/` even on Class B — `--node-modules-dir=auto` regenerates at run-time. +- ❌ Don't add `"engines": {"node": "..."}` to `deno.json` — Deno doesn't honour it and it signals the repo isn't fully migrated. + +## When migration is blocked + +If a `package.json` cannot be removed (host-required, Node-only library, npm publish target), the path goes in the hypatia rule's `path_allow_prefixes` instead. See `standards/.claude/CLAUDE.md §npm Exemptions (Approved)` for the canonical exemption table. diff --git a/docs/migrations/npm-to-deno-template/deno.json b/docs/migrations/npm-to-deno-template/deno.json new file mode 100644 index 00000000..6a872db2 --- /dev/null +++ b/docs/migrations/npm-to-deno-template/deno.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://deno.land/x/deno/cli/schemas/config-file.v1.json", + "name": "@hyperpolymath/REPO-NAME", + "version": "0.1.0", + "license": "MPL-2.0-or-later", + + "compilerOptions": { + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + + "imports": { + "@std/": "https://deno.land/std@0.224.0/" + }, + + "tasks": { + "check": "deno check src/", + "lint": "deno lint src/", + "fmt": "deno fmt src/", + "test": "deno test -A src/", + + "ban-npm": "echo '❌ npm is BANNED estate-wide (standards#253). Use deno task / deno run.' && exit 1" + }, + + "fmt": { + "include": ["src/"], + "exclude": [], + "options": { + "useTabs": false, + "lineWidth": 100, + "indentWidth": 2, + "singleQuote": false, + "proseWrap": "preserve" + } + }, + + "lint": { + "include": ["src/"], + "exclude": [], + "rules": { + "tags": ["recommended"], + "include": [ + "ban-untagged-todo", + "no-sync-fn-in-async-fn", + "single-var-declarator" + ] + } + }, + + "test": { + "include": ["src/**/*_test.ts", "src/**/*.test.ts"] + }, + + "nodeModulesDir": "none" +}