Break editor into a module

This commit is contained in:
khalid@traclabs.com
2026-05-24 08:44:29 -05:00
parent 1d01442a39
commit 070c95e254
119 changed files with 5318 additions and 26124 deletions

View File

@@ -183,14 +183,21 @@ export default defineConfig({
{
// Stickers are static brand assets — once fetched, they're
// safe to cache aggressively. CacheFirst minimizes network
// traffic when the user reopens the editor. Capacity (200)
// is comfortably more than a typical library; new stickers
// pushed via a deploy will displace old ones on access.
// traffic when the user reopens the editor.
//
// maxEntries sized to cover the full library (~1,600 files
// at this writing) with headroom for growth. The module's
// useStickerPrefetch hook fills this cache proactively on
// tab-open; without enough capacity, prefetched stickers
// would evict each other and the prefetch would be wasted.
// Browser storage quotas (typically tens to hundreds of MB)
// remain the ultimate ceiling — maxEntries just stops
// Workbox from evicting earlier than the browser would.
urlPattern: /^\/stickers\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'sticker-library',
expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
expiration: { maxEntries: 2000, maxAgeSeconds: 60 * 60 * 24 * 30 },
cacheableResponse: { statuses: [0, 200] },
},
},
@@ -198,6 +205,69 @@ export default defineConfig({
},
}),
],
// Resolution config for the goods-editor module.
// ───────────────────────────────────────────────────────────────
// The module is installed from Gitea via a git+ssh dependency
// (see host package.json). npm clones the release branch into
// node_modules/goods-editor/, which contains pre-built dist/.
// The host consumes the module exactly as any other consumer
// would: through package.json resolution to dist/goods-editor.js
// and dist/goods-editor.css.
//
// No aliases here. Earlier versions of this config aliased to
// source for HMR, but that bypassed the dist entirely and hid
// bugs that would have surfaced in deployment (dual-React from
// inline-bundled React, stale dist CSS, CJS require errors).
//
// The module's build (rollup.config.js inside goods-editor-module)
// externalizes React properly using @rollup/plugin-commonjs to
// handle the CJS interop that Vite 8's Rolldown couldn't. With
// React external, there's only one React instance in the final
// bundle by construction — no dedupe trickery needed at runtime.
//
// What stays
// ──────────
// `dedupe: ['react', ...]` remains as cheap insurance. If any
// transitive ever bundles React inline, dedupe collapses it to
// the host's copy. With proper externalization this should be a
// no-op, but it costs nothing to leave in.
//
// `optimizeDeps.exclude: ['goods-editor']` — Vite's default is to
// prebundle node_modules deps into .vite/deps/ for faster dev
// startup. The goods-editor dist is already bundled (it's the
// output of Rollup), and prebundling can subtly alter how its
// chunks are loaded. Excluding lets Vite serve the dist modules
// directly as ESM.
//
// Dev workflow with git+ssh
// ─────────────────────────
// Iterating on the module is intentionally heavyweight to mirror
// real consumers:
//
// 1. Make + commit changes in goods-editor-module on `main`.
// 2. Run `./scripts/release.sh --version=X.Y.Z` in the module.
// This builds, switches to `release` branch, commits dist,
// tags `vX.Y.Z`, pushes branch + tag to Gitea.
// 3. In this host's package.json, bump the `#vX.Y.Z` fragment
// on the goods-editor dep.
// 4. `rm -rf node_modules package-lock.json && npm install`
// so npm pulls the new tag fresh (just bumping the version
// in package.json doesn't always refresh the cloned dep).
// 5. `npm run dev` — Vite serves the new dist.
//
// For faster iteration loops that don't validate the full
// install path, use the module's `npm run dev:dist` (smoke
// tests the built dist in an isolated browser environment
// without needing this host or a release). And `npm run dev`
// in the module is the fastest of all — source-aliased dev
// host, HMR on source edits, no build needed — useful when
// working purely on module internals.
resolve: {
dedupe: ['react', 'react-dom', 'react/jsx-runtime'],
},
optimizeDeps: {
exclude: ['goods-editor'],
},
server: {
port: 3000,
proxy: {
@@ -236,7 +306,7 @@ export default defineConfig({
//
// What goes where
// ───────────────
// transformers/ @huggingface/transformers \u2014 the in-browser
// transformers/ @huggingface/transformers the in-browser
// ML runtime used for background removal.
// Biggest single dependency. Only the
// inference engine is bundled here; the
@@ -245,16 +315,16 @@ export default defineConfig({
// `transformers-models` / `transformers-lfs`
// runtimeCaching rules above for offline
// re-use).
// konva/ konva + react-konva + use-image \u2014 the canvas
// konva/ konva + react-konva + use-image the canvas
// stack. All three are needed together
// because react-konva wraps konva and
// use-image wraps it for React consumption.
// Grouping prevents one from being inlined
// into a chunk that doesn't otherwise need
// the others.
// react-vendor/ react + react-dom \u2014 stable, every visitor
// react-vendor/ react + react-dom stable, every visitor
// needs them, perfect for long-term caching.
// filerobot/ react-filerobot-image-editor \u2014 mid-size,
// filerobot/ react-filerobot-image-editor mid-size,
// used only when the user opens the advanced
// image editor. Worth its own chunk so the
// rest of the app doesn't pay for it.
@@ -276,7 +346,7 @@ export default defineConfig({
//
// If a future change makes one of these grow significantly (say,
// a styled-components major upgrade that doubles its size), it
// becomes worth pulling into its own chunk \u2014 but only then.
// becomes worth pulling into its own chunk but only then.
//
// Maintenance: when adding a new heavy dependency
// ───────────────────────────────────────────────
@@ -284,7 +354,7 @@ export default defineConfig({
// 2. If it bloated the `index` chunk past comfort (rule of thumb:
// if index alone is > 1 MiB), add a new entry to manualChunks
// targeting that package name.
// 3. The 2 MiB workbox cap acts as the loud alarm \u2014 if you forget,
// 3. The 2 MiB workbox cap acts as the loud alarm if you forget,
// the build fails and tells you exactly which chunk overflowed.
rollupOptions: {
output: {