Skip to content

feat!: delete the mirror fringe; rename adapters' /dev-spa to /single; plugins default-export their factory - #238

Merged
antfu merged 3 commits into
mainfrom
plan-040-mirror-fringe
Aug 17, 2026
Merged

feat!: delete the mirror fringe; rename adapters' /dev-spa to /single; plugins default-export their factory#238
antfu merged 3 commits into
mainfrom
plan-040-mirror-fringe

Conversation

@antfubot

@antfubot antfubot commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Intent

Deletes three drift-prone hand-copy clusters described in plan 040 ("delete the mirror fringe"), then follows up with two reviewer-requested API-shape improvements on the surface it touches. Each is an intentional breaking change during the 0.9 beta window; there is no deprecation cycle.

A. Six per-plugin vite.ts wrappers deleted

plugins/{a11y,assets,data-inspector,inspect,messages,og}/src/vite.ts were each an 8–25 line rename over devframeVite(def, options) from @devframes/vite/single, and the sole reason those six plugins depended on @devframes/vite. Deleted, along with their /vite export subpath, tsdown.config.ts entry, and the now-stale @devframes/vite dependencies entry (moved to devDependencies, since each plugin's own src/spa/vite.config.ts still uses devframeVite directly to dogfood the plugin during pnpm dev/build). Every internal vite.config.ts that imported the wrapper now calls devframeVite(pluginDevframe, options) directly.

Docs (migration guide + each affected plugin's page) lead with the recommended path for a Vite app — createPluginFromDevframe from @vitejs/devtools-kit/node, mounting into Vite DevTools — with devframeVite documented as the DevTools-free fallback, matching the stance the framework/hub docs already take elsewhere in this repo.

plugin-code-server's codeServerVite and plugin-terminals's terminalsVite are intentionally left alone (a STOP condition in the plan) — inventory found they aren't pure delegation: each composes devframeViteBridge + devframeVitePlugin together (two plugins where the other six use exactly one) and builds its devframe from the passed options via createCodeServerDevframe/createTerminalsDevframe, rather than importing a static default export. That's real behavior, not a mirror.

B. DEVFRAMES_HUB_BASE hand-copied ×4

Previously defined in packages/hub/src/node/initiate.ts and hand-copied (each commented "mirrors @devframes/hub's") into packages/vite/src/hub-client.ts, packages/nuxt/src/hub.ts, packages/nuxt/src/hub-client.ts, and packages/next/src/hub-client.tsx. Now defined once in the client-safe packages/hub/src/constants.ts (already a published @devframes/hub/constants subpath, and confirmed to stay client-safe — it only adds a plain string constant and a pure string-manipulation helper backed by ufo, a package hub already depends on). node/initiate.ts re-exports it for existing importers of that entry (no user-facing break there).

C. normalizeBase reimplemented ×4

Two different mechanisms were in play (ufo in hub/node/initiate.ts and next/src/hub.ts; hand-rolled string concatenation in vite/src/hub.ts and nuxt/src/hub.ts). Consolidated into one normalizeHubBase (ufo-based, since @devframes/hub already depends on ufo) exported next to the constant, and every call site — @devframes/hub itself plus all three framework adapters — now imports it instead of keeping a local copy.

Also deleted packages/hub-ui/src/client/state/renderers.ts, a 30-line shim that only delegated to createDockRenderersContext; its one call site (state/context.ts) now calls that directly.

D. Framework adapters' /dev-spa subpath renamed to /single

@devframes/{vite,nuxt,next} rename their single-devframe subpath from .../dev-spa to .../single (and .../dev-spa/client.../single/client on next). single names the scope precisely — author / dev-serve one devframe — matching the sibling /hub scope's naming style, where dev-spa read like an implementation detail. Source files, package.json exports, tsdown.config.ts entries, the alias.ts / tsconfig.base.json path maps, the Nuxt module.json name, and every doc/example reference move in lockstep; AGENTS.md's framework-adapter section documents the new name.

E. Plugin default export is now the factory, not a pre-built instance

Every built-in plugin used to eagerly construct one DevframeDefinition at module load (const xDevframe = createXDevframe(); export default xDevframe) and export that instance — so importing the package paid for an instance whether or not the consumer wanted the zero-config shape, and a host needing its own options discarded a second one. The default export is now the create<X>Devframe factory itself (export default createXDevframe); every consumer (examples, storybook, the hub-ui playground, each plugin's own cli.ts / spa/vite.config.ts / tests, the Next dynamic-import hosts) calls it explicitly. Documented as a new AGENTS.md convention so new plugins follow it from the start.

Breaking changes

  • The six plugins' /vite export subpath (a11yVitePlugin, …, ogVitePlugin) is gone.
  • @devframes/{vite,nuxt,next}/dev-spa.../single (and next's /dev-spa/client/single/client).
  • Each built-in plugin's default export changed from a DevframeDefinition to a () => DevframeDefinition factory.

All three are documented in docs/guide/migration-0.9.md (dedicated sections + overview-table rows).

Verification

  • pnpm lint && pnpm knip && pnpm test && pnpm typecheck && pnpm build — all green. tsnapi API snapshots that changed (the hub constants/initiate entries; the three adapters' renamed single entries; every plugin's index default-export shape) were regenerated deliberately with TSNAPI_ALLOW_BREAKING=1. knip.jsonc ignores the intentional named+default duplicates on each plugin's src/index.ts.
  • Ran examples/hub-vite end-to-end (vite, exercising viteDevframeHubinitHubnormalizeHubBase/DEVFRAMES_HUB_BASE, and every built-in plugin's factory default export): all nine plugin docks — including the untouched code-server/terminals wrappers — mounted and served 200 at /__devframes/<id>/, and /__devframes/__index.json listed every frame.

Created with the help of an agent.

@netlify

netlify Bot commented Aug 15, 2026

Copy link
Copy Markdown

Deploy Preview for devfra ready!

Name Link
🔨 Latest commit 307736c
🔍 Latest deploy log https://app.netlify.com/projects/devfra/deploys/6a825884c498d7000841f374
😎 Deploy Preview https://deploy-preview-238--devfra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

…m @devframes/hub/constants

Deletes three drift-prone hand-copy clusters:

- plugins/{a11y,assets,data-inspector,inspect,messages,og}/src/vite.ts
  — each an 8-25 line rename over devframeVite(def, options) from
  @devframes/vite/dev-spa, and the sole reason those plugins depended
  on @devframes/vite. Consumers now call devframeVite() directly
  against the plugin's default export; @devframes/vite moves to
  devDependencies where it's still needed for the plugin's own SPA
  dev config. plugin-code-server's codeServerVite and
  plugin-terminals's terminalsVite are unaffected — they compose a
  bridge + static plugin and build their devframe from options, which
  is real behaviour beyond delegation, not a mirror.

- DEVFRAMES_HUB_BASE hand-copied across packages/{vite,nuxt,next}'s
  hub-client wrappers (each commented "mirrors @devframes/hub's").
  Now defined once in the client-safe packages/hub/src/constants.ts
  and imported everywhere; packages/hub/src/node/initiate.ts
  re-exports it for existing importers of that entry.

- normalizeBase reimplemented ×4 with two different mechanisms (ufo
  vs hand-rolled). One canonical normalizeHubBase (ufo-based, hub
  already depends on ufo) now lives next to the constant and is used
  by @devframes/hub itself and all three framework adapters.

Also deletes packages/hub-ui/src/client/state/renderers.ts, a
30-line shim that only delegated to createDockRenderersContext; its
one call site now calls that directly.

Breaking: the six plugins' /vite export subpath is gone. Migration
documented in docs/guide/migration-0.9.md.

Created with the help of an agent.
…Vite

The migration note and each affected plugin's docs page now lead with
createPluginFromDevframe (@vitejs/devtools-kit/node) for a Vite DevTools
host, with devframeVite (@devframes/vite/dev-spa) as the DevTools-free
fallback — matching the existing framework/hub docs' stance that Vite
DevTools is the recommended integration path for a Vite app.

Created with the help of an agent.
@antfubot
antfubot force-pushed the plan-040-mirror-fringe branch from 23e4fb1 to 65a0347 Compare August 16, 2026 23:20
… their factory as default

Two related breaking changes to the framework adapter/plugin surface:

- @devframes/{vite,nuxt,next} rename their single-devframe subpath
  from .../dev-spa to .../single (and .../dev-spa/client to
  .../single/client on next) — 'single' names the scope precisely
  (author/dev-serve ONE devframe), matching the sibling '/hub' scope's
  naming style. Source files, package.json exports, tsdown entries,
  alias.ts/tsconfig.base.json path maps, and every doc/example
  reference are updated in lockstep; AGENTS.md's framework-adapter
  section documents the new name.

- Every built-in plugin (a11y, assets, code-server, data-inspector,
  git, inspect, messages, og, terminals) now exports its
  create<X>Devframe factory as the default export, not a pre-built
  DevframeDefinition instance constructed eagerly at module load.
  const xDevframe = createXDevframe(); export default xDevframe
  becomes export default createXDevframe — importing the module no
  longer pays for an instance nobody may want, and every consumer
  (examples, storybook, the hub-ui playground, each plugin's own
  cli.ts/spa vite.config.ts/tests) now calls the factory explicitly.
  Documented as a new AGENTS.md convention so new plugins follow it
  from the start.

tsnapi API snapshots regenerated deliberately (TSNAPI_ALLOW_BREAKING=1)
for both changes; knip.jsonc ignores the intentional named+default
duplicate export the second change introduces on every plugin's
src/index.ts.

Created with the help of an agent.
@antfubot antfubot changed the title feat!: remove per-plugin vite wrappers; export hub base constants from @devframes/hub/constants Aug 17, 2026
@antfu
antfu merged commit 3e6ef47 into main Aug 17, 2026
12 checks passed
antfubot added a commit that referenced this pull request Aug 17, 2026
Resolve dependency conflicts in plugins/inspect and plugins/messages
package.json: keep main's #238 layout (which moved @devframes/vite to
devDependencies) and drop the now-unused nostics dependency, since these
plugins reach nostics through devframe/utils/nostics.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants