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
27 changes: 16 additions & 11 deletions .github/workflows/ecosystem-playground.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
name: ecosystem-playground

# Manual-trigger only checks for playgrounds-ecosystem/modules/ — NOT part of
# the default push/pull_request CI path. Two independent tasks:
# Weekly / manual checks for playgrounds-ecosystem/ — NOT part of the default
# push/pull_request CI path (the lighter Nuxt 4 check that is lives in
# nuxt4-smoke.yml). Two independent tasks:
# * `smoke` — cheap "does any of this still build" signal: the
# nuxt4/nuxt5 playgrounds and the `nuxt build` combo.
# * `devtools-smoke` — Playwright suite that drives the real embedded DevTools
# client and asserts each module's tab renders.
# client and asserts each module's tab renders, plus a
# module on the published @nuxt/devtools-kit v3.
# See playgrounds-ecosystem/README.md and playgrounds-ecosystem/REPORTS.md for
# context (nuxt/devtools#1022).
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * 1'

jobs:
smoke:
Expand Down Expand Up @@ -45,19 +49,20 @@ jobs:
- name: Check nitro/nitropack single-engine type resolution
run: node playgrounds-ecosystem/scripts/check-nitro-type-resolution.mjs
# Per-major DevTools playgrounds (sealed, install DevTools from the
# tarballs above): prove @nuxt/devtools type-checks and builds on a Nuxt 4
# (Nitro v2) and a Nuxt 5 (Nitro v3) consumer. Dev mode is verified
# manually — see REPORTS.md — since CI can't drive a dev server.
# tarballs above): prove @nuxt/devtools type-checks, builds and boots in
# dev on a Nuxt 4 (Nitro v2) and a Nuxt 5 (Nitro v3) consumer.
- name: Nuxt 4 playground (Nitro v2)
run: |
pnpm -C playgrounds-ecosystem/nuxt4 install --no-frozen-lockfile
pnpm -C playgrounds-ecosystem/nuxt4 run play:typecheck
pnpm -C playgrounds-ecosystem/nuxt4 run play:build
pnpm -C playgrounds-ecosystem/nuxt4 run typecheck
pnpm -C playgrounds-ecosystem/nuxt4 run build
node playgrounds-ecosystem/scripts/check-dev-boot.mjs playgrounds-ecosystem/nuxt4
- name: Nuxt 5 playground (Nitro v3)
run: |
pnpm -C playgrounds-ecosystem/nuxt5 install --no-frozen-lockfile
pnpm -C playgrounds-ecosystem/nuxt5 run play:typecheck
pnpm -C playgrounds-ecosystem/nuxt5 run play:build
pnpm -C playgrounds-ecosystem/nuxt5 run typecheck
pnpm -C playgrounds-ecosystem/nuxt5 run build
node playgrounds-ecosystem/scripts/check-dev-boot.mjs playgrounds-ecosystem/nuxt5
# `modules/` stays a sealed workspace (own lockfile).
- run: pnpm -C playgrounds-ecosystem/modules install
- name: Build the module combo (Nuxt 4)
Expand Down Expand Up @@ -85,7 +90,7 @@ jobs:
- run: corepack enable
- run: pnpm install
# The smoke test drives the real embedded DevTools client, so it needs the
# full static client at packages/devtools/dist/client.
# full static client built into packages/devtools-assets/dist.
- run: pnpm build
- name: Cache Playwright browsers
uses: actions/cache@v6
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/nuxt4-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: nuxt4-smoke

# The monorepo itself develops against the Nuxt 5 nightly, so nothing in the
# default CI path runs DevTools on Nuxt 4 (Nitro v2). This installs the packed
# `@nuxt/devtools` into the sealed Nuxt 4 playground the way a user would and
# checks it type-checks, builds, and — the only mode DevTools does anything in —
# boots in dev with the client served. See playgrounds-ecosystem/README.md.
on:
push:
branches:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name != 'push' }}

jobs:
nuxt4:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: lts/*
cache: pnpm
- run: npm install -g corepack@latest
- run: corepack enable
- run: pnpm install
- run: pnpm run build
- name: Pack local DevTools tarballs
run: node playgrounds-ecosystem/scripts/pack-local.mjs --no-build
- name: Install the Nuxt 4 playground
run: pnpm -C playgrounds-ecosystem/nuxt4 install --no-frozen-lockfile
- run: pnpm -C playgrounds-ecosystem/nuxt4 run typecheck
- run: pnpm -C playgrounds-ecosystem/nuxt4 run build
- name: Boot the dev server and the DevTools client
run: node playgrounds-ecosystem/scripts/check-dev-boot.mjs playgrounds-ecosystem/nuxt4
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