Files
apparel-designer/MIGRATION_RUNBOOK.md
2026-05-24 08:44:29 -05:00

322 lines
11 KiB
Markdown

# Migration runbook — apparel-designer → goods-editor module
Step-by-step instructions for executing the module extraction. This
document is intentionally minimal-fluff: do each step in order, verify
the marker at each checkpoint, move to the next.
## Prerequisites
Verify both repos exist as siblings:
```
/Users/khalid/Documents/Claude-Desktop/
├── apparel-designer/ (the host app, in working state)
└── goods-editor-module/ (the empty module repo we just scaffolded)
```
## Phase 1 — Module scaffolding (already done)
These were created during the planning session and are ready to use:
```
goods-editor-module/
├── package.json
├── vite.config.js (library mode)
├── vite.server.config.js (Node server entry)
├── vitest.config.js
├── .gitignore
├── README.md
├── src/
│ ├── ApparelDesigner.jsx (~30KB orchestration absorbed from App.jsx)
│ ├── index.js (public API barrel)
│ └── server/
│ ├── index.js
│ └── mountEditorApi.js (Express middleware factory)
├── examples/dev-host/ (standalone dev playground)
└── scripts/
├── release.sh
└── post-migrate-patches.mjs
```
**Verification:** `ls goods-editor-module/src/index.js` should succeed.
## Phase 2 — File migration
Copy all the editor source from the host into the module.
```bash
cd /Users/khalid/Documents/Claude-Desktop
bash apparel-designer/scripts/migrate-to-module.sh
```
**What this does:** Copies utils, hooks, constants, components,
styles, fonts, stickers, tests from `apparel-designer/src/` (and
related directories) into `goods-editor-module/src/`. Source is left
intact in `apparel-designer/` — nothing is deleted yet.
**Verification:** `ls goods-editor-module/src/components/canvas/`
should show DesignCanvas.jsx, ElementToolbar.jsx, etc.
## Phase 3 — Post-copy patches
The copied files import constants like `SHIRT_COLORS` directly. Inside
the module, those need to come from props. Run the patch script:
```bash
cd /Users/khalid/Documents/Claude-Desktop/goods-editor-module
node scripts/post-migrate-patches.mjs
```
**What this does:** Modifies a handful of files (`ShirtOptionsPanel`,
`Sidebar`, `UploadTab`) to accept their data as props instead of
importing constants.
**Verification:** Search the module source for `SHIRT_COLORS` — should
return 0 hits in component files (still present as a `constants/shirt.js`
default that's no longer imported by components).
## Phase 4 — Module install + build
```bash
cd /Users/khalid/Documents/Claude-Desktop/goods-editor-module
npm install
npm run build
```
**Verification:** `ls dist/` should show:
- `goods-editor.es.js`
- `goods-editor.cjs.js`
- `style.css`
- `server/index.js`
- `server/index.cjs`
If the build fails:
- **"Cannot find module 'X'"** — the migration may have missed a file.
Check `goods-editor-module/src/` for the missing path and copy it
from `apparel-designer/src/` manually.
- **"SHIRT_COLORS is not defined"** — patch script didn't run or didn't
fully match. Manually search for the symbol in the offending file
and replace with the prop.
## Phase 5 — Module dev-host smoke test (optional but recommended)
Verify the module works in isolation before wiring it into the host:
```bash
cd /Users/khalid/Documents/Claude-Desktop/goods-editor-module
npm run dev
# open http://localhost:3000 in a browser
```
You should see the editor with the dev host's minimal top bar. If
something's broken it's a module issue; fix here before touching the
host.
The dev host doesn't have an /api/upload endpoint, so uploads will
fail — that's expected. Text, stickers, emoji, and templates work
without a server.
## Phase 6 — Host cutover
Replace the host's monolithic App.jsx + server.js with the new
module-consuming versions:
```bash
cd /Users/khalid/Documents/Claude-Desktop/apparel-designer
bash scripts/cutover-host.sh
```
**What this does:**
1. Renames the monolithic files to `*.monolith.*` (kept for reference)
2. Promotes `App.host.jsx``App.jsx`, `server.host.js``server.js`,
`package.host.json``package.json`
3. Deletes the module-owned source from `src/`
**Verification:** `ls apparel-designer/src/` should show only:
- `App.jsx`
- `App.monolith.jsx` (backup)
- `main.jsx`
- `index.css`
- `components/Header.jsx`, `components/PWAInstall.jsx`,
`components/OfflineIndicator.jsx`
- `styles/Header.css`, `styles/PWAInstall.css` (or however they
organize after cleanup)
(The host's `src/styles/` and `src/components/` may still have
host-specific CSS that wasn't deleted. That's expected — only
module-owned files were removed.)
## Phase 7 — Host install + run
```bash
cd /Users/khalid/Documents/Claude-Desktop/apparel-designer
rm -rf node_modules package-lock.json
npm install
npm run dev
```
The `file:../goods-editor-module` reference in package.json points at
the local module directory. npm install will copy it into
`node_modules/goods-editor/`.
### Important: host-owned constants survive cutover
The `cutover-host.sh` script preserves three files in `src/constants/`:
- `shirt.js` — host's color / size / price catalog
- `templates.js` — host's template definitions
- `products.js` — host's product configuration
These are imported by the new `App.jsx` and passed into
`<ApparelDesigner>` as props. Other constants (fonts, stickers,
elements, etc.) are module-owned and get deleted.
If you customize the host's catalog (different shirt colors, new
templates), you edit those files — not the module.
### Important: host-required native deps for server-side export
`goods-editor/server`'s `mountEditorApi()` accepts the host's `canvas`
and `sharp` modules via options:
```js
import canvas from 'canvas';
import sharp from 'sharp';
import { mountEditorApi } from 'goods-editor/server';
mountEditorApi(app, { canvas, sharp, exportsDir, fontsDir, ... });
```
This injection pattern means:
- The module declares both as **optional peer dependencies** — listed
for tooling visibility but not auto-installed.
- Hosts that mount the server install canvas + sharp in their OWN
`package.json` (as regular deps) and import them at the top of
their `server.js`.
- Hosts that DON'T mount the server (editor-only, preview-only)
don't install them at all. The React side of `goods-editor` doesn't
need them.
Why injection rather than `require()` inside the module: when the
module is consumed via `file:` (local dev) or `git+ssh://` (release),
the module's built server bundle lives in the consuming app's
`node_modules`. A `require('canvas')` from inside that bundle would
resolve from the MODULE'S directory chain, not the host's. The
injection pattern sidesteps this entirely — the host imports canvas
from its own `node_modules` and passes the imported object in.
**System libs:** `canvas` builds native bindings against cairo/pango.
On macOS:
```
brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
```
If `npm install` fails on the canvas build step, those libs are
usually the missing piece.
If the host doesn't need server-side export (e.g. editor-only flows),
skip the `mountEditorApi(app, ...)` call in `server.js` entirely and
don't install canvas/sharp — the editor still works as a pure React
component.
### Important: the host's `virtual:sticker-manifest` setup stays in place
The module's `src/constants/stickers.js` imports from
`virtual:sticker-manifest` — a Vite virtual module that the HOST'S
`vite.config.js` provides via `stickerManifestPlugin()`. This is
intentional: it makes the host the source-of-truth for which stickers
are available.
**Don't delete `stickerManifestPlugin()` from the host's
`vite.config.js`,** and don't delete `public/stickers/`. The cutover
script leaves both alone. If you accidentally remove the plugin, you
will see this error when running the editor:
```
Failed to resolve import "virtual:sticker-manifest" from
"node_modules/goods-editor/dist/goods-editor.es.js".
```
The fix is to restore the plugin (it's the `stickerManifestPlugin()`
function at the top of `vite.config.js`, plus its inclusion in the
`plugins:` array).
If you want to ship the editor on a host that doesn't use Vite, or
that has a fundamentally different sticker layout, you can pass
stickers in via props on `<ApparelDesigner>` instead and provide a
no-op virtual module that exports an empty `STICKER_FILES` array.
(That extension point doesn't exist yet — file an issue / add a prop
when the second host needs it.)
**Verification:** `http://localhost:3000` should show the editor with
the host's full Header and the editor mounted below it. Functionality
should match the pre-migration version exactly.
## Phase 8 — Final checks
Things to verify in the running app:
- [ ] Can add text from the Text tab
- [ ] Can upload a photo and see it on the canvas
- [ ] Can add a sticker / emoji
- [ ] Cmd+Z undoes; Cmd+Shift+Z redoes
- [ ] Crop an image, Cmd+Z restores pre-crop (the May 22 regression)
- [ ] Save button triggers an export (download via the export pipeline)
- [ ] Reload the page — designs persist via localStorage
- [ ] Production build: `npm run build && NODE_ENV=production npm start`
If everything works:
1. `git add -A && git commit -m "Migrate to goods-editor module"` in both
repos
2. Cut a release of the module:
```bash
cd goods-editor-module
./scripts/release.sh --version=0.1.0-alpha.0
```
3. In the host, swap the dep from `file:` to `git+ssh://` once you've
pushed to Gitea.
## Rollback
If anything goes sideways and you need to undo the cutover:
```bash
cd apparel-designer
mv src/App.jsx src/App.host.jsx
mv server.js server.host.js
mv package.json package.host.json
mv src/App.monolith.jsx src/App.jsx
mv server.monolith.js server.js
mv package.monolith.json package.json
# Restore deleted module-owned files from git:
git checkout -- src/ fonts/ e2e/ playwright.config.js vitest.config.js
rm -rf node_modules package-lock.json
npm install
npm run dev
```
The `goods-editor-module/` repo stays put — rollback only affects the
host. You can iterate on the module separately and try the cutover
again later.
## Done
After successful cutover, future development looks like:
- **Editor changes** (canvas behavior, sidebar UX, hooks, utilities):
edit in `goods-editor-module/`. Test via `npm run dev` (dev host).
When happy, `./scripts/release.sh --version=X.Y.Z` and bump the
host's `package.json`.
- **Host changes** (homepage, marketing, cart, top bar): edit in
`apparel-designer/`. AI tools pointed at this directory only see
the host's ~10 files plus the editor's public API surface, not the
editor's ~100 internal files. That's the win.