From 3fc40dac0db9f5176231b304084cbfda99d763be Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Fri, 21 Aug 2026 05:33:09 +0000 Subject: [PATCH 1/4] feat: serve the client UI as devframe remote assets via @nuxt/devtools-assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The built client SPA no longer ships inside the @nuxt/devtools tarball. It is published as the version-locked @nuxt/devtools-assets package and declared as a devframe remote-assets source: a locally installed copy is served with zero network (monorepo, e2e, offline/air-gapped installs), otherwise files stream on demand from a CDN mirror of npm into an on-disk cache. The client build is now mount-path portable — hash routing plus relative asset URLs with a location-derived runtime baseURL — so devframe's ctx.views.hostStatic serves the directory verbatim and the serve-time __NUXT_DEVTOOLS_BASE__ index.html rewrite is gone. Connection meta is mounted on the client base like the hub does for its devframes, so a direct navigation to /__nuxt_devtools__/client/ now discovers the RPC endpoint too. A new clientAssets module option overrides the source: a directory path serves a local build, false skips mounting (used by the dogfooding client app itself). --- knip.jsonc | 7 ++ packages/devtools-assets/README.md | 18 +++ packages/devtools-assets/package.json | 23 ++++ packages/devtools-kit/src/_types/options.ts | 16 +++ packages/devtools/client/app.vue | 4 +- packages/devtools/client/nuxt.config.ts | 14 +++ packages/devtools/package.json | 9 +- packages/devtools/scripts/copy-client.mjs | 45 +++++++- packages/devtools/src/dirs.ts | 4 +- packages/devtools/src/module-main.ts | 119 +++++++++++++------- pnpm-lock.yaml | 22 +++- turbo.json | 16 +++ 12 files changed, 243 insertions(+), 54 deletions(-) create mode 100644 packages/devtools-assets/README.md create mode 100644 packages/devtools-assets/package.json diff --git a/knip.jsonc b/knip.jsonc index e85e546c5a..2aabd1947d 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -63,6 +63,13 @@ "playground/nuxt.config.ts" ] }, + // The assets package is never imported: it only satisfies the runtime + // package resolution of the client UI's remote-assets source (see + // `resolveClientAssetsSource` in `src/module-main.ts`), keeping the + // monorepo/e2e flows serving the local build with zero network. + "ignoreDependencies": [ + "@nuxt/devtools-assets" + ], "entry": [ "scripts/*.mjs", "src/webcomponents/scripts/*.ts", diff --git a/packages/devtools-assets/README.md b/packages/devtools-assets/README.md new file mode 100644 index 0000000000..7367305e77 --- /dev/null +++ b/packages/devtools-assets/README.md @@ -0,0 +1,18 @@ +# @nuxt/devtools-assets + +Pre-built client UI assets for [Nuxt DevTools](https://github.com/nuxt/devtools). + +`@nuxt/devtools` does not bundle its client UI. Instead, it declares this +version-locked package as [devframe remote assets](https://devfra.me/guide/client-assets.html#remote-assets): +files are resolved from a locally installed copy of this package when present, +otherwise streamed on demand from a CDN mirror of npm and cached on disk. + +You normally don't need to install this package. For offline or air-gapped +environments, install it explicitly so the UI is served with zero network: + +```sh +npm install -D @nuxt/devtools-assets +``` + +Keep its version in lockstep with `@nuxt/devtools` (it is published together +with each release). diff --git a/packages/devtools-assets/package.json b/packages/devtools-assets/package.json new file mode 100644 index 0000000000..ea784808a0 --- /dev/null +++ b/packages/devtools-assets/package.json @@ -0,0 +1,23 @@ +{ + "name": "@nuxt/devtools-assets", + "type": "module", + "version": "4.0.0-alpha.12", + "description": "Pre-built client UI assets for Nuxt DevTools, resolved on demand.", + "license": "MIT", + "homepage": "https://devtools.nuxt.com", + "repository": { + "type": "git", + "url": "git+https://github.com/nuxt/devtools.git", + "directory": "packages/devtools-assets" + }, + "exports": { + "./package.json": "./package.json" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "pnpm -C ../devtools run build:client", + "prepack": "pnpm build" + } +} diff --git a/packages/devtools-kit/src/_types/options.ts b/packages/devtools-kit/src/_types/options.ts index cad1c5dd19..eff7387e28 100644 --- a/packages/devtools-kit/src/_types/options.ts +++ b/packages/devtools-kit/src/_types/options.ts @@ -74,6 +74,22 @@ export interface ModuleOptions { */ iframeProps?: Record + /** + * Where the DevTools client UI (the SPA rendered in the DevTools frame) is + * served from. + * + * - `undefined` (default): resolve the version-locked `@nuxt/devtools-assets` + * package — a locally installed copy when present (zero network; install + * it explicitly for offline or air-gapped use), otherwise streamed on + * demand from a CDN mirror of npm and cached on disk. + * - a directory path: serve a local build of the client UI from that + * directory. + * - `false`: don't mount the client UI at all. Used by the DevTools client + * app itself, whose dev server already serves the live client on the same + * base path. + */ + clientAssets?: string | false + /** * Experimental features */ diff --git a/packages/devtools/client/app.vue b/packages/devtools/client/app.vue index 2922efcbc2..9b2f0db41a 100644 --- a/packages/devtools/client/app.vue +++ b/packages/devtools/client/app.vue @@ -32,7 +32,9 @@ useHead({ { rel: 'icon', type: 'image/svg+xml', - href: '/nuxt.svg', + // Relative: resolves against the document URL, which is always the + // client's mount root (the production build uses hash routing). + href: './nuxt.svg', }, ], }) diff --git a/packages/devtools/client/nuxt.config.ts b/packages/devtools/client/nuxt.config.ts index 7891880ca3..9aba708451 100644 --- a/packages/devtools/client/nuxt.config.ts +++ b/packages/devtools/client/nuxt.config.ts @@ -61,10 +61,24 @@ export default defineNuxtConfig({ // We set a placeholder for the middleware to be replaced with the correct base URL baseURL: '/__NUXT_DEVTOOLS_BASE__/', }, + router: { + options: { + // Hash routing keeps the document URL at the mount root, so relative + // asset URLs always resolve regardless of the active tab route. + hashMode: true, + }, + }, }, ssr: false, + devtools: { + // This app *is* the DevTools client — its dev server serves the live + // client on the very base path the module would otherwise mount the + // built assets on. + clientAssets: false, + }, + app: { baseURL: '/__nuxt_devtools__/client/', }, diff --git a/packages/devtools/package.json b/packages/devtools/package.json index dd42a062a9..ec96eb5baa 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -21,8 +21,8 @@ "dist" ], "scripts": { - "build": "pnpm dev:prepare && pnpm build:module && pnpm build:client", - "build:client": "nuxi generate client && node scripts/copy-client.mjs", + "build": "pnpm dev:prepare && pnpm build:module", + "build:client": "pnpm dev:prepare && nuxi generate client && node scripts/copy-client.mjs", "build:module": "nuxt-build-module build", "stub": "nuxt-build-module build --stub", "dev": "nuxi dev client", @@ -32,6 +32,7 @@ "prepack": "pnpm build" }, "peerDependencies": { + "@nuxt/devtools-assets": "workspace:*", "@nuxt/kit": "^4.0.0-0 || ^5.0.0-0", "nitro": "^3.0.0-0", "nitropack": "^2.0.0", @@ -39,6 +40,9 @@ "vite-plugin-inspect": "catalog:prod" }, "peerDependenciesMeta": { + "@nuxt/devtools-assets": { + "optional": true + }, "nitro": { "optional": true }, @@ -81,6 +85,7 @@ "@iconify-json/ri": "catalog:icons", "@iconify-json/simple-icons": "catalog:icons", "@iconify-json/tabler": "catalog:icons", + "@nuxt/devtools-assets": "workspace:*", "@nuxt/kit": "catalog:prod", "@parcel/watcher": "catalog:buildtools", "@types/markdown-it-link-attributes": "catalog:types", diff --git a/packages/devtools/scripts/copy-client.mjs b/packages/devtools/scripts/copy-client.mjs index 01ea5ca4ed..882545a08a 100644 --- a/packages/devtools/scripts/copy-client.mjs +++ b/packages/devtools/scripts/copy-client.mjs @@ -1,3 +1,44 @@ -import { cpSync } from 'node:fs' +// Copy the generated client SPA into the `@nuxt/devtools-assets` package and +// make it mount-path portable, so devframe can serve the directory verbatim +// at any base (a local install, the on-disk cache, or its CDN back-proxy) — +// see https://devfra.me/guide/client-assets.html. +import { cpSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' -cpSync('client/.output/public', 'dist/client', { recursive: true }) +const SOURCE = 'client/.output/public' +const TARGET = '../devtools-assets/dist' + +// The client is generated with the `/__NUXT_DEVTOOLS_BASE__/` placeholder as +// its base URL (see `client/nuxt.config.ts` — Nuxt cannot generate with a +// relative base directly). Rewrite each HTML shell so it works at any mount +// point: +// +// 1. The inline runtime config's `baseURL` becomes a `location`-derived +// expression. The client uses hash routing in production, so +// `location.pathname` is always the mount path (possibly ending in +// `index.html`, hence stripping the trailing filename). +// 2. Every other placeholder occurrence (asset `href`/`src`, importmap) +// becomes `./`, relative to the document — which, per 1., is always the +// mount root. +const PLACEHOLDER = '/__NUXT_DEVTOOLS_BASE__/' +const RUNTIME_CONFIG_BASE_RE = /baseURL:"\/__NUXT_DEVTOOLS_BASE__\/"/ +const RUNTIME_CONFIG_BASE_REPLACEMENT = 'baseURL:location.pathname.replace(/[^/]*$/,"")' + +rmSync(TARGET, { recursive: true, force: true }) +cpSync(SOURCE, TARGET, { recursive: true }) + +const htmlFiles = readdirSync(TARGET).filter(file => file.endsWith('.html')) +if (htmlFiles.length === 0) + throw new Error(`No HTML shell found in ${TARGET} — did \`nuxi generate client\` run?`) + +for (const file of htmlFiles) { + const path = join(TARGET, file) + let html = readFileSync(path, 'utf-8') + if (!RUNTIME_CONFIG_BASE_RE.test(html)) + throw new Error(`Expected the inline runtime-config \`baseURL:"${PLACEHOLDER}"\` in ${file} — Nuxt's serialization may have changed; update copy-client.mjs.`) + // Order matters: rewrite the runtime-config occurrence first, then the + // remaining (asset URL) occurrences. + html = html.replace(RUNTIME_CONFIG_BASE_RE, RUNTIME_CONFIG_BASE_REPLACEMENT) + html = html.replaceAll(PLACEHOLDER, './') + writeFileSync(path, html) +} diff --git a/packages/devtools/src/dirs.ts b/packages/devtools/src/dirs.ts index 2942744183..f0500d833d 100644 --- a/packages/devtools/src/dirs.ts +++ b/packages/devtools/src/dirs.ts @@ -2,7 +2,5 @@ import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' export const packageDir = resolve(fileURLToPath(import.meta.url), '../..') -export const distDir = resolve(fileURLToPath(import.meta.url), '..') -export const runtimeDir = resolve(distDir, 'runtime') -export const clientDir = resolve(distDir, 'client') +export const runtimeDir = resolve(fileURLToPath(import.meta.url), '../runtime') diff --git a/packages/devtools/src/module-main.ts b/packages/devtools/src/module-main.ts index 9c658b7ec2..9e854bdaac 100644 --- a/packages/devtools/src/module-main.ts +++ b/packages/devtools/src/module-main.ts @@ -1,11 +1,9 @@ import type { PluginWithDevTools } from '@vitejs/devtools-kit' -import type { ServerResponse } from 'node:http' +import type { StaticAssetsSource } from 'devframe' import type { Nuxt } from 'nuxt/schema' import type { Plugin } from 'vite' import type { ModuleOptions, NuxtDevToolsOptions } from './types' import type { AnyNitroConfig } from './utils/nitro-compat' -import { existsSync } from 'node:fs' -import fs from 'node:fs/promises' import os from 'node:os' import { deprecate, NUXT_DEVTOOLS_GROUP_ID } from '@nuxt/devtools-kit' import { addImports, addPlugin, addTemplate, addVitePlugin, extendViteConfig, logger } from '@nuxt/kit' @@ -13,15 +11,48 @@ import { colors } from 'consola/utils' import { serveStaticNodeMiddleware } from 'devframe/utils/serve-static' import { join } from 'pathe' import { searchForWorkspaceRoot } from 'vite' -import { version } from '../package.json' +import { peerDependencies, version } from '../package.json' import { createDefaultTabOptions, setServerTasksEnabledByDefault } from './constant' -import { clientDir, packageDir, runtimeDir } from './dirs' +import { packageDir, runtimeDir } from './dirs' import { setupRPC } from './server-rpc' import { skipInSSR } from './server-rpc/skip-in-ssr' import { readLocalOptions } from './utils/local-options' const MULTIPLE_SLASHES_RE = /\/+/g -const DEVTOOLS_BASE_RE = /\/__NUXT_DEVTOOLS_BASE__\//g + +/** + * The published package holding the built client UI, version-locked to this + * package and declared as an optional peer dependency. Served as devframe + * remote assets: a locally installed copy wins (zero network — the monorepo + * and air-gapped installs), then the on-disk cache, then a CDN mirror of npm. + */ +const ASSETS_PACKAGE = '@nuxt/devtools-assets' + +/** + * Resolve the `StaticAssetsSource` for the client UI, honoring the + * `clientAssets` module option (`false` = don't mount, a string = serve that + * local directory). + * + * Nightly releases rename workspace packages through `npm:-nightly@` + * alias ranges (see `scripts/bump-nightly.ts`), so the real assets package + * name is read back from our own peer-dependency entry — a nightly build then + * fetches its matching nightly assets. + */ +function resolveClientAssetsSource(options: ModuleOptions): StaticAssetsSource | undefined { + if (options.clientAssets === false) + return undefined + if (typeof options.clientAssets === 'string') + return options.clientAssets + const range: string = (peerDependencies as Record)[ASSETS_PACKAGE] ?? '' + const name = range.startsWith('npm:') + ? range.slice('npm:'.length, range.lastIndexOf('@')) + : ASSETS_PACKAGE + return { + package: name, + version, + resolveFrom: import.meta.url, + } +} export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { // Disable in test mode @@ -85,6 +116,18 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { ) } + const ROUTE_PATH = `${nuxt.options.app.baseURL || '/'}/__nuxt_devtools__`.replace(MULTIPLE_SLASHES_RE, '/') + const ROUTE_CLIENT = `${ROUTE_PATH}/client` + const ROUTE_ANALYZE = `${ROUTE_PATH}/analyze` + + const clientAssetsSource = resolveClientAssetsSource(options) + // Where the client UI lives from the browser's point of view. With + // `clientAssets: false` the app under development *is* the client (the + // dogfooding `nuxi dev client` flow), already served live on its own base. + const clientUrl = clientAssetsSource + ? `${ROUTE_CLIENT}/` + : `${nuxt.options.app.baseURL || '/'}/`.replace(MULTIPLE_SLASHES_RE, '/') + const DevTools = await import('@vitejs/devtools').then(r => r.DevTools()) addVitePlugin(DevTools) @@ -105,6 +148,33 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { // setup callback too, and would otherwise create a second, inert // group + hub member. See `skipInSSR`. if (!skipInSSR(ctx)) { + if (clientAssetsSource) { + // The client SPA ships relative asset URLs (mount-path portable), + // which only resolve on the directory URL — send `…/client` to + // `…/client/` before the static mount sees it. + ctx.viteServer?.middlewares.use((req, res, next) => { + const [pathname = '', search = ''] = (req.url ?? '').split('?') + if (pathname !== ROUTE_CLIENT) + return next() + res.statusCode = 302 + res.setHeader('Location', `${ROUTE_CLIENT}/${search ? `?${search}` : ''}`) + res.end() + }) + // Serve `__connection.json` on the client's base (registered + // before the static mount so its SPA fallback doesn't swallow the + // route) — the same mounting the hub does for each devframe it + // installs. Inside the hub's iframe the injected parent connection + // wins; this makes a *direct* navigation to the client discover + // the RPC endpoint too. + if (ctx.host.mountConnectionMeta) + await ctx.host.mountConnectionMeta(`${ROUTE_CLIENT}/`) + // devframe's own static hosting: a local directory is served + // directly; the default remote source resolves per request from a + // locally installed `@nuxt/devtools-assets`, the on-disk cache, or + // its CDN back-proxy (https://devfra.me/guide/client-assets.html). + ctx.views.hostStatic(ROUTE_CLIENT, clientAssetsSource) + } + // Register the `Nuxt` group and a single **shared-frame anchor** // iframe. The anchor owns one kept-alive iframe (its `frameId`); the // client app ships a `devframe:frame-nav` postMessage shim that @@ -116,7 +186,7 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { id: NUXT_DEVTOOLS_GROUP_ID, type: 'group', title: 'Nuxt', - icon: '/__nuxt_devtools__/client/nuxt.svg', + icon: `${clientUrl}nuxt.svg`, category: 'framework', defaultOrder: -2000, defaultChildId: 'nuxt:devtools', @@ -135,8 +205,8 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { id: 'nuxt:devtools', type: 'iframe', title: 'Nuxt DevTools', - icon: '/__nuxt_devtools__/client/nuxt.svg', - url: '/__nuxt_devtools__/client/', + icon: `${clientUrl}nuxt.svg`, + url: clientUrl, groupId: NUXT_DEVTOOLS_GROUP_ID, frameId: 'nuxt:devtools', subTabs: { protocol: 'postmessage' }, @@ -218,8 +288,6 @@ window.__NUXT_DEVTOOLS_TIME_METRIC__.appInit = Date.now() connectDevToolsKit = _connectDevToolsKit - const clientDirExists = existsSync(clientDir) - extendViteConfig((config) => { config.server ||= {} config.server.fs ||= {} @@ -241,40 +309,11 @@ window.__NUXT_DEVTOOLS_TIME_METRIC__.appInit = Date.now() from: join(runtimeDir, 'use-nuxt-devtools'), }) - const ROUTE_PATH = `${nuxt.options.app.baseURL || '/'}/__nuxt_devtools__`.replace(MULTIPLE_SLASHES_RE, '/') - const ROUTE_CLIENT = `${ROUTE_PATH}/client` - const ROUTE_ANALYZE = `${ROUTE_PATH}/analyze` - // TODO: Use WS from nitro server when possible nuxt.hook('vite:serverCreated', (server) => { const devtoolsAnalyzeDir = join(nuxt.options.rootDir, 'node_modules/.cache/nuxt-devtools/analyze') server.middlewares.use(ROUTE_ANALYZE, serveStaticNodeMiddleware(devtoolsAnalyzeDir, { single: false })) - - // Serve the front end in production - if (clientDirExists) { - const indexHtmlPath = join(clientDir, 'index.html') - const indexContent = fs.readFile(indexHtmlPath, 'utf-8') - const handleStatic = serveStaticNodeMiddleware(clientDir, { - single: false, - }) - // We replace the base URL in the index.html based on user's settings - const handleIndex = async (res: ServerResponse) => { - res.setHeader('Content-Type', 'text/html') - res.statusCode = 200 - res.write((await indexContent).replace(DEVTOOLS_BASE_RE, `${ROUTE_CLIENT}/`)) - res.end() - } - server.middlewares.use(ROUTE_CLIENT, (req, res) => { - // Serve the (base-rewritten) SPA index for the root document, ignoring - // any query string, which sirv would otherwise serve as a raw, - // un-rewritten `index.html`. - const pathname = (req.url || '/').split('?')[0] - if (pathname === '/' || pathname === '') - return handleIndex(res) - return handleStatic(req, res, () => handleIndex(res)) - }) - } }) await import('./integrations/plugin-metrics').then(({ setup }) => setup(ctx)) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0017de576d..568723b344 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -543,6 +543,9 @@ importers: '@iconify-json/tabler': specifier: catalog:icons version: 1.2.38 + '@nuxt/devtools-assets': + specifier: workspace:* + version: link:../devtools-assets '@nuxt/kit': specifier: catalog:prod version: '@nuxt/kit-nightly@5.0.0-29755283.b4586ef2(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)))' @@ -670,6 +673,8 @@ importers: specifier: catalog:frontend version: 3.0.5(vue@3.5.41(typescript@6.0.3)) + packages/devtools-assets: {} + packages/devtools-kit: dependencies: nostics: @@ -1682,17 +1687,20 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nuxt/cli-nightly@4.0.0-20260818-221829-0bb4e52': - resolution: {integrity: sha512-OgxCEcnOsJQnedBlZqAqH/yEefBX1wqQoBhI6NKwGx6VX1SGImUniBYdS6L7HWOCMAcHSYbfUkQs8U+9ib/lqw==} + '@nuxt/cli-nightly@4.0.0-20260819-230108-a36ce44': + resolution: {integrity: sha512-kjyR2ilcNGrMar1UsavU2KOMDIE/c9i7j83gLLENHAoWduSGtFaeW2Y7+KwmUfvTqFS1NtfA4JTZBNs/ZsSBJA==} engines: {node: ^22.19.0 || ^24.11.0 || >=26.0.0} hasBin: true peerDependencies: + '@nuxt/docs': ^4.0.0 '@nuxt/schema': ^4.5.2 jiti: ^2.7.0 oxc-parser: '>=0.143.0' rolldown: ^1.0.0-rc.16 || ^1.0.0 serve: ^14.2.6 peerDependenciesMeta: + '@nuxt/docs': + optional: true '@nuxt/schema': optional: true jiti: @@ -8281,7 +8289,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@nuxt/cli-nightly@4.0.0-20260818-221829-0bb4e52(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.131.0)(rolldown@1.2.4)': + '@nuxt/cli-nightly@4.0.0-20260819-230108-a36ce44(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.131.0)(rolldown@1.2.4)': dependencies: '@bomb.sh/tab': 0.0.22(cac@7.0.0)(citty@0.2.2) '@clack/prompts': 1.7.0 @@ -8316,7 +8324,7 @@ snapshots: - cac - commander - '@nuxt/cli-nightly@4.0.0-20260818-221829-0bb4e52(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.143.0)(rolldown@1.2.4)': + '@nuxt/cli-nightly@4.0.0-20260819-230108-a36ce44(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.143.0)(rolldown@1.2.4)': dependencies: '@bomb.sh/tab': 0.0.22(cac@7.0.0)(citty@0.2.2) '@clack/prompts': 1.7.0 @@ -13128,7 +13136,7 @@ snapshots: nuxt-nightly@5.0.0-29755283.b4586ef2(@oxc-project/types@0.144.0)(@parcel/watcher@2.6.0)(@types/node@26.2.0)(@vitejs/devtools-kit@0.5.1(cac@7.0.0)(crossws@0.4.10(srvx@0.11.22))(srvx@0.11.22)(vite@8.2.1))(@vitejs/devtools@0.5.1)(@vue/compiler-sfc@3.5.41)(autoprefixer@10.5.4(postcss@8.5.26))(cac@7.0.0)(cssnano@7.1.9(postcss@8.5.26))(db0@0.3.4)(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.8.1(jiti@2.7.0)(supports-color@10.2.2))(giget@3.3.1)(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(nuxt-nightly@5.0.0-29755283.b4586ef2)(optionator@0.9.4)(oxc-parser@0.131.0)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.4)(rollup@4.62.4))(rollup@4.62.4)(terser@5.50.0)(tsx@4.23.12)(typescript@6.0.3)(vite@8.2.1)(vue-tsc@3.3.10(typescript@6.0.3))(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.5.8(esbuild@0.28.2)(magicast@0.5.4)(oxc-parser@0.131.0)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20260818-221829-0bb4e52(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.131.0)(rolldown@1.2.4)' + '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20260819-230108-a36ce44(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.131.0)(rolldown@1.2.4)' '@nuxt/devtools': link:packages/devtools '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29755283.b4586ef2(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.131.0)(rolldown@1.2.4)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)))' '@nuxt/nitro-server': '@nuxt/nitro-server-nightly@5.0.0-29755283.b4586ef2(@oxc-project/types@0.144.0)(@vitejs/devtools-kit@0.5.1(cac@7.0.0)(crossws@0.4.10(srvx@0.11.22))(srvx@0.11.22)(vite@8.2.1))(chokidar@5.0.0)(db0@0.3.4)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1(supports-color@10.2.2))(jiti@2.7.0)(lightningcss@1.33.0)(magicast@0.5.4)(nuxt-nightly@5.0.0-29755283.b4586ef2)(ofetch@2.0.0-alpha.3)(oxc-parser@0.131.0)(rolldown@1.2.4)(rollup@4.62.4)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)))(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))' @@ -13206,6 +13214,7 @@ snapshots: - '@libsql/client' - '@netlify/blobs' - '@netlify/runtime' + - '@nuxt/docs' - '@oxc-project/types' - '@pinia/colada' - '@planetscale/database' @@ -13273,7 +13282,7 @@ snapshots: nuxt-nightly@5.0.0-29755283.b4586ef2(@oxc-project/types@0.144.0)(@parcel/watcher@2.6.0)(@types/node@26.2.0)(@vitejs/devtools-kit@0.5.1(cac@7.0.0)(crossws@0.4.10(srvx@0.11.22))(srvx@0.11.22)(vite@8.2.1))(@vitejs/devtools@0.5.1)(@vue/compiler-sfc@3.5.41)(autoprefixer@10.5.4(postcss@8.5.26))(cac@7.0.0)(cssnano@7.1.9(postcss@8.5.26))(db0@0.3.4)(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.8.1(jiti@2.7.0)(supports-color@10.2.2))(giget@3.3.1)(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(nuxt-nightly@5.0.0-29755283.b4586ef2)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.4)(rollup@4.62.4))(rollup@4.62.4)(terser@5.50.0)(tsx@4.23.12)(typescript@6.0.3)(vite@8.2.1)(vue-tsc@3.3.10(typescript@6.0.3))(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.5.8(esbuild@0.28.2)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20260818-221829-0bb4e52(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.143.0)(rolldown@1.2.4)' + '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20260819-230108-a36ce44(@nuxt/schema-nightly@5.0.0-29755283.b4586ef2)(cac@7.0.0)(jiti@2.7.0)(oxc-parser@0.143.0)(rolldown@1.2.4)' '@nuxt/devtools': link:packages/devtools '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29755283.b4586ef2(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)))' '@nuxt/nitro-server': '@nuxt/nitro-server-nightly@5.0.0-29755283.b4586ef2(@oxc-project/types@0.144.0)(@vitejs/devtools-kit@0.5.1(cac@7.0.0)(crossws@0.4.10(srvx@0.11.22))(srvx@0.11.22)(vite@8.2.1))(chokidar@5.0.0)(db0@0.3.4)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1(supports-color@10.2.2))(jiti@2.7.0)(lightningcss@1.33.0)(magicast@0.5.4)(nuxt-nightly@5.0.0-29755283.b4586ef2)(ofetch@2.0.0-alpha.3)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)))(vite@8.2.1)(webpack@5.109.2(cssnano@7.1.9(postcss@8.5.26))(csso@5.0.5)(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))' @@ -13351,6 +13360,7 @@ snapshots: - '@libsql/client' - '@netlify/blobs' - '@netlify/runtime' + - '@nuxt/docs' - '@oxc-project/types' - '@pinia/colada' - '@planetscale/database' diff --git a/turbo.json b/turbo.json index 7c43ef9d7d..6ccbd0f4c6 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,22 @@ "outputs": [ "dist/**" ] + }, + "@nuxt/devtools-assets#build": { + // Generates the client app that lives in `packages/devtools` + // (`client/**` dogfooding `src/module`) into this package's `dist/`. + // Those inputs are outside this package, so the task can't be hashed — + // never cache it. The explicit dep replaces `^build` (this package has + // no real workspace dependencies). Only `@nuxt/devtools-kit`'s dist is + // required: the client app consumes `@nuxt/devtools` and + // `@nuxt/devtools-ui-kit` from source (and `build:client` self-stubs + // the module via `dev:prepare`) — depending on their builds here would + // be a cycle, since `@nuxt/devtools` dev-depends on this package. + "dependsOn": [ + "@nuxt/devtools-kit#build" + ], + "outputLogs": "new-only", + "cache": false } } } From 6f3425f83d98c67cfc9a77faa9db92cb41e4fc67 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Fri, 21 Aug 2026 07:42:47 +0000 Subject: [PATCH 2/4] refactor: drop the clientAssets module option, detect the dogfooding client by rootDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only real consumer was the client app itself opting out of the static mount. The module already knows its own client app's directory, so detect that rootDir instead — the client app only exists in this repo, never next to the published package. --- packages/devtools-kit/src/_types/options.ts | 16 -------------- packages/devtools/client/nuxt.config.ts | 7 ------ packages/devtools/src/module-main.ts | 24 ++++++++++----------- 3 files changed, 11 insertions(+), 36 deletions(-) diff --git a/packages/devtools-kit/src/_types/options.ts b/packages/devtools-kit/src/_types/options.ts index eff7387e28..cad1c5dd19 100644 --- a/packages/devtools-kit/src/_types/options.ts +++ b/packages/devtools-kit/src/_types/options.ts @@ -74,22 +74,6 @@ export interface ModuleOptions { */ iframeProps?: Record - /** - * Where the DevTools client UI (the SPA rendered in the DevTools frame) is - * served from. - * - * - `undefined` (default): resolve the version-locked `@nuxt/devtools-assets` - * package — a locally installed copy when present (zero network; install - * it explicitly for offline or air-gapped use), otherwise streamed on - * demand from a CDN mirror of npm and cached on disk. - * - a directory path: serve a local build of the client UI from that - * directory. - * - `false`: don't mount the client UI at all. Used by the DevTools client - * app itself, whose dev server already serves the live client on the same - * base path. - */ - clientAssets?: string | false - /** * Experimental features */ diff --git a/packages/devtools/client/nuxt.config.ts b/packages/devtools/client/nuxt.config.ts index 9aba708451..f4b99fad48 100644 --- a/packages/devtools/client/nuxt.config.ts +++ b/packages/devtools/client/nuxt.config.ts @@ -72,13 +72,6 @@ export default defineNuxtConfig({ ssr: false, - devtools: { - // This app *is* the DevTools client — its dev server serves the live - // client on the very base path the module would otherwise mount the - // built assets on. - clientAssets: false, - }, - app: { baseURL: '/__nuxt_devtools__/client/', }, diff --git a/packages/devtools/src/module-main.ts b/packages/devtools/src/module-main.ts index 9e854bdaac..290fd75533 100644 --- a/packages/devtools/src/module-main.ts +++ b/packages/devtools/src/module-main.ts @@ -9,7 +9,7 @@ import { deprecate, NUXT_DEVTOOLS_GROUP_ID } from '@nuxt/devtools-kit' import { addImports, addPlugin, addTemplate, addVitePlugin, extendViteConfig, logger } from '@nuxt/kit' import { colors } from 'consola/utils' import { serveStaticNodeMiddleware } from 'devframe/utils/serve-static' -import { join } from 'pathe' +import { join, resolve } from 'pathe' import { searchForWorkspaceRoot } from 'vite' import { peerDependencies, version } from '../package.json' import { createDefaultTabOptions, setServerTasksEnabledByDefault } from './constant' @@ -29,20 +29,14 @@ const MULTIPLE_SLASHES_RE = /\/+/g const ASSETS_PACKAGE = '@nuxt/devtools-assets' /** - * Resolve the `StaticAssetsSource` for the client UI, honoring the - * `clientAssets` module option (`false` = don't mount, a string = serve that - * local directory). + * Resolve the `StaticAssetsSource` for the client UI. * * Nightly releases rename workspace packages through `npm:-nightly@` * alias ranges (see `scripts/bump-nightly.ts`), so the real assets package * name is read back from our own peer-dependency entry — a nightly build then * fetches its matching nightly assets. */ -function resolveClientAssetsSource(options: ModuleOptions): StaticAssetsSource | undefined { - if (options.clientAssets === false) - return undefined - if (typeof options.clientAssets === 'string') - return options.clientAssets +function resolveClientAssetsSource(): StaticAssetsSource { const range: string = (peerDependencies as Record)[ASSETS_PACKAGE] ?? '' const name = range.startsWith('npm:') ? range.slice('npm:'.length, range.lastIndexOf('@')) @@ -120,10 +114,14 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { const ROUTE_CLIENT = `${ROUTE_PATH}/client` const ROUTE_ANALYZE = `${ROUTE_PATH}/analyze` - const clientAssetsSource = resolveClientAssetsSource(options) - // Where the client UI lives from the browser's point of view. With - // `clientAssets: false` the app under development *is* the client (the - // dogfooding `nuxi dev client` flow), already served live on its own base. + // In the dogfooding `nuxi dev client` flow the app under development *is* + // the client — already served live on its own base — so don't mount the + // built assets (whose stale copy would otherwise be what the dock opens). + // The client app only exists in this repo, never next to the published + // package, so the check can't misfire for users. + const isSelfClient = resolve(nuxt.options.rootDir) === resolve(packageDir, 'client') + const clientAssetsSource = isSelfClient ? undefined : resolveClientAssetsSource() + // Where the client UI lives from the browser's point of view. const clientUrl = clientAssetsSource ? `${ROUTE_CLIENT}/` : `${nuxt.options.app.baseURL || '/'}/`.replace(MULTIPLE_SLASHES_RE, '/') From 51b153e108089e0e36974f9117079de33580aa50 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Fri, 21 Aug 2026 08:20:58 +0000 Subject: [PATCH 3/4] fix(release): route package prepack through turbo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each package's prepack ran its own build in isolation, never building its workspace dependencies. `pnpm -r publish` skips packages whose version already exists on npm, so a skipped dep's dist could be absent when a dependent package's prepack runs — e.g. @nuxt/devtools-assets' client generate consumes @nuxt/devtools-kit. Route each prepack through `turbo run build --filter=` so turbo's dependsOn graph builds workspace deps in the correct order and cache- restores shared ones. Also scope @nuxt/devtools#build to @nuxt/devtools-kit only, so packing the module no longer regenerates the uncacheable client it no longer ships. --- packages/devtools-assets/package.json | 2 +- packages/devtools-kit/package.json | 2 +- packages/devtools-ui-kit/package.json | 2 +- packages/devtools/package.json | 2 +- turbo.json | 15 +++++++++++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/devtools-assets/package.json b/packages/devtools-assets/package.json index ea784808a0..aae55e386d 100644 --- a/packages/devtools-assets/package.json +++ b/packages/devtools-assets/package.json @@ -18,6 +18,6 @@ ], "scripts": { "build": "pnpm -C ../devtools run build:client", - "prepack": "pnpm build" + "prepack": "turbo run build --filter=@nuxt/devtools-assets" } } diff --git a/packages/devtools-kit/package.json b/packages/devtools-kit/package.json index e575ecfd65..990ef86e58 100644 --- a/packages/devtools-kit/package.json +++ b/packages/devtools-kit/package.json @@ -24,7 +24,7 @@ "build": "unbuild", "stub": "unbuild --stub", "dev:prepare": "nr stub", - "prepack": "pnpm build" + "prepack": "turbo run build --filter=@nuxt/devtools-kit" }, "peerDependencies": { "@nuxt/kit": "^4.0.0-0 || ^5.0.0-0", diff --git a/packages/devtools-ui-kit/package.json b/packages/devtools-ui-kit/package.json index 6fe093c8cb..d4eb095e4c 100644 --- a/packages/devtools-ui-kit/package.json +++ b/packages/devtools-ui-kit/package.json @@ -23,7 +23,7 @@ "build": "nuxt-build-module build", "stub": "nuxt-build-module build --stub", "dev": "nuxi dev playground", - "prepack": "pnpm build", + "prepack": "turbo run build --filter=@nuxt/devtools-ui-kit", "playground:build": "nuxi generate playground" }, "peerDependencies": { diff --git a/packages/devtools/package.json b/packages/devtools/package.json index ec96eb5baa..e46b0ccf6c 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -29,7 +29,7 @@ "dev:playground": "pnpm build && nuxi dev playground", "dev:prepare": "pnpm run stub && nuxi prepare client", "prepare": "tsx scripts/prepare.ts", - "prepack": "pnpm build" + "prepack": "turbo run build --filter=@nuxt/devtools" }, "peerDependencies": { "@nuxt/devtools-assets": "workspace:*", diff --git a/turbo.json b/turbo.json index 6ccbd0f4c6..2eb84b87a8 100644 --- a/turbo.json +++ b/turbo.json @@ -28,6 +28,21 @@ ], "outputLogs": "new-only", "cache": false + }, + "@nuxt/devtools#build": { + // The `@nuxt/devtools` *module* only depends on `@nuxt/devtools-kit`; + // `@nuxt/devtools-assets` and `@nuxt/devtools-ui-kit` are workspace + // dev/peer deps used solely by the client app (built under + // `@nuxt/devtools-assets#build`), not by the module. Depending only on + // the kit avoids regenerating the (uncacheable) client every time the + // module is packed — the module no longer ships the client. + "dependsOn": [ + "@nuxt/devtools-kit#build" + ], + "outputLogs": "new-only", + "outputs": [ + "dist/**" + ] } } } From 23eae77da1ca9dafb0e4902601f18545f5b49f2b Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Fri, 21 Aug 2026 08:37:06 +0000 Subject: [PATCH 4/4] refactor: remove the legacy v3 iframe-embedding path The DevTools client is now hosted by devframe / the Vite DevTools hub, so Nuxt DevTools no longer creates or manages its own client iframe. Drop the dead v3 embedding path: - `view/client.ts`: `getIframe`, `syncClient`, `waitForClientInjection`, the `iframe` local and the `devtools:navigate` route-persistence handler - the `__NUXT_DEVTOOLS_VIEW__` injection protocol (host guard, window global type, and the client-side `setClient` defineProperty) - `view/state.ts` frame-geometry storage and the dead client-side `useDevToolsFrameState`/`useDevToolsPanelsState` twins - `NuxtDevtoolsHostClient.getIframe`/`syncClient`/`app.frameState`, the `DevToolsFrameState` and `NuxtDevtoolsGlobal` types - the `iframeProps` module option (only the removed iframe consumed it) The current host<->client bridge (`__NUXT_DEVTOOLS_HOST__` + `connectParent`), the component inspector, and popup mode (`__NUXT_DEVTOOLS_DISABLE__`) are unaffected. BREAKING CHANGE: the `iframeProps` module option is removed; the client iframe is now managed by the Vite DevTools hub. --- .../devtools-kit/src/_types/client-api.ts | 25 ------ packages/devtools-kit/src/_types/options.ts | 5 -- .../devtools/client/composables/storage.ts | 13 --- packages/devtools/client/plugins/global.ts | 10 --- packages/devtools/src/module-main.ts | 5 -- .../src/runtime/plugins/devtools.client.ts | 5 -- .../src/runtime/plugins/view/client.ts | 86 +------------------ .../src/runtime/plugins/view/state.ts | 14 --- packages/devtools/src/types/global.ts | 9 +- 9 files changed, 2 insertions(+), 170 deletions(-) delete mode 100644 packages/devtools/client/composables/storage.ts delete mode 100644 packages/devtools/src/runtime/plugins/view/state.ts diff --git a/packages/devtools-kit/src/_types/client-api.ts b/packages/devtools-kit/src/_types/client-api.ts index 36598976bd..6f34e31c44 100644 --- a/packages/devtools-kit/src/_types/client-api.ts +++ b/packages/devtools-kit/src/_types/client-api.ts @@ -11,18 +11,6 @@ import type { NuxtDevtoolsNotifyInput } from './notify' import type { ServerFunctions } from './rpc' import type { TimelineMetrics } from './timeline-metrics' -export interface DevToolsFrameState { - width: number - height: number - top: number - left: number - open: boolean - route: string - position: 'left' | 'right' | 'bottom' | 'top' - closeOnOutsideClick: boolean - minimizePanelInactive: number -} - export interface NuxtDevtoolsClientHooks { /** * When the DevTools navigates, used for persisting the current tab @@ -57,8 +45,6 @@ export interface NuxtDevtoolsHostClient { nuxt: NuxtApp hooks: Hookable - getIframe: () => HTMLIFrameElement | undefined - inspector?: { enable: () => void disable: () => void @@ -80,7 +66,6 @@ export interface NuxtDevtoolsHostClient { navigate: (path: string, hard?: boolean) => void appConfig: AppConfig colorMode: Ref<'dark' | 'light'> - frameState: Ref $fetch: $Fetch } @@ -95,12 +80,6 @@ export interface NuxtDevtoolsHostClient { * A counter to trigger reactivity updates */ revision: Ref - - /** - * Update client - * @internal - */ - syncClient: () => NuxtDevtoolsHostClient } export interface CodeHighlightOptions { @@ -152,7 +131,3 @@ export interface NuxtDevtoolsIframeClient { host: NuxtDevtoolsHostClient devtools: NuxtDevtoolsClient } - -export interface NuxtDevtoolsGlobal { - setClient: (client: NuxtDevtoolsHostClient) => void -} diff --git a/packages/devtools-kit/src/_types/options.ts b/packages/devtools-kit/src/_types/options.ts index cad1c5dd19..ebca26ecbc 100644 --- a/packages/devtools-kit/src/_types/options.ts +++ b/packages/devtools-kit/src/_types/options.ts @@ -69,11 +69,6 @@ export interface ModuleOptions { */ disableAuthorization?: boolean - /** - * Props for the iframe element, useful for environment with stricter CSP - */ - iframeProps?: Record - /** * Experimental features */ diff --git a/packages/devtools/client/composables/storage.ts b/packages/devtools/client/composables/storage.ts deleted file mode 100644 index 745f8b9a28..0000000000 --- a/packages/devtools/client/composables/storage.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { DevToolsFrameState } from '~~/../src/types' -import { useLocalStorage } from '@vueuse/core' - -const devToolsFrameState = useLocalStorage('nuxt-devtools-frame-state', {} as any, { listenToStorageChanges: true }) -const devToolsPanelsState = useLocalStorage>('nuxt-devtools-panels-state', {} as any, { listenToStorageChanges: false }) - -export function useDevToolsFrameState() { - return devToolsFrameState -} - -export function useDevToolsPanelsState() { - return devToolsPanelsState -} diff --git a/packages/devtools/client/plugins/global.ts b/packages/devtools/client/plugins/global.ts index 9f4a5dceba..44d26ee8e0 100644 --- a/packages/devtools/client/plugins/global.ts +++ b/packages/devtools/client/plugins/global.ts @@ -45,16 +45,6 @@ export default defineNuxtPlugin(() => { } } - Object.defineProperty(window, '__NUXT_DEVTOOLS_VIEW__', { - value: { - setClient(_client) { - setupClient(_client) - }, - } as typeof window['__NUXT_DEVTOOLS_VIEW__'], - enumerable: false, - configurable: true, - }) - connectParent() setTimeout(connectParent, 1000) diff --git a/packages/devtools/src/module-main.ts b/packages/devtools/src/module-main.ts index 290fd75533..802c088ac7 100644 --- a/packages/devtools/src/module-main.ts +++ b/packages/devtools/src/module-main.ts @@ -77,11 +77,6 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { }) } - if (options.iframeProps) { - nuxt.options.runtimeConfig.app.devtools ||= {} - nuxt.options.runtimeConfig.app.devtools.iframeProps = options.iframeProps - } - // Make unimport exposing more information, like the usage of each auto imported function nuxt.options.imports.collectMeta = true diff --git a/packages/devtools/src/runtime/plugins/devtools.client.ts b/packages/devtools/src/runtime/plugins/devtools.client.ts index d15d8888ff..2f4c25a5e8 100644 --- a/packages/devtools/src/runtime/plugins/devtools.client.ts +++ b/packages/devtools/src/runtime/plugins/devtools.client.ts @@ -15,11 +15,6 @@ export default defineNuxtPlugin((nuxt: any) => { try { if (window.__NUXT_DEVTOOLS_DISABLE__ || window.parent?.__NUXT_DEVTOOLS_DISABLE__) return - - if (parent && window.self !== parent) { - if (parent.__NUXT_DEVTOOLS_VIEW__) - return - } } catch (e) { console.error('Nuxt DevTools: Failed to check parent window') diff --git a/packages/devtools/src/runtime/plugins/view/client.ts b/packages/devtools/src/runtime/plugins/view/client.ts index 6d533330cf..0a7420e121 100644 --- a/packages/devtools/src/runtime/plugins/view/client.ts +++ b/packages/devtools/src/runtime/plugins/view/client.ts @@ -13,12 +13,9 @@ import { events as inspectorEvents, hasData as inspectorHasData, state as inspec import { computed, markRaw, nextTick, reactive, ref, shallowReactive, shallowRef, toRef, watch } from 'vue' // eslint-disable-next-line ts/ban-ts-comment // @ts-ignore tsconfig -import { useAppConfig, useRuntimeConfig } from '#imports' +import { useAppConfig } from '#imports' import { initTimelineMetrics } from '../../function-metrics-helpers' -import { state } from './state' - -const MULTIPLE_SLASHES_RE = /\/+/g // The `Nuxt` dock group id (see `NUXT_DEVTOOLS_GROUP_ID`). Activating the group // auto-opens its `defaultChildId` (the shared-frame anchor). The anchor iframe @@ -47,7 +44,6 @@ export async function setupDevToolsClient({ timeMetric: any router: Router }) { - let iframe: HTMLIFrameElement | undefined let inspector: NuxtDevtoolsHostClient['inspector'] | undefined const colorMode = useClientColorMode() @@ -58,9 +54,6 @@ export async function setupDevToolsClient({ hooks: createHooks(), inspector: getInspectorInstance(), - getIframe, - syncClient, - devtools: { toggle() { const ctx = getViteDevToolsContext() @@ -104,7 +97,6 @@ export async function setupDevToolsClient({ router.push(path) }, colorMode, - frameState: state, $fetch: globalThis.$fetch as $Fetch, }, @@ -120,78 +112,6 @@ export async function setupDevToolsClient({ window.__NUXT_DEVTOOLS_HOST__ = client - function syncClient() { - if (!client.inspector) - client.inspector = getInspectorInstance() - - try { - iframe?.contentWindow?.__NUXT_DEVTOOLS_VIEW__?.setClient(client) - } - catch (e) { - // cross-origin - console.error('[nuxt-devtools] Failed to connect view', e) - } - return client - } - - function getIframe() { - if (!iframe) { - const runtimeConfig = useRuntimeConfig() - const CLIENT_BASE = '/__nuxt_devtools__/client' - const CLIENT_PATH = `${runtimeConfig.app.baseURL.replace(CLIENT_BASE, '/')}${CLIENT_BASE}`.replace(MULTIPLE_SLASHES_RE, '/') - const initialUrl = CLIENT_PATH + state.value.route - iframe = document.createElement('iframe') - - // custom iframe props - for (const [key, value] of Object.entries(runtimeConfig.app.devtools?.iframeProps || {})) - iframe.setAttribute(key, String(value)) - - iframe.id = 'nuxt-devtools-iframe' - iframe.src = initialUrl - iframe.onload = async () => { - try { - await waitForClientInjection() - client.syncClient() - } - catch (e) { - console.error('Nuxt DevTools client injection failed') - console.error(e) - } - } - } - - return iframe - } - - function waitForClientInjection(retry = 20, timeout = 300) { - let lastError: any - const test = () => { - try { - return !!iframe?.contentWindow?.__NUXT_DEVTOOLS_VIEW__ - } - catch (e) { - lastError = e - } - return false - } - - if (test()) - return - - return new Promise((resolve, reject) => { - const interval = setInterval(() => { - if (test()) { - clearInterval(interval) - resolve() - } - else if (retry-- <= 0) { - clearInterval(interval) - reject(lastError) - } - }, timeout) - }) - } - function getInspectorInstance(): NuxtDevtoolsHostClient['inspector'] { if (inspector) return inspector @@ -400,8 +320,4 @@ function setupReactivity(client: NuxtDevtoolsHostClient, router: Router | undefi client.nuxt.hook('app:mounted', () => { refreshReactivity() }) - // record last route - client.hooks.hook('devtools:navigate', (path) => { - state.value.route = path - }) } diff --git a/packages/devtools/src/runtime/plugins/view/state.ts b/packages/devtools/src/runtime/plugins/view/state.ts deleted file mode 100644 index 9972964665..0000000000 --- a/packages/devtools/src/runtime/plugins/view/state.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { DevToolsFrameState } from '@nuxt/devtools/types' -import { useObjectStorage } from './utils' - -export const state = useObjectStorage('nuxt-devtools-frame-state', { - width: 80, - height: 60, - top: 0, - left: 50, - open: false, - route: '/', - position: 'bottom', - closeOnOutsideClick: false, - minimizePanelInactive: 5000, -}, false) diff --git a/packages/devtools/src/types/global.ts b/packages/devtools/src/types/global.ts index 4435032885..fee4b1191b 100644 --- a/packages/devtools/src/types/global.ts +++ b/packages/devtools/src/types/global.ts @@ -1,4 +1,4 @@ -import type { LoadingTimeMetric, NuxtDevtoolsHostClient, NuxtDevtoolsIframeClient, NuxtDevtoolsGlobal as NuxtDevtoolsViewGlobal, PluginMetric, TimelineMetrics } from '.' +import type { LoadingTimeMetric, NuxtDevtoolsHostClient, NuxtDevtoolsIframeClient, PluginMetric, TimelineMetrics } from '.' declare global { interface Window { @@ -12,13 +12,6 @@ declare global { */ __NUXT_DEVTOOLS_HOST__?: NuxtDevtoolsHostClient - /** - * Nuxt DevTools for receiving host client - * - * @internal - */ - __NUXT_DEVTOOLS_VIEW__?: NuxtDevtoolsViewGlobal - /** * Metrics for plugin loading time *