diff --git a/.gitignore b/.gitignore index 0e9bd5c8..f71faa25 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ dist node_modules TODOs.md -.vscode \ No newline at end of file +.vscode +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index c32e32a5..0c8f12be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +## [0.21.3](https://github.com/vuejs/vitepress/compare/v0.21.2...v0.21.3) (2022-01-06) + +### Bug Fixes + +- prioritize vue installed in user project root ([9b3243b](https://github.com/vuejs/vitepress/commit/9b3243b75752209943af5b247f5d38e641d4ff6d)) + +## [0.21.2](https://github.com/vuejs/vitepress/compare/v0.21.1...v0.21.2) (2022-01-06) + +## [0.21.1](https://github.com/vuejs/vitepress/compare/v0.21.0...v0.21.1) (2022-01-06) + +### Performance Improvements + +- do not include head config in client bundle for production ([6f3a96f](https://github.com/vuejs/vitepress/commit/6f3a96f06daec4baad4420b54137a7afb1512e7f)) + +# [0.21.0](https://github.com/vuejs/vitepress/compare/v0.20.10...v0.21.0) (2022-01-06) + +### Bug Fixes + +- Chinese file link build failed ([#425](https://github.com/vuejs/vitepress/issues/425)) ([ae029ae](https://github.com/vuejs/vitepress/commit/ae029ae9e17fa6df1d2f89043f1891271e9c5b9b)), closes [#424](https://github.com/vuejs/vitepress/issues/424) +- initial render of 404 pages ([#418](https://github.com/vuejs/vitepress/issues/418)) ([a3bf52f](https://github.com/vuejs/vitepress/commit/a3bf52fed53e82b9756c844f6bdd576662d2e726)) +- remove `.` for mjs in `supportedConfigExtensions` ([#447](https://github.com/vuejs/vitepress/issues/447)) ([fb6a4ad](https://github.com/vuejs/vitepress/commit/fb6a4ad3e008af9ce4393fb3ca37645f4efba951)) +- **serve:** respect base config in serve mode ([#470](https://github.com/vuejs/vitepress/issues/470)) ([08a0b12](https://github.com/vuejs/vitepress/commit/08a0b129928cef44e613ff410d769a7ac7bf5fa3)), closes [#416](https://github.com/vuejs/vitepress/issues/416) +- set tempDir outside package root ([#439](https://github.com/vuejs/vitepress/issues/439)) ([bd35451](https://github.com/vuejs/vitepress/commit/bd35451ed42d7b5c47e2b49a7e659807cd7d7a0c)), closes [#435](https://github.com/vuejs/vitepress/issues/435) +- use algolia search lang ([#459](https://github.com/vuejs/vitepress/issues/459)) ([444562c](https://github.com/vuejs/vitepress/commit/444562c3a763bab7a9c0ebfca5eec635e142a61f)) + +### Features + +- add details custom container ([#455](https://github.com/vuejs/vitepress/issues/455)) ([a8f147f](https://github.com/vuejs/vitepress/commit/a8f147f153efdd17989a02eb620c3ae9ab0d13dd)) +- catch localhost links as dead links ([7387649](https://github.com/vuejs/vitepress/commit/7387649ff7c621402e49e26493b4eed25006fb4b)) +- expose `__path` and `__relativePath` on md instance for md plugins ([4cec660](https://github.com/vuejs/vitepress/commit/4cec660401d8d01830e5a11b9c66bc0ac5a935db)) +- improve typescript support for config file, add `defineConfigWithTheme` ([#465](https://github.com/vuejs/vitepress/issues/465)) ([ba41bb9](https://github.com/vuejs/vitepress/commit/ba41bb90551c01b9f84de2d2d3bc1920ce2ebe93)) +- properly remove `{#custom-anchor}` syntax in headers ([6120da2](https://github.com/vuejs/vitepress/commit/6120da25a87f6bec3918be804e95f2b3c8afb6c8)) +- user configurable `outDir` ([#448](https://github.com/vuejs/vitepress/issues/448)) ([5b04bb9](https://github.com/vuejs/vitepress/commit/5b04bb9eb5ced720414f4b0d729fde36432dd451)) + ## [0.20.10](https://github.com/vuejs/vitepress/compare/v0.20.9...v0.20.10) (2021-12-25) ### Features diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index ad95abb7..3b4f5aa2 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,5 +1,6 @@ +import { defineConfig } from '../../src/node' -export default { +export default defineConfig({ lang: 'en-US', title: 'VitePress', description: 'Vite & Vue powered static site generator.', @@ -51,7 +52,7 @@ export default { '/': getGuideSidebar() } } -} +}) function getGuideSidebar() { return [ diff --git a/docs/config/algolia-search.md b/docs/config/algolia-search.md index 9f5208f6..3ee35eb1 100644 --- a/docs/config/algolia-search.md +++ b/docs/config/algolia-search.md @@ -47,41 +47,4 @@ module.exports = { } ``` -VitePress will automatically add a `language` _facetFilter_ to the `searchParameters.facetFilter` array with the correct language value. **Make sure to properly configure your DocSearch config as well** by adding `language` as a _custom attribute for faceting_ and by setting it based on the `lang` attribute of the `` element. Here is a short example of DocSearch config: - -```json -{ - "index_name": "", - "start_urls": [ - { - "url": "" - } - ], - "stop_urls": ["(?:(? .sidebar-links > .sidebar-link .sidebar-link-item.active", - "global": true, - "default_value": "Documentation" - }, - "lvl1": ".content h1", - "lvl2": ".content h2", - "lvl3": ".content h3", - "lvl4": ".content h4", - "lvl5": ".content h5", - "lvl6": ".content p, .content li", - "text": ".content [class^=language-]", - "language": { - "selector": "/html/@lang", - "type": "xpath", - "global": true, - "default_value": "en-US" - } - }, - "custom_settings": { - "attributesForFaceting": ["language"] - } -} -``` - -You can take a look at the [DocSearch config used by Vue Router](https://github.com/algolia/docsearch-configs/blob/master/configs/next_router_vuejs.json) for a complete example. +VitePress will automatically add a `lang` _facetFilter_ to the `searchParameters.facetFilter` array with the correct language value. Algolia automatically adds the correct facet filter based on the `lang` attribute on the `` tag. This will match search results with the currently viewed language of the page. diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 7dc8d1e0..8ffaad52 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -1,5 +1,7 @@ # Configuration +## Overview + Without any configuration, the page is pretty minimal, and the user has no way to navigate around the site. To customize your site, let’s first create a `.vitepress` directory inside your docs directory. This is where all VitePress-specific files will be placed. Your project structure is probably like this: ```bash @@ -21,3 +23,57 @@ module.exports = { ``` Check out the [Config Reference](/config/basics) for a full list of options. + +## Config Intellisense + +Since VitePress ships with TypeScript typings, you can leverage your IDE's intellisense with jsdoc type hints: + +```js +/** + * @type {import('vitepress').UserConfig} + */ +const config = { + // ... +} + +export default config +``` + +Alternatively, you can use the `defineConfig` helper at which should provide intellisense without the need for jsdoc annotations: + +```js +import { defineConfig } from 'vitepress' + +export default defineConfig({ + // ... +}) +``` + +VitePress also directly supports TS config files. You can use `.vitepress/config.ts` with the `defineConfig` helper as well. + +## Typed Theme Config + +By default, `defineConfig` helper leverages the theme config type from default theme: + +```ts +import { defineConfig } from 'vitepress' + +export default defineConfig({ + themeConfig: { + // Type is `DefaultTheme.Config` + } +}) +``` + +If you use a custom theme and want type checks for the theme config, you'll need to use `defineConfigWithTheme` instead, and pass the config type for your custom theme via a generic argument: + +```ts +import { defineConfigWithTheme } from 'vitepress' +import { ThemeConfig } from 'your-theme' + +export default defineConfigWithTheme({ + themeConfig: { + // Type is `ThemeConfig` + } +}) +``` diff --git a/docs/guide/deploy.md b/docs/guide/deploy.md index 4221893c..25c3058f 100644 --- a/docs/guide/deploy.md +++ b/docs/guide/deploy.md @@ -38,7 +38,7 @@ $ yarn docs:build $ yarn docs:serve ``` -The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at http://localhost:5000. It's an easy way to check if the production build looks OK in your local environment. +The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at `http://localhost:5000`. It's an easy way to check if the production build looks OK in your local environment. You may configure the port of the server py passing `--port` flag as an argument. @@ -50,7 +50,7 @@ You may configure the port of the server py passing `--port` flag as an argument } ``` -Now the `docs:serve` method will launch the server at http://localhost:8080. +Now the `docs:serve` method will launch the server at `http://localhost:8080`. ## GitHub Pages @@ -136,7 +136,7 @@ deploy: If you are deploying to `https://.gitlab.io//`, for example your repository is at `https://gitlab.com//`, then set `base` to `'//'`. -2. Set `dest` in `.vitepress/config.js` to `public`. +2. Set `outDir` in `.vitepress/config.js` to `../public`. 3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 8436901e..2b891fd3 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -44,7 +44,7 @@ This section will help you build a basic VitePress documentation site from groun $ yarn docs:dev ``` - VitePress will start a hot-reloading development server at http://localhost:3000. + VitePress will start a hot-reloading development server at `http://localhost:3000`. By now, you should have a basic but functional VitePress documentation site. diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md index e0efc2f0..63cc8471 100644 --- a/docs/guide/markdown.md +++ b/docs/guide/markdown.md @@ -138,6 +138,10 @@ This is a warning ::: danger This is a dangerous warning ::: + +::: details +This is a details block, which does not work in Internet Explorer or Edge. +::: ``` **Output** @@ -158,15 +162,25 @@ This is a warning This is a dangerous warning ::: +::: details +This is a details block, which does not work in Internet Explorer or Edge. +::: + ### Custom Title **Input** -```md +````md ::: danger STOP Danger zone, do not proceed ::: + +::: details Click me to view the code +```js +console.log('Hello, VitePress!') ``` +::: +```` **Output** @@ -174,6 +188,12 @@ Danger zone, do not proceed Danger zone, do not proceed ::: +::: details Click me to view the code +```js +console.log('Hello, VitePress!') +``` +::: + ## Syntax Highlighting in Code Blocks VitePress uses [Prism](https://prismjs.com/) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block: diff --git a/package.json b/package.json index cc38bfd1..7d2e39f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vitepress", - "version": "0.20.10", + "version": "0.21.3", "description": "Vite & Vue powered static site generator", "main": "dist/node/index.js", "typings": "types/index.d.ts", diff --git a/src/client/app/router.ts b/src/client/app/router.ts index 4f41f2c3..b622b23a 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -26,8 +26,8 @@ const getDefaultRoute = (): Route => ({ component: null, // this will be set upon initial page load, which is before // the app is mounted, so it's guaranteed to be available in - // components - data: null as any + // components. We just need enough data for 404 pages to render. + data: { frontmatter: {} } as any }) interface PageModule { diff --git a/src/client/theme-default/components/AlgoliaSearchBox.vue b/src/client/theme-default/components/AlgoliaSearchBox.vue index a354c8f6..928fd4b1 100644 --- a/src/client/theme-default/components/AlgoliaSearchBox.vue +++ b/src/client/theme-default/components/AlgoliaSearchBox.vue @@ -55,7 +55,7 @@ const { lang } = useData() // if the user has multiple locales, the search results should be filtered // based on the language const facetFilters: string[] = props.multilang - ? ['language:' + lang.value] + ? ['lang:' + lang.value] : [] if (props.options.searchParameters?.facetFilters) { @@ -66,10 +66,10 @@ watch( lang, (newLang, oldLang) => { const index = facetFilters.findIndex( - (filter) => filter === 'language:' + oldLang + (filter) => filter === 'lang:' + oldLang ) if (index > -1) { - facetFilters.splice(index, 1, 'language:' + newLang) + facetFilters.splice(index, 1, 'lang:' + newLang) } } ) diff --git a/src/client/theme-default/components/NavBarTitle.vue b/src/client/theme-default/components/NavBarTitle.vue index 4bfcbb1d..8b9d3709 100644 --- a/src/client/theme-default/components/NavBarTitle.vue +++ b/src/client/theme-default/components/NavBarTitle.vue @@ -24,6 +24,9 @@ const { site, theme, localePath } = useData() font-size: 1.3rem; font-weight: 600; color: var(--c-text); + display: flex; + justify-content: center; + align-items: center; } .nav-bar-title:hover { diff --git a/src/client/theme-default/composables/sideBar.ts b/src/client/theme-default/composables/sideBar.ts index 7781c5c6..96fb6b24 100644 --- a/src/client/theme-default/composables/sideBar.ts +++ b/src/client/theme-default/composables/sideBar.ts @@ -22,7 +22,7 @@ export function useSideBar() { return [] } - // if it's `atuo`, render headers of the current page + // if it's `auto`, render headers of the current page if (frontSidebar === 'auto') { return resolveAutoSidebar(headers, sidebarDepth) } diff --git a/src/client/theme-default/config.ts b/src/client/theme-default/config.ts index d7284871..92869ac4 100644 --- a/src/client/theme-default/config.ts +++ b/src/client/theme-default/config.ts @@ -1,146 +1 @@ -export namespace DefaultTheme { - export interface Config { - logo?: string - nav?: NavItem[] | false - sidebar?: SideBarConfig | MultiSideBarConfig - - /** - * GitHub repository following the format /. - * - * @example `"vuejs/vue-next"` - */ - repo?: string - - /** - * Customize the header label. Defaults to GitHub/Gitlab/Bitbucket - * depending on the provided repo. - * - * @example `"Contribute!"` - */ - repoLabel?: string - - /** - * If your docs are in a different repository from your main project. - * - * @example `"vuejs/docs-next"` - */ - docsRepo?: string - - /** - * If your docs are not at the root of the repo. - * - * @example `"docs"` - */ - docsDir?: string - - /** - * If your docs are in a different branch. Defaults to `master`. - * - * @example `"next"` - */ - docsBranch?: string - - /** - * Enable links to edit pages at the bottom of the page. - */ - editLinks?: boolean - - /** - * Custom text for edit link. Defaults to "Edit this page". - */ - editLinkText?: string - - /** - * Show last updated time at the bottom of the page. Defaults to `false`. - * If given a string, it will be displayed as a prefix (default value: - * "Last Updated"). - */ - lastUpdated?: string | boolean - - prevLinks?: boolean - nextLinks?: boolean - - locales?: Record> - - algolia?: AlgoliaSearchOptions - - carbonAds?: { - carbon: string - custom?: string - placement: string - } - } - - // navbar -------------------------------------------------------------------- - - export type NavItem = NavItemWithLink | NavItemWithChildren - - export interface NavItemBase { - text: string - target?: string - rel?: string - ariaLabel?: string - activeMatch?: string - } - - export interface NavItemWithLink extends NavItemBase { - link: string - } - - export interface NavItemWithChildren extends NavItemBase { - items: NavItemWithLink[] - } - - // sidebar ------------------------------------------------------------------- - - export type SideBarConfig = SideBarItem[] | 'auto' | false - - export interface MultiSideBarConfig { - [path: string]: SideBarConfig - } - - export type SideBarItem = SideBarLink | SideBarGroup - - export interface SideBarLink { - text: string - link: string - } - - export interface SideBarGroup { - text: string - link?: string - - /** - * @default false - */ - collapsable?: boolean - - children: SideBarItem[] - } - - // algolia ------------------------------------------------------------------ - // partially copied from @docsearch/react/dist/esm/DocSearch.d.ts - export interface AlgoliaSearchOptions { - appId?: string - apiKey: string - indexName: string - placeholder?: string - searchParameters?: any - disableUserPersonalization?: boolean - initialQuery?: string - } - - // locales ------------------------------------------------------------------- - - export interface LocaleConfig { - /** - * Text for the language dropdown. - */ - selectText?: string - - /** - * Label for this locale in the language dropdown. - */ - label?: string - } -} +export { DefaultTheme } from '../shared' diff --git a/src/client/theme-default/index.ts b/src/client/theme-default/index.ts index b9c753dd..bcd79051 100644 --- a/src/client/theme-default/index.ts +++ b/src/client/theme-default/index.ts @@ -8,6 +8,7 @@ import { Theme } from 'vitepress' import Layout from './Layout.vue' import NotFound from './NotFound.vue' +export { DefaultTheme } from './config' const theme: Theme = { Layout, NotFound diff --git a/src/client/tsconfig.json b/src/client/tsconfig.json index f7f086aa..92a8af1c 100644 --- a/src/client/tsconfig.json +++ b/src/client/tsconfig.json @@ -12,5 +12,5 @@ "vitepress": ["index.ts"] } }, - "include": [".", "../../types/shared.d.ts"] + "include": ["."] } diff --git a/src/node/alias.ts b/src/node/alias.ts index 7b7aff21..f7ef31a6 100644 --- a/src/node/alias.ts +++ b/src/node/alias.ts @@ -14,13 +14,24 @@ export const DEFAULT_THEME_PATH = path.join(DIST_CLIENT_PATH, 'theme-default') export const SITE_DATA_ID = '@siteData' export const SITE_DATA_REQUEST_PATH = '/' + SITE_DATA_ID -export function resolveAliases(themeDir: string): AliasOptions { +const vueRuntimePath = 'vue/dist/vue.runtime.esm-bundler.js' + +export function resolveAliases(root: string, themeDir: string): AliasOptions { const paths: Record = { '/@theme': themeDir, '/@shared': SHARED_PATH, [SITE_DATA_ID]: SITE_DATA_REQUEST_PATH } + // prioritize vue installed in project root and fallback to + // vue that comes with vitepress itself. + let vuePath + try { + vuePath = require.resolve(vueRuntimePath, { paths: [root] }) + } catch (e) { + vuePath = require.resolve(vueRuntimePath) + } + const aliases: Alias[] = [ ...Object.keys(paths).map((p) => ({ find: p, @@ -40,7 +51,7 @@ export function resolveAliases(themeDir: string): AliasOptions { // vitepress itself { find: /^vue$/, - replacement: require.resolve('vue/dist/vue.runtime.esm-bundler.js') + replacement: vuePath } ] diff --git a/src/node/build/render.ts b/src/node/build/render.ts index d15dab97..dec8323b 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -21,8 +21,20 @@ export async function renderPage( const routePath = `/${page.replace(/\.md$/, '')}` const siteData = resolveSiteDataByRoute(config.site, routePath) router.go(routePath) + // lazy require server-renderer for production build - const content = await require('vue/server-renderer').renderToString(app) + // prioritize project root over vitepress' own dep + let rendererPath + try { + rendererPath = require.resolve('vue/server-renderer', { + paths: [config.root] + }) + } catch (e) { + rendererPath = require.resolve('vue/server-renderer') + } + + // render page + const content = await require(rendererPath).renderToString(app) const pageName = page.replace(/\//g, '_') // server build doesn't need hash diff --git a/src/node/config.ts b/src/node/config.ts index dbd7e88d..092b5dcd 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -14,9 +14,10 @@ import { SiteData, HeadConfig, LocaleConfig, - createLangDictionary + createLangDictionary, + DefaultTheme } from './shared' -import { resolveAliases, APP_PATH, DEFAULT_THEME_PATH } from './alias' +import { resolveAliases, DEFAULT_THEME_PATH } from './alias' import { MarkdownOptions } from './markdown/markdown' import _debug from 'debug' @@ -27,7 +28,7 @@ const debug = _debug('vitepress:config') export type { MarkdownOptions } export interface UserConfig { - extends?: RawConfigExports + extends?: RawConfigExports lang?: string base?: string title?: string @@ -37,7 +38,7 @@ export interface UserConfig { locales?: Record markdown?: MarkdownOptions /** - * Opitons to pass on to `@vitejs/plugin-vue` + * Options to pass on to `@vitejs/plugin-vue` */ vue?: VuePluginOptions /** @@ -47,6 +48,7 @@ export interface UserConfig { srcDir?: string srcExclude?: string[] + outDir?: string shouldPreload?: (link: string, page: string) => boolean /** @@ -64,10 +66,10 @@ export interface UserConfig { cleanUrls?: boolean } -export type RawConfigExports = - | UserConfig - | Promise - | (() => UserConfig | Promise) +export type RawConfigExports = + | UserConfig + | Promise> + | (() => UserConfig | Promise>) export interface SiteConfig extends Pick< @@ -92,7 +94,16 @@ const resolve = (root: string, file: string) => /** * Type config helper */ -export function defineConfig(config: RawConfigExports) { +export function defineConfig(config: UserConfig) { + return config +} + +/** + * Type config helper for custom theme config + */ +export function defineConfigWithTheme( + config: UserConfig +) { return config } @@ -104,6 +115,9 @@ export async function resolveConfig( const [userConfig, configPath] = await resolveUserConfig(root, command, mode) const site = await resolveSiteData(root, userConfig) const srcDir = path.resolve(root, userConfig.srcDir || '.') + const outDir = userConfig.outDir + ? path.resolve(root, userConfig.outDir) + : resolve(root, 'dist') // resolve theme path const userThemeDir = resolve(root, 'theme') @@ -131,11 +145,11 @@ export async function resolveConfig( themeDir, pages, configPath, - outDir: resolve(root, 'dist'), - tempDir: path.resolve(APP_PATH, 'temp'), + outDir, + tempDir: resolve(root, '.temp'), cleanUrls: !!userConfig.cleanUrls, markdown: userConfig.markdown, - alias: resolveAliases(themeDir), + alias: resolveAliases(root, themeDir), vue: userConfig.vue, vite: userConfig.vite, shouldPreload: userConfig.shouldPreload, @@ -145,7 +159,7 @@ export async function resolveConfig( return config } -const supportedConfigExtensions = ['js', 'ts', '.mjs', 'mts'] +const supportedConfigExtensions = ['js', 'ts', 'mjs', 'mts'] async function resolveUserConfig( root: string, diff --git a/src/node/index.ts b/src/node/index.ts index f671e921..bc08b91b 100644 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -4,4 +4,11 @@ export * from './serve/serve' export * from './config' export * from './markdown/markdown' -export type { SiteData, HeadConfig, LocaleConfig } from '../../types/shared' +// shared types +export type { + SiteData, + HeadConfig, + Header, + LocaleConfig, + DefaultTheme +} from '../../types/shared' diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 41b0c4af..40ddea44 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -10,7 +10,7 @@ import { snippetPlugin } from './plugins/snippet' import { hoistPlugin } from './plugins/hoist' import { preWrapperPlugin } from './plugins/preWrapper' import { linkPlugin } from './plugins/link' -import { extractHeaderPlugin } from './plugins/header' +import { headingPlugin } from './plugins/headings' import { Header } from '../shared' import anchor from 'markdown-it-anchor' import attrs from 'markdown-it-attrs' @@ -40,9 +40,10 @@ export interface MarkdownParsedData { headers?: Header[] } -export interface MarkdownRenderer { +export interface MarkdownRenderer extends MarkdownIt { + __path: string + __relativePath: string __data: MarkdownParsedData - render: (src: string, env?: any) => { html: string; data: any } } export type { Header } @@ -65,7 +66,7 @@ export const createMarkdownRenderer = ( .use(snippetPlugin, siteConfig.srcDir) .use(hoistPlugin) .use(containerPlugin) - .use(extractHeaderPlugin) + .use(headingPlugin) .use(linkPlugin, { target: '_blank', rel: 'noopener noreferrer', @@ -95,17 +96,5 @@ export const createMarkdownRenderer = ( md.use(lineNumberPlugin) } - // wrap render so that we can return both the html and extracted data. - const render = md.render - const wrappedRender: MarkdownRenderer['render'] = (src) => { - ;(md as any).__data = {} - const html = render.call(md, src) - return { - html, - data: (md as any).__data - } - } - ;(md as any).render = wrappedRender - - return md as any + return md as MarkdownRenderer } diff --git a/src/node/markdown/plugins/containers.ts b/src/node/markdown/plugins/containers.ts index 1afd55ca..61dd7b91 100644 --- a/src/node/markdown/plugins/containers.ts +++ b/src/node/markdown/plugins/containers.ts @@ -7,6 +7,7 @@ export const containerPlugin = (md: MarkdownIt) => { .use(...createContainer('info', 'INFO')) .use(...createContainer('warning', 'WARNING')) .use(...createContainer('danger', 'WARNING')) + .use(...createContainer('details', 'Details')) // explicitly escape Vue syntax .use(container, 'v-pre', { render: (tokens: Token[], idx: number) => @@ -31,11 +32,14 @@ function createContainer(klass: string, defaultTitle: string): ContainerArgs { const token = tokens[idx] const info = token.info.trim().slice(klass.length).trim() if (token.nesting === 1) { + if (klass === 'details') { + return `
${info ? `${info}` : ''}\n` + } return `

${ info || defaultTitle }

\n` } else { - return `
\n` + return klass === 'details' ? `
\n` : `\n` } } } diff --git a/src/node/markdown/plugins/header.ts b/src/node/markdown/plugins/headings.ts similarity index 78% rename from src/node/markdown/plugins/header.ts rename to src/node/markdown/plugins/headings.ts index df44d2ca..ce186862 100644 --- a/src/node/markdown/plugins/header.ts +++ b/src/node/markdown/plugins/headings.ts @@ -1,16 +1,16 @@ -import MarkdownIt from 'markdown-it' -import { MarkdownParsedData } from '../markdown' +import { MarkdownRenderer } from '../markdown' import { deeplyParseHeader } from '../../utils/parseHeader' import { slugify } from './slugify' +import MarkdownIt from 'markdown-it' -export const extractHeaderPlugin = (md: MarkdownIt, include = ['h2', 'h3']) => { +export const headingPlugin = (md: MarkdownIt, include = ['h2', 'h3']) => { md.renderer.rules.heading_open = (tokens, i, options, env, self) => { const token = tokens[i] if (include.includes(token.tag)) { const title = tokens[i + 1].content const idAttr = token.attrs!.find(([name]) => name === 'id') const slug = idAttr && idAttr[1] - const data = (md as any).__data as MarkdownParsedData + const data = (md as MarkdownRenderer).__data const headers = data.headers || (data.headers = []) headers.push({ level: parseInt(token.tag.slice(1), 10), diff --git a/src/node/markdown/plugins/hoist.ts b/src/node/markdown/plugins/hoist.ts index 2a152d0e..ca309d5f 100644 --- a/src/node/markdown/plugins/hoist.ts +++ b/src/node/markdown/plugins/hoist.ts @@ -1,5 +1,5 @@ import MarkdownIt from 'markdown-it' -import { MarkdownParsedData } from '../markdown' +import { MarkdownRenderer } from '../markdown' // hoist