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 feat!: delete the mirror fringe; rename adapters' /dev-spa to /single; plugins default-export their factory 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants