chore: modernize stack - #12
Merged
Merged
Conversation
MIGRATION.md §0. Bump `.tool-versions` to `nodejs 24.20.0` and drop the `python 3.9.5` line (only the old native canvas / tfjs-node toolchain needed it). .gitignore: add `.turbo`, `*.tsbuildinfo`, and `/.fork-update` (the local patch bundle for the feedsbrain/face-api.js fork, not part of the app). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §1, §2, §4, §6 — the dependency half. The regenerated
package-lock.json can't be split per section, so all version moves land in one
commit:
- face-api.js -> `github:feedsbrain/face-api.js#master`, pinned in the lockfile
to commit 7cc8a41 ("chore: make package git-installable"). No @tensorflow/tfjs
added: the fork depends on tfjs-core + backend-cpu + backend-webgl itself and
its ES build self-registers a backend.
- react / react-dom 17 -> 19; @types/react(-dom) 19; add @types/node 24.
- antd 4 -> 6; add @ant-design/cssinjs + @ant-design/nextjs-registry.
Drop @ant-design/icons (was imported nowhere).
- next 11 -> 16; eslint 7 -> 9; eslint-config-next 11 -> 16; typescript 4 -> 5.9.
Drop babel-plugin-import and next-plugin-antd-less.
- add engines.node ">=20.9.0"; `lint` script `next lint` -> `eslint`
(next 16 removed `next lint`).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §2. `React.FC<{}>` + the explicit `React` import -> plain arrow
component. (DetectionLayout / WebcamDetect get the same treatment in the App
Router commit, where they also gain other App Router / antd 6 changes.)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §3 and §4 — inseparable because the antd 6 wiring lives in the new
App Router files.
Next 16 / App Router:
- delete .babelrc.js (re-enables SWC + Turbopack; a stray babel config silently
pulls Babel back in) and drop the babel-plugin-import usage.
- next.config.js -> next.config.mjs, Less wrapper removed (drop
next-plugin-antd-less); `output: 'standalone'` deliberately not set (it warns
under `next start`, which start:prod relies on).
- delete src/pages/*. Add src/app/layout.tsx (root <html>/<body>, metadata),
src/app/page.tsx (server component -> <CameraDetection />),
src/app/api/hello/route.ts (Route Handler replacing pages/api/hello.js).
- src/components/CameraDetection.tsx: 'use client' wrapper doing
dynamic(() => import('./WebcamDetect'), { ssr: false }) inside DetectionLayout
— keeps face-api.js / TF.js (which touch window/document at import) off the
server; a Server Component can't pass ssr: false.
- WebcamDetect.tsx: 'use client'; `router.events` is gone in the App Router, so
the mount effect's cleanup calls stopVideoCapture() directly (+ an `active`
flag so a late loadModels().then() can't start the camera post-unmount).
start/stopVideoCapture -> useCallback declared before the effects;
self-recursive setTimeout retry -> hoisted inner `attempt()`; `currentModel`
state (setter only ever called in an effect — react-hooks@7 error) -> plain
`const currentModel = model ?? 'mobilenet'`; typed refs.
antd 4 -> 6:
- SSR style extraction: <AntdRegistry> from @ant-design/nextjs-registry in
layout.tsx (App Router equivalent of the old _document StyleProvider).
- src/styles/variables.less (@primary-color) -> deleted; theme token moves to
src/app/providers.tsx ('use client' ConfigProvider,
token.colorPrimary '#14424d').
- prop deltas actually hit: <Card bordered={false}> -> variant="borderless";
<Space direction="vertical"> -> orientation="vertical".
DetectionLayout.tsx: React.FC<Props> -> ({ children }: Props), stale
eslint-disable comments removed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §5. tsconfig.json: target es5 -> ES2022; moduleResolution
node -> bundler; add incremental, plugins [{ name: "next" }], paths
@/* -> ./src/*; include .next/types. `strict: false` kept (typed refs are
enough; tightening is a follow-up). next-env.d.ts picks up the App Router
type references; `next build` also flips jsx preserve -> react-jsx — expected,
left as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §6. Next 16 removed `next lint` and `next build` no longer lints. Delete .eslintrc.json; add eslint.config.mjs extending eslint-config-next/core-web-vitals (v16 ships flat config, bundling next + next/typescript) plus an ignores block. Not wrapped in FlatCompat — compat.extends() on the already-flat array throws "Converting circular structure to JSON". (`lint` script -> `eslint` landed with the deps commit.) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §7. Base node:14.17.3-buster* -> node:24-bookworm-slim (both stages). Build stage installs git (needed to resolve the github:feedsbrain/face-api.js dependency) then `npm ci`. Runtime stage copies node_modules / .next / public / package*.json / next.config.mjs from the build stage instead of reinstalling; CMD ["npm","run","start:prod"] (still `next start -p 80`). No standalone output (see §3). The version.json `sed` patch is kept. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §8. The workflow only runs `docker build` on a self-hosted runner, so the Node bump rides along inside the image. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
MIGRATION.md §9. Rewrite README for the new stack (fix stale "standalone" wording — standalone was tried and reverted). Add SPEC.md (as-built description), MIGRATION.md (step-by-step record), and AGENTS.md / CLAUDE.md (the Next-generated "this is not the Next.js you know" agent note + @AGENTS.md include). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R84aj1GZ2yuPGqpioL1kfM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modernizing stack and ui