Skip to content

Commit 332e06d

Browse files
authored
feat: source cli command docs from nuxt/cli (#2392)
1 parent a38a8bd commit 332e06d

8 files changed

Lines changed: 149 additions & 17 deletions

File tree

app/components/DocsProseImg.vue

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script setup lang="ts">
2+
import { ProseImg } from '#components'
3+
import { CLI_DOCS_REPO } from '#shared/utils/cli-docs'
4+
5+
const props = defineProps({
6+
src: {
7+
type: String,
8+
default: ''
9+
},
10+
alt: {
11+
type: String,
12+
default: ''
13+
},
14+
width: {
15+
type: [String, Number],
16+
default: undefined
17+
},
18+
height: {
19+
type: [String, Number],
20+
default: undefined
21+
}
22+
})
23+
24+
// The CLI's terminal captures are SVGs carrying both colour schemes via
25+
// `prefers-color-scheme`, some animated with SMIL, and rasterising them through IPX
26+
// drops both. `none` keeps the URL untouched while retaining the default styling and
27+
// zoom, which a plain `<img>` would lose.
28+
const provider = computed(() => props.src.startsWith(`https://raw.githubusercontent.com/${CLI_DOCS_REPO}/`) ? 'none' : undefined)
29+
</script>
30+
31+
<template>
32+
<ProseImg
33+
:src="src"
34+
:alt="alt"
35+
:width="width"
36+
:height="height"
37+
:provider="provider"
38+
/>
39+
</template>

app/pages/docs/[...slug].vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { joinURL } from 'ufo'
44
import type { ContentNavigationItem } from '@nuxt/content'
55
import { findPageBreadcrumb } from '@nuxt/content/utils'
66
import { mapContentNavigation } from '@nuxt/ui/utils/content'
7+
import { DocsProseImg } from '#components'
78
import { SUPPORTED_DOCS_PATH_REGEX } from '#shared/utils/docs'
89
910
definePageMeta({
@@ -250,7 +251,7 @@ const noRightAside = computed(() => route.path.includes('/examples/'))
250251
</UPageHeader>
251252

252253
<UPageBody>
253-
<ContentRenderer v-if="page.body" :value="page" />
254+
<ContentRenderer v-if="page.body" :value="page" :components="{ img: DocsProseImg }" />
254255
<div>
255256
<Feedback :page="page" />
256257
<USeparator class="mt-6 mb-10">

content.config.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,51 @@
11
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
2+
import { CLI_DOCS_PREFIX, CLI_DOCS_REFS, CLI_DOCS_REPO } from './shared/utils/cli-docs'
23

34
const docsV3Source = {
45
cwd: process.env.NUXT_V3_PATH ?? undefined,
56
repository: !process.env.NUXT_V3_PATH ? 'https://github.com/nuxt/nuxt/tree/3.x' : undefined,
67
include: 'docs/**/*',
7-
exclude: ['docs/**/*.json'],
8+
exclude: ['docs/**/*.json', 'docs/4.api/4.commands/**'],
89
prefix: '/docs/3.x'
910
}
1011

1112
const docsV4Source = {
1213
cwd: process.env.NUXT_V4_PATH ?? undefined,
1314
repository: !process.env.NUXT_V4_PATH ? 'https://github.com/nuxt/nuxt/tree/4.x' : undefined,
1415
include: 'docs/**/*',
15-
exclude: ['docs/**/*.json'],
16+
exclude: ['docs/**/*.json', 'docs/4.api/4.commands/**'],
1617
prefix: '/docs/4.x'
1718
}
1819

1920
const docsV5Source = {
2021
cwd: process.env.NUXT_V5_PATH ?? undefined,
2122
repository: !process.env.NUXT_V5_PATH ? 'https://github.com/nuxt/nuxt/tree/main' : undefined,
2223
include: 'docs/**/*',
23-
exclude: ['docs/**/*.json'],
24+
exclude: ['docs/**/*.json', 'docs/4.api/4.commands/**'],
2425
prefix: '/docs/5.x'
2526
}
2627

28+
const cliV3Source = {
29+
cwd: process.env.NUXT_CLI_PATH ?? undefined,
30+
repository: !process.env.NUXT_CLI_PATH ? `https://github.com/${CLI_DOCS_REPO}/tree/${CLI_DOCS_REFS.docsv3}` : undefined,
31+
include: 'docs/**/*',
32+
prefix: `/docs/3.x/${CLI_DOCS_PREFIX}`
33+
}
34+
35+
const cliV4Source = {
36+
cwd: process.env.NUXT_CLI_PATH ?? undefined,
37+
repository: !process.env.NUXT_CLI_PATH ? `https://github.com/${CLI_DOCS_REPO}/tree/${CLI_DOCS_REFS.docsv4}` : undefined,
38+
include: 'docs/**/*',
39+
prefix: `/docs/4.x/${CLI_DOCS_PREFIX}`
40+
}
41+
42+
const cliV5Source = {
43+
cwd: process.env.NUXT_CLI_PATH ?? undefined,
44+
repository: !process.env.NUXT_CLI_PATH ? `https://github.com/${CLI_DOCS_REPO}/tree/${CLI_DOCS_REFS.docsv5}` : undefined,
45+
include: 'docs/**/*',
46+
prefix: `/docs/5.x/${CLI_DOCS_PREFIX}`
47+
}
48+
2749
const examplesV3Source = {
2850
cwd: process.env.NUXT_EXAMPLES_PATH ?? undefined,
2951
repository: !process.env.NUXT_EXAMPLES_PATH ? 'https://github.com/nuxt/examples' : undefined,
@@ -214,7 +236,7 @@ export default defineContentConfig({
214236
}),
215237
docsv5: defineCollection({
216238
type: 'page',
217-
source: [docsV5Source, examplesV5Source],
239+
source: [docsV5Source, cliV5Source, examplesV5Source],
218240
schema: z.object({
219241
titleTemplate: z.string().optional(),
220242
links: z.array(Button),
@@ -223,7 +245,7 @@ export default defineContentConfig({
223245
}),
224246
docsv4: defineCollection({
225247
type: 'page',
226-
source: [docsV4Source, examplesV4Source],
248+
source: [docsV4Source, cliV4Source, examplesV4Source],
227249
schema: z.object({
228250
titleTemplate: z.string().optional(),
229251
links: z.array(Button),
@@ -232,7 +254,7 @@ export default defineContentConfig({
232254
}),
233255
docsv3: defineCollection({
234256
type: 'page',
235-
source: [docsV3Source, examplesV3Source],
257+
source: [docsV3Source, cliV3Source, examplesV3Source],
236258
schema: z.object({
237259
titleTemplate: z.string().optional(),
238260
links: z.array(Button),

nuxt.config.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createResolver } from 'nuxt/kit'
22
import { parseMdc } from './helpers/mdc-parser.mjs'
3+
import { CLI_DOCS_PREFIX, CLI_DOCS_REFS, CLI_DOCS_REPO } from './shared/utils/cli-docs'
34

45
const { resolve } = createResolver(import.meta.url)
56

@@ -246,10 +247,10 @@ export default defineNuxtConfig({
246247
'/docs/3.x/api/kit': { redirect: '/docs/3.x/api/kit/modules', prerender: false },
247248
'/docs/4.x/api/kit': { redirect: '/docs/4.x/api/kit/modules', prerender: false },
248249
'/docs/5.x/api/kit': { redirect: '/docs/5.x/api/kit/modules', prerender: false },
249-
'/docs/api/commands': { redirect: '/docs/api/commands/dev', prerender: false },
250-
'/docs/3.x/api/commands': { redirect: '/docs/3.x/api/commands/dev', prerender: false },
251-
'/docs/4.x/api/commands': { redirect: '/docs/4.x/api/commands/dev', prerender: false },
252-
'/docs/5.x/api/commands': { redirect: '/docs/5.x/api/commands/dev', prerender: false },
250+
'/docs/api/commands': { redirect: '/docs/api/commands/overview', prerender: false },
251+
'/docs/3.x/api/commands': { redirect: '/docs/3.x/api/commands/overview', prerender: false },
252+
'/docs/4.x/api/commands': { redirect: '/docs/4.x/api/commands/overview', prerender: false },
253+
'/docs/5.x/api/commands': { redirect: '/docs/5.x/api/commands/overview', prerender: false },
253254
'/docs/api/advanced': { redirect: '/docs/api/advanced/hooks', prerender: false },
254255
'/docs/3.x/api/advanced': { redirect: '/docs/3.x/api/advanced/hooks', prerender: false },
255256
'/docs/4.x/api/advanced': { redirect: '/docs/4.x/api/advanced/hooks', prerender: false },
@@ -537,6 +538,15 @@ export default defineNuxtConfig({
537538
},
538539
hooks: {
539540
'content:file:beforeParse': async ({ file }) => {
541+
// Command docs are served from `nuxt/cli`, but Content only ships the markdown,
542+
// not the terminal captures committed beside it. Root-relative image sources are
543+
// repo-relative, so resolve them against the ref this page was parsed from: a
544+
// hardcoded ref would serve `main`'s captures on the 3.x tree.
545+
const collection = file.id.split('/')[0] as keyof typeof CLI_DOCS_REFS
546+
if (file.id.includes(`/${CLI_DOCS_PREFIX}/`) && CLI_DOCS_REFS[collection]) {
547+
const base = `https://raw.githubusercontent.com/${CLI_DOCS_REPO}/${CLI_DOCS_REFS[collection]}`
548+
file.body = file.body.replaceAll(/(!\[[^\]]*\]\()\/(?!\/)/g, `$1${base}/`)
549+
}
540550
if (file.id.startsWith('docsv5/')) {
541551
file.body = file.body.replaceAll(/\(\/docs\/(?!\d\.x)/g, '(/docs/5.x/')
542552
// Pages that only exist on main (5.x) but are linked as /docs/4.x/* from
@@ -556,6 +566,9 @@ export default defineNuxtConfig({
556566
if (file.id.startsWith('docsv4/')) {
557567
file.body = file.body.replaceAll(/\(\/docs\/(?!\d\.x)/g, '(/docs/4.x/')
558568
}
569+
if (file.id.startsWith('docsv3/')) {
570+
file.body = file.body.replaceAll(/\(\/docs\/(?!\d\.x)/g, '(/docs/3.x/')
571+
}
559572
},
560573
'content:file:afterParse': async ({ file, content }) => {
561574
if (file.id === 'index/index.yml') {
@@ -631,7 +644,11 @@ export default defineNuxtConfig({
631644
},
632645
image: {
633646
format: ['webp', 'jpeg', 'jpg', 'png', 'svg'],
634-
provider: 'ipx'
647+
provider: 'ipx',
648+
// Opt-out escape hatch for images IPX must not touch, used by `DocsProseImg`.
649+
providers: {
650+
none: { provider: 'none' }
651+
}
635652
},
636653
llms: {
637654
domain: 'https://nuxt.com',

patches/@nuxt__content.patch

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
diff --git a/dist/module.mjs b/dist/module.mjs
2+
index f1516bab826818eacc9088a24df96cf4ff895502..b14f1f4579af1e502fc57e9834e88feb916222cc 100644
3+
--- a/dist/module.mjs
4+
+++ b/dist/module.mjs
5+
@@ -2007,11 +2007,16 @@ function defineGitSource(source) {
6+
const repository = source?.repository && gitUrlParse(source.repository.url);
7+
if (repository) {
8+
const { source: gitSource, owner, name } = repository;
9+
- resolvedSource.cwd = join(rootDir, ".data", "content", `${gitSource}-${owner}-${name}-${repository.ref || "main"}`);
10+
let ref;
11+
if (source.repository.branch && source.repository.tag) {
12+
throw new Error("Cannot specify both branch and tag for git repository. Please specify one of `branch` or `tag`.");
13+
}
14+
+ // Keyed on the resolved ref so two refs of one repo do not share a checkout.
15+
+ // Backport of https://github.com/nuxt/content/pull/3839; drop on the next release.
16+
+ const resolvedRef = source.repository.branch || source.repository.tag || repository.ref || "main";
17+
+ const refKey = /^[\w.-]+$/.test(resolvedRef) ? resolvedRef : `${resolvedRef.replace(/[^\w.-]+/g, "-")}-${hash(resolvedRef).slice(0, 8)}`;
18+
+ const refPrefix = source.repository.tag ? "tag-" : "";
19+
+ resolvedSource.cwd = join(rootDir, ".data", "content", `${gitSource}-${owner}-${name}-${refPrefix}${refKey}`);
20+
if (source.repository.branch) ref = { branch: source.repository.branch };
21+
if (source.repository.tag) ref = { tag: source.repository.tag };
22+
if (!source.repository?.auth && source.authBasic) {

pnpm-lock.yaml

Lines changed: 8 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ minimumReleaseAgeExclude:
3131
- vue
3232
- '@vue/*'
3333
- '@nuxtjs/mdc@0.23.1'
34+
35+
patchedDependencies:
36+
# Keys the git checkout directory on the resolved ref, so the `nuxt/cli` sources on
37+
# `3.x` and `main` stop overwriting each other's working tree.
38+
# https://github.com/nuxt/content/pull/3839
39+
'@nuxt/content': patches/@nuxt__content.patch

shared/utils/cli-docs.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Single source of truth for which `nuxt/cli` ref backs each docs version's
2+
// command reference. `3.x` is the released CLI; `main` is where v4 is developed and
3+
// where `@nuxt/cli-nightly` is published from, which is what `nuxt-nightly@5.x`
4+
// depends on, so a 5.x reader sees the CLI they actually install. Consumed by two
5+
// surfaces that must agree, or pages resolve their assets against a different ref
6+
// than they were parsed from:
7+
//
8+
// - content.config.ts → the `cliV*Source` collection sources
9+
// - nuxt.config.ts → `content:file:beforeParse` asset URL rewriting
10+
export const CLI_DOCS_REPO = 'nuxt/cli'
11+
12+
// TODO: repoint docsv4 to `main` when the CLI v4 releases.
13+
export const CLI_DOCS_REFS = {
14+
docsv3: '3.x',
15+
docsv4: '3.x',
16+
docsv5: 'main'
17+
} as const
18+
19+
// Command docs live at `docs/` in `nuxt/cli` but mount under the API section of
20+
// each version tree, so this is both the source `prefix` and the marker that
21+
// identifies a CLI-sourced file inside the parse hook.
22+
export const CLI_DOCS_PREFIX = '4.api/4.commands'

0 commit comments

Comments
 (0)