diff --git a/.changeset/brave-cycles-kick.md b/.changeset/brave-cycles-kick.md new file mode 100644 index 000000000..cfb936443 --- /dev/null +++ b/.changeset/brave-cycles-kick.md @@ -0,0 +1,5 @@ +--- +"@rspress/plugin-auto-nav-sidebar": minor +--- + +support \_meta.{js,ts} diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/_meta.json b/e2e/fixtures/auto-nav-sidebar-js/doc/_meta.json new file mode 100644 index 000000000..a5dc6179e --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/_meta.json @@ -0,0 +1,10 @@ +[ + { + "text": "Guide", + "link": "/guide/" + }, + { + "text": "Api", + "link": "/api/" + } +] diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/_meta.json b/e2e/fixtures/auto-nav-sidebar-js/doc/api/_meta.json new file mode 100644 index 000000000..1520f9be0 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/_meta.json @@ -0,0 +1,19 @@ +[ + { + "type": "file", + "name": "index", + "label": "API Overview" + }, + { + "type": "dir", + "name": "config", + "label": "Config" + }, + { + "type": "dir", + "name": "client-api", + "label": "Client API" + }, + "commands", + "single-page" +] diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/_meta.ts b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/_meta.ts new file mode 100644 index 000000000..166e8b304 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/_meta.ts @@ -0,0 +1,13 @@ +export default [ + { + type: 'file', + name: 'index', + label: 'Client API Overview', + }, + { + type: 'file', + name: 'api-runtime', + overviewHeaders: [], + }, + 'api-components', +]; diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/api-components.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/api-components.mdx new file mode 100644 index 000000000..761580be0 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/api-components.mdx @@ -0,0 +1,5 @@ +# Components + +## Usage + +## Example diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/api-runtime.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/api-runtime.mdx new file mode 100644 index 000000000..6a68d3562 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/api-runtime.mdx @@ -0,0 +1,5 @@ +# Runtime API + +## usePageData + +## useLang diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/index.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/index.mdx new file mode 100644 index 000000000..41fc3a5f6 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/client-api/index.mdx @@ -0,0 +1,3 @@ +--- +overview: true +--- diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/commands.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/commands.mdx new file mode 100644 index 000000000..7950df784 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/commands.mdx @@ -0,0 +1,5 @@ +# Commands + +## rspress dev + +## rspress build diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config.mdx new file mode 100644 index 000000000..36ed94e7d --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config.mdx @@ -0,0 +1,4 @@ +--- +overview: true +overviewHeaders: [2] +--- diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/_meta.js b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/_meta.js new file mode 100644 index 000000000..050e90db2 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/_meta.js @@ -0,0 +1,14 @@ +export default [ + 'config-basic', + 'config-theme', + 'config-frontmatter', + { + type: 'file', + name: 'config-build', + overviewHeaders: [2, 3], + }, + { + type: 'file', + name: 'config-extname.json', + }, +]; diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-basic.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-basic.mdx new file mode 100644 index 000000000..ac39e2ba1 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-basic.mdx @@ -0,0 +1,5 @@ +# Basic Config + +## root + +## logoText diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-build.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-build.mdx new file mode 100644 index 000000000..5f6ffa103 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-build.mdx @@ -0,0 +1,9 @@ +# Build Config + +## builderConfig + +### Default Config + +## markdown + +### markdown.remarkPlugins diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-extname.json.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-extname.json.mdx new file mode 100644 index 000000000..cdb36b2d0 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-extname.json.mdx @@ -0,0 +1 @@ +# Extname Config diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-frontmatter.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-frontmatter.mdx new file mode 100644 index 000000000..4cac3ed15 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-frontmatter.mdx @@ -0,0 +1,9 @@ +--- +overviewHeaders: [] +--- + +# Front Matter Config + +## title + +## description diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-theme.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-theme.mdx new file mode 100644 index 000000000..f99473c33 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/config/config-theme.mdx @@ -0,0 +1,5 @@ +# Theme Config + +## nav + +## sidebar diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/index.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/index.mdx new file mode 100644 index 000000000..41fc3a5f6 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/index.mdx @@ -0,0 +1,3 @@ +--- +overview: true +--- diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/api/single-page.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/api/single-page.mdx new file mode 100644 index 000000000..c38eed3e7 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/api/single-page.mdx @@ -0,0 +1,11 @@ +--- +overviewHeaders: [] +--- + +# Single + +## Single-1 + +### Single-11 + +## Single-2 diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/guide/_meta.json b/e2e/fixtures/auto-nav-sidebar-js/doc/guide/_meta.json new file mode 100644 index 000000000..9aa97c7e5 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/guide/_meta.json @@ -0,0 +1,8 @@ +[ + "index", + { + "type": "dir", + "name": "advanced", + "label": "Advanced" + } +] diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/guide/advanced/plugin.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/guide/advanced/plugin.mdx new file mode 100644 index 000000000..edd2cbeca --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/guide/advanced/plugin.mdx @@ -0,0 +1,3 @@ +# Plugin + +This plugin is a simple plugin that allows you to create a custom command that will send a message to a channel of your choice. diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/guide/index.mdx b/e2e/fixtures/auto-nav-sidebar-js/doc/guide/index.mdx new file mode 100644 index 000000000..8c0d02fad --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/guide/index.mdx @@ -0,0 +1 @@ +# Guide diff --git a/e2e/fixtures/auto-nav-sidebar-js/doc/index.md b/e2e/fixtures/auto-nav-sidebar-js/doc/index.md new file mode 100644 index 000000000..29658341f --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/doc/index.md @@ -0,0 +1 @@ +# Hello World diff --git a/e2e/fixtures/auto-nav-sidebar-js/package.json b/e2e/fixtures/auto-nav-sidebar-js/package.json new file mode 100644 index 000000000..9f5d81f53 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/package.json @@ -0,0 +1,16 @@ +{ + "name": "@rspress-fixture/rspress-auto-nav-sidebar-js", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "rspress dev", + "build": "rspress build", + "preview": "rspress preview" + }, + "dependencies": { + "rspress": "workspace:*" + }, + "devDependencies": { + "@types/node": "^14" + } +} diff --git a/e2e/fixtures/auto-nav-sidebar-js/rspress.config.ts b/e2e/fixtures/auto-nav-sidebar-js/rspress.config.ts new file mode 100644 index 000000000..85cca368c --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/rspress.config.ts @@ -0,0 +1,6 @@ +import * as path from 'path'; +import { defineConfig } from 'rspress/config'; + +export default defineConfig({ + root: path.join(__dirname, 'doc'), +}); diff --git a/e2e/fixtures/auto-nav-sidebar-js/tsconfig.json b/e2e/fixtures/auto-nav-sidebar-js/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/e2e/fixtures/auto-nav-sidebar-js/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index f4335789d..3658372f9 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -8,10 +8,12 @@ import chalk from 'chalk'; import { loadConfigFile } from './config/loadConfigFile'; import update from './update'; +// for config reload, restart the server const CONFIG_FILES = [ 'rspress.config.ts', 'rspress.config.js', '_meta.json', + '_meta.js', 'i18n.json', ]; diff --git a/packages/plugin-auto-nav-sidebar/src/walk.ts b/packages/plugin-auto-nav-sidebar/src/walk.ts index 9fc9b7de3..0fbf01620 100644 --- a/packages/plugin-auto-nav-sidebar/src/walk.ts +++ b/packages/plugin-auto-nav-sidebar/src/walk.ts @@ -14,6 +14,84 @@ import { import { NavMeta, SideMeta } from './type'; import { detectFilePath, extractTitleAndOverviewHeaders } from './utils'; import { logger } from '@rspress/shared/logger'; +import { loadConfig } from '@rspress/shared/node-utils'; + +// Get the sidebar config from the `_meta.json` file +async function getSideMetaFromMetaFile( + workDir: string, + rootDir: string, +): Promise { + const metaJsFilePath = path.resolve(workDir, '_meta.js'); + if (await fs.exists(metaJsFilePath)) { + const { content: sideMeta } = (await loadConfig({ + path: metaJsFilePath, + })) as any; + return sideMeta; + } + + const metaJsonFilePath = path.resolve(workDir, '_meta.json'); + if (await fs.exists(metaJsonFilePath)) { + const sideMeta = (await fs.readJSON(metaJsonFilePath, 'utf8')) as SideMeta; + return sideMeta; + } + + // If `_meta.js` or `_meta.json` file doesn't exist, we will generate the sidebar config from the directory structure. + let subItems = await fs.readdir(workDir); + // If there exists a file with the same name of the directory folder + // we don't need to generate SideMeta for this single file + subItems = subItems.filter(item => { + const hasExtension = ['.md', '.mdx'].some(ext => item.endsWith(ext)); + const hasSameBaseName = subItems.some(elem => { + const baseName = elem.replace(/\.[^/.]+$/, ''); + return baseName === item.replace(/\.[^/.]+$/, '') && elem !== item; + }); + return !(hasExtension && hasSameBaseName); + }); + const sideMeta = ( + await Promise.all( + subItems.map(async item => { + // Fix https://github.com/web-infra-dev/rspress/issues/346 + if ( + item === '_meta.json' || + item === '_meta.js' || + item === '_meta.ts' + ) { + return null; + } + const stat = await fs.stat(path.join(workDir, item)); + // If the item is a directory, we will transform it to a object with `type` and `name` property. + if (stat.isDirectory()) { + // set H1 title to sidebar label when have same name md/mdx file + const mdFilePath = path.join(workDir, `${item}.md`); + const mdxFilePath = path.join(workDir, `${item}.mdx`); + let label = item; + + const setLabelFromFilePath = async (filePath: string) => { + const { title } = await extractTitleAndOverviewHeaders( + filePath, + rootDir, + ); + label = title; + }; + + if (fs.existsSync(mdxFilePath)) { + await setLabelFromFilePath(mdxFilePath); + } else if (fs.existsSync(mdFilePath)) { + await setLabelFromFilePath(mdFilePath); + } + + return { + type: 'dir', + name: item, + label, + }; + } + return item; + }), + ) + ).filter(Boolean) as SideMeta; + return sideMeta; +} export async function scanSideMeta( workDir: string, @@ -32,64 +110,7 @@ export async function scanSideMeta( const metaFile = path.resolve(workDir, '_meta.json'); // Fix the windows path const relativePath = slash(path.relative(rootDir, workDir)); - let sideMeta: SideMeta | undefined; - // Get the sidebar config from the `_meta.json` file - try { - // Don't use require to avoid require cache, which make hmr not work. - sideMeta = (await fs.readJSON(metaFile, 'utf8')) as SideMeta; - } catch (e) { - // If the `_meta.json` file doesn't exist, we will generate the sidebar config from the directory structure. - let subItems = await fs.readdir(workDir); - // If there exists a file with the same name of the directory folder - // we don't need to generate SideMeta for this single file - subItems = subItems.filter(item => { - const hasExtension = ['.md', '.mdx'].some(ext => item.endsWith(ext)); - const hasSameBaseName = subItems.some(elem => { - const baseName = elem.replace(/\.[^/.]+$/, ''); - return baseName === item.replace(/\.[^/.]+$/, '') && elem !== item; - }); - return !(hasExtension && hasSameBaseName); - }); - sideMeta = ( - await Promise.all( - subItems.map(async item => { - // Fix https://github.com/web-infra-dev/rspress/issues/346 - if (item === '_meta.json') { - return null; - } - const stat = await fs.stat(path.join(workDir, item)); - // If the item is a directory, we will transform it to a object with `type` and `name` property. - if (stat.isDirectory()) { - // set H1 title to sidebar label when have same name md/mdx file - const mdFilePath = path.join(workDir, `${item}.md`); - const mdxFilePath = path.join(workDir, `${item}.mdx`); - let label = item; - - const setLabelFromFilePath = async (filePath: string) => { - const { title } = await extractTitleAndOverviewHeaders( - filePath, - rootDir, - ); - label = title; - }; - - if (fs.existsSync(mdxFilePath)) { - await setLabelFromFilePath(mdxFilePath); - } else if (fs.existsSync(mdFilePath)) { - await setLabelFromFilePath(mdFilePath); - } - - return { - type: 'dir', - name: item, - label, - }; - } - return item; - }), - ) - ).filter(Boolean) as SideMeta; - } + const sideMeta: SideMeta = await getSideMetaFromMetaFile(workDir, rootDir); const sidebarFromMeta: ( | SidebarGroup diff --git a/packages/shared/src/node-utils.ts b/packages/shared/src/node-utils.ts index 4d50255fa..cbc3337d5 100644 --- a/packages/shared/src/node-utils.ts +++ b/packages/shared/src/node-utils.ts @@ -1 +1,2 @@ export * from './node-utils/loadFrontMatter'; +export * from './node-utils/loadConfig'; diff --git a/packages/shared/src/node-utils/loadConfig.ts b/packages/shared/src/node-utils/loadConfig.ts new file mode 100644 index 000000000..d80ee834c --- /dev/null +++ b/packages/shared/src/node-utils/loadConfig.ts @@ -0,0 +1,2 @@ +// TODO: load config file logic of _meta.js +export { loadConfig } from '@rsbuild/core'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09ac43458..a0fe908ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,6 +104,16 @@ importers: specifier: ^14 version: 14.0.0 + e2e/fixtures/auto-nav-sidebar-js: + dependencies: + rspress: + specifier: workspace:* + version: link:../../../packages/cli + devDependencies: + '@types/node': + specifier: ^14 + version: 14.0.0 + e2e/fixtures/auto-nav-sidebar-no-meta: dependencies: rspress: