Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
],
"scripts": {
"build": "pnpm dev:prepare && pnpm build:module",
"build:client": "pnpm dev:prepare && nuxi generate client && node scripts/copy-client.mjs",
"build:client": "nuxi generate client && node scripts/copy-client.mjs",
"build:module": "nuxt-build-module build",
"stub": "nuxt-build-module build --stub",
"dev": "nuxi dev client",
Expand Down
13 changes: 7 additions & 6 deletions packages/devtools/src/module-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,20 +291,21 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) {
setServerTasksEnabledByDefault(true)

// Inject inline script. Force our small runtime plugin to be bundled
// rather than externalized as a node_modules import at runtime — Nitro v2
// (`nitropack`) and Nitro v3 (`nitro`) expose this via different config
// shapes (`externals.inline` vs `noExternals`), so `config`'s type here is
// a union of both; handle whichever applies.
// rather than externalized as a node_modules import at runtime. Nitro v2
// (`nitropack`, Nuxt 4) spells this `externals.inline` and its
// `noExternals` is a boolean, so it must never receive an array: a
// non-empty one is truthy and inlines every dependency. Nitro v3 (`nitro`,
// Nuxt 5) has no `externals` and takes a `noExternals` list instead.
const inlinePath = join(runtimeDir, 'nitro')
if ('externals' in config) {
config.externals ||= {}
config.externals.inline ||= []
config.externals.inline.push(inlinePath)
}
if ('noExternals' in config && Array.isArray(config.noExternals)) {
else if (Array.isArray(config.noExternals)) {
config.noExternals.push(inlinePath)
}
else if (!('noExternals' in config) || config.noExternals === undefined) {
else if (config.noExternals === undefined) {
config.noExternals = [inlinePath]
}
config.virtual = config.virtual || {}
Expand Down
8 changes: 6 additions & 2 deletions packages/devtools/src/runtime/plugins/devtools.client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TimelineServerState } from '@nuxt/devtools/types'

import type { Plugin } from 'nuxt/app'
import type { Router } from 'vue-router'
import { shallowReactive, watchEffect } from 'vue'

Expand All @@ -8,7 +8,9 @@ import { shallowReactive, watchEffect } from 'vue'
import { defineNuxtPlugin, useRouter, useState } from '#imports'
import { setupHooksDebug } from '../shared/hooks'

export default defineNuxtPlugin((nuxt: any) => {
// Annotated so Nuxt's injection typing (`typeof import(this).default`) does
// not have to infer through the composables this plugin calls.
const plugin: Plugin = defineNuxtPlugin((nuxt: any) => {
if (typeof document === 'undefined' || typeof window === 'undefined')
return

Expand Down Expand Up @@ -69,3 +71,5 @@ export default defineNuxtPlugin((nuxt: any) => {
)
})
})

export default plugin
5 changes: 4 additions & 1 deletion packages/devtools/src/runtime/settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { NuxtDevToolsOptions } from '@nuxt/devtools/types'

// @ts-expect-error virtual module
// Declared by `nuxi prepare` only in some setups, so `@ts-expect-error` would
// be unused in the others.
// eslint-disable-next-line ts/ban-ts-comment
// @ts-ignore virtual module
import _settings from '#build/devtools/settings'

export const settings = _settings as {
Expand Down
32 changes: 1 addition & 31 deletions packages/devtools/test/devtools-origin.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { Nuxt } from 'nuxt/schema'
import type { Plugin } from 'vite'
import { fileURLToPath } from 'node:url'
import { buildOtpAuthUrl } from 'devframe/node/auth'
import { createHooks } from 'hookable'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { fakeNuxt } from './fake-nuxt'

const mocks = vi.hoisted(() => ({
vitePlugins: [] as Plugin[],
Expand All @@ -27,34 +25,6 @@ vi.mock('@nuxt/kit', async (importOriginal) => {
}
})

function fakeNuxt(): Nuxt {
const hooks = createHooks()
const clientDir = fileURLToPath(new URL('../client', import.meta.url))
return {
options: {
rootDir: clientDir,
srcDir: clientDir,
builder: '@nuxt/vite-builder',
dev: true,
test: false,
dir: { public: 'public', app: 'app' },
app: { baseURL: '/' },
_layers: [],
analyzeDir: '/tmp/fixture-app/.nuxt/analyze',
runtimeConfig: {},
future: { compatibilityVersion: 4 },
_nuxtConfigFile: '/tmp/fixture-app/nuxt.config.ts',
build: {},
imports: {},
vite: {},
},
vfs: {},
hooks,
hook: hooks.hook.bind(hooks),
callHook: hooks.callHook.bind(hooks),
} as unknown as Nuxt
}

afterEach(() => {
mocks.vitePlugins.length = 0
vi.unstubAllEnvs()
Expand Down
32 changes: 32 additions & 0 deletions packages/devtools/test/fake-nuxt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Nuxt } from 'nuxt/schema'
import { fileURLToPath } from 'node:url'
import { createHooks } from 'hookable'

/** The minimum `Nuxt` shape `enableModule()` reads in dev mode. */
export function fakeNuxt(): Nuxt {
const hooks = createHooks()
const clientDir = fileURLToPath(new URL('../client', import.meta.url))
return {
options: {
rootDir: clientDir,
srcDir: clientDir,
builder: '@nuxt/vite-builder',
dev: true,
test: false,
dir: { public: 'public', app: 'app' },
app: { baseURL: '/' },
_layers: [],
analyzeDir: '/tmp/fixture-app/.nuxt/analyze',
runtimeConfig: {},
future: { compatibilityVersion: 4 },
_nuxtConfigFile: '/tmp/fixture-app/nuxt.config.ts',
build: {},
imports: {},
vite: {},
},
vfs: {},
hooks,
hook: hooks.hook.bind(hooks),
callHook: hooks.callHook.bind(hooks),
} as unknown as Nuxt
}
54 changes: 54 additions & 0 deletions packages/devtools/test/nitro-inline-runtime.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { fakeNuxt } from './fake-nuxt'

vi.mock('@vitejs/devtools', () => ({
DevTools: vi.fn(() => []),
}))

vi.mock('@nuxt/kit', async (importOriginal) => {
const actual = await importOriginal<typeof import('@nuxt/kit')>()
return {
...actual,
addImports: vi.fn(),
addPlugin: vi.fn(),
addTemplate: vi.fn(),
addVitePlugin: vi.fn(),
extendViteConfig: vi.fn(),
}
})

afterEach(() => {
vi.unstubAllEnvs()
})

async function nitroConfigAfterSetup<T extends object>(config: T): Promise<T> {
vi.stubEnv('NODE_ENV', 'development')
vi.stubEnv('TEST', '')
const nuxt = fakeNuxt()
const { enableModule } = await import('../src/module-main')
await enableModule({
dataInspector: false,
viteInspect: false,
componentInspector: false,
vueDevTools: false,
codeServer: { enabled: false },
} as any, nuxt)
await nuxt.callHook('nitro:config', config as any)
return config
}

describe('inlining the nitro runtime plugin', () => {
it('uses externals.inline on Nitro v2 and leaves its boolean noExternals alone', async () => {
const config = await nitroConfigAfterSetup({ externals: { inline: ['vue'] } } as { externals: { inline: string[] }, noExternals?: boolean })

expect(config.externals.inline).toEqual(['vue', expect.stringMatching(/runtime[\\/]nitro$/)])
expect(config.noExternals).toBeUndefined()
})

it('appends to the noExternals list on Nitro v3', async () => {
const config = await nitroConfigAfterSetup({ noExternals: ['nuxt/dist'] } as { noExternals: (string | RegExp)[], externals?: object })

expect(config.noExternals).toEqual(['nuxt/dist', expect.stringMatching(/runtime[\\/]nitro$/)])
expect(config.externals).toBeUndefined()
})
})
15 changes: 0 additions & 15 deletions patches/nitro@3.0.260610-beta.patch

This file was deleted.

Loading
Loading