-
+
diff --git a/content.config.ts b/content.config.ts
index 710345936..604bee29c 100644
--- a/content.config.ts
+++ b/content.config.ts
@@ -1,10 +1,11 @@
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
+import { CLI_DOCS_PREFIX, CLI_DOCS_REFS, CLI_DOCS_REPO } from './shared/utils/cli-docs'
const docsV3Source = {
cwd: process.env.NUXT_V3_PATH ?? undefined,
repository: !process.env.NUXT_V3_PATH ? 'https://github.com/nuxt/nuxt/tree/3.x' : undefined,
include: 'docs/**/*',
- exclude: ['docs/**/*.json'],
+ exclude: ['docs/**/*.json', 'docs/4.api/4.commands/**'],
prefix: '/docs/3.x'
}
@@ -12,7 +13,7 @@ const docsV4Source = {
cwd: process.env.NUXT_V4_PATH ?? undefined,
repository: !process.env.NUXT_V4_PATH ? 'https://github.com/nuxt/nuxt/tree/4.x' : undefined,
include: 'docs/**/*',
- exclude: ['docs/**/*.json'],
+ exclude: ['docs/**/*.json', 'docs/4.api/4.commands/**'],
prefix: '/docs/4.x'
}
@@ -20,10 +21,31 @@ const docsV5Source = {
cwd: process.env.NUXT_V5_PATH ?? undefined,
repository: !process.env.NUXT_V5_PATH ? 'https://github.com/nuxt/nuxt/tree/main' : undefined,
include: 'docs/**/*',
- exclude: ['docs/**/*.json'],
+ exclude: ['docs/**/*.json', 'docs/4.api/4.commands/**'],
prefix: '/docs/5.x'
}
+const cliV3Source = {
+ cwd: process.env.NUXT_CLI_PATH ?? undefined,
+ repository: !process.env.NUXT_CLI_PATH ? `https://github.com/${CLI_DOCS_REPO}/tree/${CLI_DOCS_REFS.docsv3}` : undefined,
+ include: 'docs/**/*',
+ prefix: `/docs/3.x/${CLI_DOCS_PREFIX}`
+}
+
+const cliV4Source = {
+ cwd: process.env.NUXT_CLI_PATH ?? undefined,
+ repository: !process.env.NUXT_CLI_PATH ? `https://github.com/${CLI_DOCS_REPO}/tree/${CLI_DOCS_REFS.docsv4}` : undefined,
+ include: 'docs/**/*',
+ prefix: `/docs/4.x/${CLI_DOCS_PREFIX}`
+}
+
+const cliV5Source = {
+ cwd: process.env.NUXT_CLI_PATH ?? undefined,
+ repository: !process.env.NUXT_CLI_PATH ? `https://github.com/${CLI_DOCS_REPO}/tree/${CLI_DOCS_REFS.docsv5}` : undefined,
+ include: 'docs/**/*',
+ prefix: `/docs/5.x/${CLI_DOCS_PREFIX}`
+}
+
const examplesV3Source = {
cwd: process.env.NUXT_EXAMPLES_PATH ?? undefined,
repository: !process.env.NUXT_EXAMPLES_PATH ? 'https://github.com/nuxt/examples' : undefined,
@@ -214,7 +236,7 @@ export default defineContentConfig({
}),
docsv5: defineCollection({
type: 'page',
- source: [docsV5Source, examplesV5Source],
+ source: [docsV5Source, cliV5Source, examplesV5Source],
schema: z.object({
titleTemplate: z.string().optional(),
links: z.array(Button),
@@ -223,7 +245,7 @@ export default defineContentConfig({
}),
docsv4: defineCollection({
type: 'page',
- source: [docsV4Source, examplesV4Source],
+ source: [docsV4Source, cliV4Source, examplesV4Source],
schema: z.object({
titleTemplate: z.string().optional(),
links: z.array(Button),
@@ -232,7 +254,7 @@ export default defineContentConfig({
}),
docsv3: defineCollection({
type: 'page',
- source: [docsV3Source, examplesV3Source],
+ source: [docsV3Source, cliV3Source, examplesV3Source],
schema: z.object({
titleTemplate: z.string().optional(),
links: z.array(Button),
diff --git a/nuxt.config.ts b/nuxt.config.ts
index bb9d7dcc9..c77bf08a6 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -1,5 +1,6 @@
import { createResolver } from 'nuxt/kit'
import { parseMdc } from './helpers/mdc-parser.mjs'
+import { CLI_DOCS_PREFIX, CLI_DOCS_REFS, CLI_DOCS_REPO } from './shared/utils/cli-docs'
const { resolve } = createResolver(import.meta.url)
@@ -246,10 +247,10 @@ export default defineNuxtConfig({
'/docs/3.x/api/kit': { redirect: '/docs/3.x/api/kit/modules', prerender: false },
'/docs/4.x/api/kit': { redirect: '/docs/4.x/api/kit/modules', prerender: false },
'/docs/5.x/api/kit': { redirect: '/docs/5.x/api/kit/modules', prerender: false },
- '/docs/api/commands': { redirect: '/docs/api/commands/dev', prerender: false },
- '/docs/3.x/api/commands': { redirect: '/docs/3.x/api/commands/dev', prerender: false },
- '/docs/4.x/api/commands': { redirect: '/docs/4.x/api/commands/dev', prerender: false },
- '/docs/5.x/api/commands': { redirect: '/docs/5.x/api/commands/dev', prerender: false },
+ '/docs/api/commands': { redirect: '/docs/api/commands/overview', prerender: false },
+ '/docs/3.x/api/commands': { redirect: '/docs/3.x/api/commands/overview', prerender: false },
+ '/docs/4.x/api/commands': { redirect: '/docs/4.x/api/commands/overview', prerender: false },
+ '/docs/5.x/api/commands': { redirect: '/docs/5.x/api/commands/overview', prerender: false },
'/docs/api/advanced': { redirect: '/docs/api/advanced/hooks', prerender: false },
'/docs/3.x/api/advanced': { redirect: '/docs/3.x/api/advanced/hooks', prerender: false },
'/docs/4.x/api/advanced': { redirect: '/docs/4.x/api/advanced/hooks', prerender: false },
@@ -537,6 +538,15 @@ export default defineNuxtConfig({
},
hooks: {
'content:file:beforeParse': async ({ file }) => {
+ // Command docs are served from `nuxt/cli`, but Content only ships the markdown,
+ // not the terminal captures committed beside it. Root-relative image sources are
+ // repo-relative, so resolve them against the ref this page was parsed from: a
+ // hardcoded ref would serve `main`'s captures on the 3.x tree.
+ const collection = file.id.split('/')[0] as keyof typeof CLI_DOCS_REFS
+ if (file.id.includes(`/${CLI_DOCS_PREFIX}/`) && CLI_DOCS_REFS[collection]) {
+ const base = `https://raw.githubusercontent.com/${CLI_DOCS_REPO}/${CLI_DOCS_REFS[collection]}`
+ file.body = file.body.replaceAll(/(!\[[^\]]*\]\()\/(?!\/)/g, `$1${base}/`)
+ }
if (file.id.startsWith('docsv5/')) {
file.body = file.body.replaceAll(/\(\/docs\/(?!\d\.x)/g, '(/docs/5.x/')
// Pages that only exist on main (5.x) but are linked as /docs/4.x/* from
@@ -556,6 +566,9 @@ export default defineNuxtConfig({
if (file.id.startsWith('docsv4/')) {
file.body = file.body.replaceAll(/\(\/docs\/(?!\d\.x)/g, '(/docs/4.x/')
}
+ if (file.id.startsWith('docsv3/')) {
+ file.body = file.body.replaceAll(/\(\/docs\/(?!\d\.x)/g, '(/docs/3.x/')
+ }
},
'content:file:afterParse': async ({ file, content }) => {
if (file.id === 'index/index.yml') {
@@ -631,7 +644,11 @@ export default defineNuxtConfig({
},
image: {
format: ['webp', 'jpeg', 'jpg', 'png', 'svg'],
- provider: 'ipx'
+ provider: 'ipx',
+ // Opt-out escape hatch for images IPX must not touch, used by `DocsProseImg`.
+ providers: {
+ none: { provider: 'none' }
+ }
},
llms: {
domain: 'https://nuxt.com',
diff --git a/patches/@nuxt__content.patch b/patches/@nuxt__content.patch
new file mode 100644
index 000000000..4c2b2a8b8
--- /dev/null
+++ b/patches/@nuxt__content.patch
@@ -0,0 +1,22 @@
+diff --git a/dist/module.mjs b/dist/module.mjs
+index f1516bab826818eacc9088a24df96cf4ff895502..b14f1f4579af1e502fc57e9834e88feb916222cc 100644
+--- a/dist/module.mjs
++++ b/dist/module.mjs
+@@ -2007,11 +2007,16 @@ function defineGitSource(source) {
+ const repository = source?.repository && gitUrlParse(source.repository.url);
+ if (repository) {
+ const { source: gitSource, owner, name } = repository;
+- resolvedSource.cwd = join(rootDir, ".data", "content", `${gitSource}-${owner}-${name}-${repository.ref || "main"}`);
+ let ref;
+ if (source.repository.branch && source.repository.tag) {
+ throw new Error("Cannot specify both branch and tag for git repository. Please specify one of `branch` or `tag`.");
+ }
++ // Keyed on the resolved ref so two refs of one repo do not share a checkout.
++ // Backport of https://github.com/nuxt/content/pull/3839; drop on the next release.
++ const resolvedRef = source.repository.branch || source.repository.tag || repository.ref || "main";
++ const refKey = /^[\w.-]+$/.test(resolvedRef) ? resolvedRef : `${resolvedRef.replace(/[^\w.-]+/g, "-")}-${hash(resolvedRef).slice(0, 8)}`;
++ const refPrefix = source.repository.tag ? "tag-" : "";
++ resolvedSource.cwd = join(rootDir, ".data", "content", `${gitSource}-${owner}-${name}-${refPrefix}${refKey}`);
+ if (source.repository.branch) ref = { branch: source.repository.branch };
+ if (source.repository.tag) ref = { tag: source.repository.tag };
+ if (!source.repository?.auth && source.authBasic) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7864c2c99..0a45979bb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,6 +9,9 @@ overrides:
'@workflow/serde': 4.1.2
'@nuxt/content>@nuxtjs/mdc': ^0.23.1
+patchedDependencies:
+ '@nuxt/content': c5b7bb228dea1e45f274e367308107c2f611ad739b4bb5742478643be3a95f9f
+
importers:
.:
@@ -54,7 +57,7 @@ importers:
version: 2.1.0
'@nuxt/content':
specifier: ^3.15.2
- version: 3.15.2(@libsql/client@0.17.4)(@oxc-project/types@0.144.0)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(supports-color@10.2.2)(valibot@1.4.2(typescript@6.0.3))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))
+ version: 3.15.2(patch_hash=c5b7bb228dea1e45f274e367308107c2f611ad739b4bb5742478643be3a95f9f)(@libsql/client@0.17.4)(@oxc-project/types@0.144.0)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(supports-color@10.2.2)(valibot@1.4.2(typescript@6.0.3))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))
'@nuxt/hints':
specifier: ^1.1.4
version: 1.1.4(@libsql/client@0.17.4)(@vercel/functions@3.9.3(ws@8.21.3))(@vitest/expect@4.1.10)(better-sqlite3@13.0.3)(db0@0.3.4(@libsql/client@0.17.4)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3)))(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magicast@0.5.4)(rolldown@1.2.4)(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.10(@types/node@26.2.0)(happy-dom@20.11.2)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)))(vue@3.5.41(typescript@6.0.3))
@@ -66,7 +69,7 @@ importers:
version: 1.3.5(@oxc-project/types@0.144.0)(@types/youtube@0.3.0)(@unhead/vue@3.3.2(@oxc-project/types@0.144.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)))(@vercel/functions@3.9.3(ws@8.21.3))(db0@0.3.4(@libsql/client@0.17.4)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3)))(esbuild@0.28.2)(ioredis@5.11.1(supports-color@10.2.2))(magicast@0.5.4)(rolldown@1.2.4)(rollup@4.62.4)(typescript@6.0.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@nuxt/ui':
specifier: ^4.10.0
- version: 4.10.0(ae7cefd1db2330b460f8c2d512a8cf44)
+ version: 4.10.0(1c051f5eaf5cc4a909f4318924c3960a)
'@nuxthub/core':
specifier: ^0.10.8
version: 0.10.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@vercel/functions@3.9.3(ws@8.21.3))(db0@0.3.4(@libsql/client@0.17.4)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3)))(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.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(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)))(vue-tsc@3.3.10(typescript@6.0.3))
@@ -11874,7 +11877,7 @@ snapshots:
- magicast
- supports-color
- '@nuxt/content@3.15.2(@libsql/client@0.17.4)(@oxc-project/types@0.144.0)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(supports-color@10.2.2)(valibot@1.4.2(typescript@6.0.3))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))':
+ '@nuxt/content@3.15.2(patch_hash=c5b7bb228dea1e45f274e367308107c2f611ad739b4bb5742478643be3a95f9f)(@libsql/client@0.17.4)(@oxc-project/types@0.144.0)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(supports-color@10.2.2)(valibot@1.4.2(typescript@6.0.3))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))':
dependencies:
'@nuxt/kit': 4.5.2(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(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)))
'@nuxtjs/mdc': 0.23.1(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.62.4)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0)))
@@ -12737,7 +12740,7 @@ snapshots:
- vite
- webpack
- '@nuxt/ui@4.10.0(ae7cefd1db2330b460f8c2d512a8cf44)':
+ '@nuxt/ui@4.10.0(1c051f5eaf5cc4a909f4318924c3960a)':
dependencies:
'@floating-ui/dom': 1.8.0
'@iconify/vue': 5.0.1(vue@3.5.41(typescript@6.0.3))
@@ -12806,7 +12809,7 @@ snapshots:
optionalDependencies:
'@internationalized/date': 3.12.3
'@internationalized/number': 3.6.7
- '@nuxt/content': 3.15.2(@libsql/client@0.17.4)(@oxc-project/types@0.144.0)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(supports-color@10.2.2)(valibot@1.4.2(typescript@6.0.3))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))
+ '@nuxt/content': 3.15.2(patch_hash=c5b7bb228dea1e45f274e367308107c2f611ad739b4bb5742478643be3a95f9f)(@libsql/client@0.17.4)(@oxc-project/types@0.144.0)(better-sqlite3@13.0.3)(drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260702.1)(@libsql/client@0.17.4)(better-sqlite3@13.0.3))(esbuild@0.28.2)(magic-string@1.2.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.4)(rollup@4.62.4)(supports-color@10.2.2)(valibot@1.4.2(typescript@6.0.3))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.50.0)(tsx@4.23.12)(yaml@2.9.0))
ai: 7.0.66(zod@4.4.3)
valibot: 1.4.2(typescript@6.0.3)
zod: 4.4.3
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index df9cd1f64..3d46a1065 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -31,3 +31,9 @@ minimumReleaseAgeExclude:
- vue
- '@vue/*'
- '@nuxtjs/mdc@0.23.1'
+
+patchedDependencies:
+ # Keys the git checkout directory on the resolved ref, so the `nuxt/cli` sources on
+ # `3.x` and `main` stop overwriting each other's working tree.
+ # https://github.com/nuxt/content/pull/3839
+ '@nuxt/content': patches/@nuxt__content.patch
diff --git a/shared/utils/cli-docs.ts b/shared/utils/cli-docs.ts
new file mode 100644
index 000000000..bdee75cba
--- /dev/null
+++ b/shared/utils/cli-docs.ts
@@ -0,0 +1,22 @@
+// Single source of truth for which `nuxt/cli` ref backs each docs version's
+// command reference. `3.x` is the released CLI; `main` is where v4 is developed and
+// where `@nuxt/cli-nightly` is published from, which is what `nuxt-nightly@5.x`
+// depends on, so a 5.x reader sees the CLI they actually install. Consumed by two
+// surfaces that must agree, or pages resolve their assets against a different ref
+// than they were parsed from:
+//
+// - content.config.ts → the `cliV*Source` collection sources
+// - nuxt.config.ts → `content:file:beforeParse` asset URL rewriting
+export const CLI_DOCS_REPO = 'nuxt/cli'
+
+// TODO: repoint docsv4 to `main` when the CLI v4 releases.
+export const CLI_DOCS_REFS = {
+ docsv3: '3.x',
+ docsv4: '3.x',
+ docsv5: 'main'
+} as const
+
+// Command docs live at `docs/` in `nuxt/cli` but mount under the API section of
+// each version tree, so this is both the source `prefix` and the marker that
+// identifies a CLI-sourced file inside the parse hook.
+export const CLI_DOCS_PREFIX = '4.api/4.commands'