Skip to content

feat(core): resolve vite integration options within devtools - #549

Open
webfansplz wants to merge 4 commits into
mainfrom
refactor/vite-integration
Open

feat(core): resolve vite integration options within devtools#549
webfansplz wants to merge 4 commits into
mainfrom
refactor/vite-integration

Conversation

@webfansplz

Copy link
Copy Markdown
Member

Background

This PR addresses the configuration ownership and versioning concerns raised in this Vite core review comment.

The review identified two problems with the previous integration:

  1. DevTools options existed in both the Vite core devtools option and the explicit DevTools() plugin. Options available only on the plugin required users to register both configuration paths.
  2. Vite exposed the DevTools-owned ResolvedDevToolsConfig through ResolvedConfig.devtools. This meant additive resolved-config changes required a Vite release, and a newer installed DevTools runtime could receive the older config shape bundled with Vite.

As noted in the review, DevTools expected config.devtools to match the ResolvedDevToolsConfig from its currently installed version, but that assumption was incorrect: the value and type were produced by the DevTools version used when Vite was built.

This PR moves complete configuration ownership back into the installed @vitejs/devtools version:

  • Vite passes only the raw user options and its resolved host.
  • DevTools owns DevToolsConfig, defaults, normalization, and ResolvedDevToolsConfig.
  • DevTools no longer casts Vite's config.devtools to its own resolved type.

This PR implements the DevTools side of that boundary and is paired with the corresponding Vite core change.

Approach

Vite now passes a minimal integration payload containing its resolved host and the raw user options:

DevToolsIntegration({
  config,
  devtools: {
    host,
    options,
  },
})

The installed DevTools version normalizes the options itself:

normalizeDevToolsConfig(
  devtools.options,
  devtools.host,
)

The resolved configuration remains private to DevTools and is forwarded to serve, build, authentication, and standalone contexts as needed.

Changes

Resolve integration options inside DevTools

DevToolsIntegration() and runDevTools() now receive an explicit DevToolsIntegrationConfig:

interface DevToolsIntegrationConfig {
  host: string
  options: boolean | DevToolsConfig | undefined
}

The installed DevTools version now:

  • determines whether DevTools is enabled;
  • applies serve, build, or all;
  • resolves configuration defaults;
  • selects enabled Vite environments;
  • configures Rolldown build analysis;
  • starts standalone DevTools with the resolved options.

DevTools no longer reads or casts config.devtools from Vite as its own resolved type.

Keep resolved configuration internal

Resolved DevTools configuration is associated with each ViteDevToolsNodeContext internally.

Authentication and server setup now read from the internal resolved config instead of:

context.viteConfig.devtools.config

This includes:

  • clientAuth;
  • clientAuthTokens;
  • allowedOrigins.

A default resolved config preserves existing behavior for standalone mode and manual DevTools() usage.

Forward resolved options to standalone DevTools

The build integration now forwards its resolved configuration when starting standalone DevTools.

The standalone startup flow was moved into startDevTools() so the integration can pass:

  • the resolved project root;
  • server and CLI options;
  • authentication settings;
  • UI configuration;
  • the resolved DevTools config associated with the standalone context.

This keeps the standalone UI and authentication behavior consistent with the Vite core configuration.

Support UI and build options through the core config

DevToolsConfig now includes the user-facing UI and build options used by the Vite integration:

  • builtinDevTools;
  • branding;
  • embeddedVisibility;
  • dockPreferences;
  • build.withApp;
  • build.outDir.

This allows users to keep a single standard Vite configuration:

import { defineConfig } from 'vite'

export default defineConfig({
  devtools: {
    apply: 'serve',
    clientAuth: false,
    embeddedVisibility: 'passive',
  },
})

Users no longer need to register DevTools() only to configure these options.

Keep the public plugin API clean

The public manual API remains:

DevTools(options?: DevToolsOptions)

Resolved integration state is not exposed as another public plugin option.

Internal integration setup uses:

createDevToolsPlugins(options, resolvedConfig)

to forward resolved state without expanding the public DevTools() API.

Add a stable manual-plugin entry

The public DevTools() factory now includes an inert entry plugin:

{ name: 'vite:devtools' }

Only the public manual entry includes this marker. Internal and standalone plugin creation use createDevToolsPlugins() directly.

The companion Vite implementation uses this stable entry to detect when a project configures both:

plugins: [DevTools()]

and:

devtools: {}

The conflict is detected and reported by Vite core. DevTools does not inspect, remove, or replace plugins in Vite's plugin list.

Keep the config declaration lightweight

The public DevTools UI configuration types are defined locally instead of referencing the complete @devframes/hub-ui declaration graph.

This keeps @vitejs/devtools/config suitable for Vite's optional-peer type bridge without pulling h3, crossws, srvx, or other runtime declarations into Vite consumers.

Update documentation

The documentation now uses the Vite core option directly and no longer combines it with an explicit DevTools() plugin:

import { defineConfig } from 'vite'

export default defineConfig({
  devtools: {
    clientAuthTokens: ['your-trusted-token'],
  },
})

Companion Vite core change

The paired Vite change keeps ResolvedConfig.devtools as a minimal Vite-owned integration state:

interface ResolvedDevToolsIntegration {
  enabled: boolean
  host: string
  options: boolean | DevToolsConfig | undefined
}

Vite obtains the complete config type from the installed optional peer through an internal declaration bridge:

// vite/types/internal/devtoolsOptions.d.ts

// @ts-ignore `@vitejs/devtools` may not be installed
export type { DevToolsConfig } from '@vitejs/devtools/config'

This follows the same pattern Vite already uses for optional Less, Sass, Stylus, esbuild, and Terser types.

As a result:

  • Vite does not bundle a frozen snapshot of DevTools config fields.
  • TypeScript resolves the config type from the installed DevTools version.
  • Users continue importing defineConfig from vite.
  • Projects without @vitejs/devtools remain unaffected.
  • Runtime normalization and config typing come from the same DevTools version.

Vite core also rejects configurations that enable both the core option and the explicit plugin. If the core option is omitted or has enabled: false, manual DevTools() usage continues to work.

Behavior

Core integration

export default defineConfig({
  devtools: {
    apply: 'serve',
  },
})

Vite forwards the raw options and host. DevTools owns normalization and integration behavior.

Manual plugin only

Existing manual usage remains supported:

export default defineConfig({
  plugins: [
    DevTools({
      embeddedVisibility: 'passive',
    }),
  ],
})

This continues to work when the Vite core devtools option is omitted or explicitly disabled.

Duplicate configuration

The companion Vite change rejects configurations that enable both the manual plugin and the core option:

export default defineConfig({
  plugins: [DevTools()],
  devtools: true,
})

This avoids ambiguous precedence and duplicate serve or build plugins.

Breaking changes

The integration entry points now require an explicit DevTools integration config:

DevToolsIntegration({ config, devtools })
runDevTools(builder, devtools)

This is part of the v0.7 integration contract and is not intended to support the previous Vite integration calling convention.

The public manual DevTools() API remains backward compatible.

Copilot AI lite review requested due to automatic review settings August 25, 2026 04:47
@webfansplz
webfansplz marked this pull request as draft August 25, 2026 04:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR re-establishes DevTools as the sole owner of DevTools option normalization/resolution in the Vite integration, so Vite forwards only raw user options + host while the installed @vitejs/devtools version produces and consumes ResolvedDevToolsConfig internally. This avoids Vite exposing or freezing DevTools’ resolved config shape via ResolvedConfig.devtools, and keeps standalone/build/auth behavior consistent with the DevTools version actually installed.

Changes:

  • Introduces an explicit DevToolsIntegrationConfig { host, options } boundary and moves option normalization (normalizeDevToolsConfig) into DevTools’ integration entrypoints.
  • Stores resolved DevTools config internally per ViteDevToolsNodeContext (WeakMap) and updates auth/origin handling to read from that internal resolved config instead of config.devtools casts.
  • Refactors standalone startup into startDevTools() so build integration can forward resolved config, and updates docs to configure UI/build options via Vite’s core devtools option.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/core/src/node/ui.ts Moves UI option types to a lightweight local type module and adjusts branding resolution typing.
packages/core/src/node/start.ts Extracts standalone startup server/hub boot into startDevTools() and allows forwarding resolved config.
packages/core/src/node/standalone.ts Allows standalone to receive resolved config and uses internal plugin creation helpers.
packages/core/src/node/server.ts Reads allowedOrigins from internal resolved config instead of config.devtools casting.
packages/core/src/node/resolved-config.ts Adds internal storage (WeakMap) for ResolvedDevToolsConfig per node context plus defaults.
packages/core/src/node/plugins/server.ts Threads optional ResolvedDevToolsConfig into createDevToolsContext() for serve integration.
packages/core/src/node/plugins/integration.ts Changes integration contract to accept { host, options }, normalizes inside DevTools, and forwards resolved config into build/standalone flows.
packages/core/src/node/plugins/index.ts Adds stable manual-plugin marker (vite:devtools), introduces createDevToolsPlugins(), and maps resolved config to plugin options.
packages/core/src/node/plugins/build.ts Threads resolved config into build-time context creation for static snapshot generation.
packages/core/src/node/plugins/tests/index.test.ts Adds coverage for the manual-plugin marker being present only on the public entry.
packages/core/src/node/plugin-options.ts Defines lightweight public option types for UI/build without pulling full hub-ui type graphs.
packages/core/src/node/context.ts Attaches internal resolved config to the created node context.
packages/core/src/node/config.ts Extends DevToolsConfig with the new user-facing plugin/core options and defines normalization defaults.
packages/core/src/node/cli-commands.ts Delegates start() to the new startDevTools() implementation.
packages/core/src/node/auth-handler.ts Reads auth settings from internal resolved config instead of context.viteConfig.devtools.
packages/core/src/node/tests/integration.test.ts Updates integration tests for the new config boundary and verifies resolved config forwarding to standalone start.
packages/core/src/node/tests/context-auth.test.ts Updates auth-context tests to pass normalized resolved config explicitly.
packages/core/src/integration.ts Updates exported integration entrypoints/signatures to accept the explicit DevToolsIntegrationConfig.
docs/guide/index.md Updates guidance to configure UI/build options via Vite’s devtools option rather than requiring the DevTools() plugin.
docs/errors/DTK0013.md Updates auth-token documentation example to use Vite’s devtools option directly.
Suppressed comments (1)

docs/guide/index.md:99

  • Same as above: the dockPreferences example omits apply, which defaults to 'all'. If the intent is only to configure the embedded dock for vite dev, include apply: 'serve' to avoid enabling build-time integration unintentionally.
export default defineConfig({
  devtools: {
    dockPreferences: {
      defaultMode: 'edge',
      defaultPosition: 'bottom',
    },
  },
})

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/guide/index.md
@pkg-pr-new

pkg-pr-new Bot commented Aug 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vitejs/devtools

npm i https://pkg.pr.new/@vitejs/devtools@549

@vitejs/devtools-kit

npm i https://pkg.pr.new/@vitejs/devtools-kit@549

@vitejs/devtools-oxc

npm i https://pkg.pr.new/@vitejs/devtools-oxc@549

@vitejs/devtools-rolldown

npm i https://pkg.pr.new/@vitejs/devtools-rolldown@549

@vitejs/devtools-vite

npm i https://pkg.pr.new/@vitejs/devtools-vite@549

@vitejs/devtools-vitest

npm i https://pkg.pr.new/@vitejs/devtools-vitest@549

commit: 023c754

@webfansplz

Copy link
Copy Markdown
Member Author

@antfu I’ve updated the Vite core integration in vitejs/vite#23333 to match the latest implementation in this PR. Could you help review it?

@webfansplz
webfansplz marked this pull request as ready for review August 25, 2026 05:51
@webfansplz
webfansplz requested a review from antfu August 25, 2026 05:52
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