import path from 'path' ; import { defineConfig , mergeConfig , Plugin , ResolvedConfig } from 'vite' ; import { SiteConfig , resolveSiteData } from './config' ; import { createMarkdownToVueRenderFn , MarkdownCompileResult } from './markdownToVue' ; import { DIST_CLIENT_PATH , APP_PATH , SITE_DATA_REQUEST_PATH } from './alias' ; import { slash } from './utils/slash' ; import { OutputAsset , OutputChunk } from 'rollup' ; import { staticDataPlugin } from './staticDataPlugin' ; const hashRE = /\.(\w+)\.js$/ ; const staticInjectMarkerRE = /\b(const _hoisted_\d+ = \/\*(?:#|@)__PURE__\*\/\s*createStaticVNode)\("(.*)", (\d+)\)/g ; const staticStripRE = /['"`]__VP_STATIC_START__[^]*?__VP_STATIC_END__['"`]/g ; const staticRestoreRE = /__VP_STATIC_(START|END)__/g ; const scriptClientRE = /<script\b[^>]*client\b[^>]*>([^]*?)<\/script>/ ; const isPageChunk = ( chunk ) : chunk is OutputChunk & { facadeModuleId : string ; } => ! ! ( chunk . type === 'chunk' && chunk . isEntry && chunk . facadeModuleId && chunk . facadeModuleId . endsWith ( '.md' ) ) ; export function createVitePressPlugin ( root , siteConfig , ssr = false , pageToHashMap ? , clientJSMap ? ) { const { srcDir , configPath , alias , markdown , site , vue : userVuePluginOptions , vite : userViteConfig , pages } = siteConfig ; let markdownToVue ; const vuePlugin = require ( '@vitejs/plugin-vue' ) ( { include : [ /\.vue$/ , /\.md$/ ] , ... userVuePluginOptions } ) ; const processClientJS = ( code , id ) => { return scriptClientRE . test ( code ) ? code . replace ( scriptClientRE , ( _ , content ) => { if ( ssr && clientJSMap ) clientJSMap [ id ] = content ; return `\n` . repeat ( _ . split ( '\n' ) . length - 1 ) ; } ) : code ; } ; let siteData = site ; let hasDeadLinks = false ; let config ; const vitePressPlugin = { name : 'vitepress' , configResolved ( resolvedConfig ) { config = resolvedConfig ; markdownToVue = createMarkdownToVueRenderFn ( srcDir , markdown , pages , config . define , config . command === 'build' ) ; } , config ( ) { const baseConfig = defineConfig ( { resolve : { alias } , define : { __CARBON__ : ! ! site . themeConfig . carbonAds ?. carbon , __BSA__ : ! ! site . themeConfig . carbonAds ?. custom , __ALGOLIA__ : ! ! site . themeConfig . algolia } , optimizeDeps : { include : [ 'vue' ] , exclude : [ '@docsearch/js' ] } , server : { fs : { allow : [ DIST_CLIENT_PATH , srcDir , process . cwd ( ) ] } } } ) ; return userViteConfig ? mergeConfig ( userViteConfig , baseConfig ) : baseConfig ; } , resolveId ( id ) { if ( id === SITE_DATA_REQUEST_PATH ) { return SITE_DATA_REQUEST_PATH ; } } , load ( id ) { if ( id === SITE_DATA_REQUEST_PATH ) { let data = siteData ; if ( config . command === 'build' ) { data = { ... siteData , head : [ ] } ; } return `export default ${ JSON . stringify ( JSON . stringify ( data ) ) }` ; } } , transform ( code , id ) { if ( id . endsWith ( '.vue' ) ) { return processClientJS ( code , id ) ; } else if ( id . endsWith ( '.md' ) ) { const { vueSrc , deadLinks , includes } = markdownToVue ( code , id , config . publicDir ) ; if ( deadLinks . length ) { hasDeadLinks = true ; } if ( includes . length ) { includes . forEach ( ( i ) => { this . addWatchFile ( i ) ; } ) ; } return processClientJS ( vueSrc , id ) ; } } , renderStart ( ) { if ( hasDeadLinks ) { throw new Error ( `One or more pages contain dead links.` ) ; } } , configureServer ( server ) { if ( configPath ) { server . watcher . add ( configPath ) ; } return ( ) => { server . middlewares . use ( ( req , res , next ) => { if ( req . url ! . endsWith ( '.html' ) ) { res . statusCode = 200 ; res . end ( `
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="description" content="">
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/@fs/${ APP_PATH }/index.js"></script>
  </body>
</html>` ) ; return ; } next ( ) ; } ) ; } ; } , renderChunk ( code , chunk ) { if ( ! ssr && isPageChunk ( chunk as OutputChunk ) ) { code = code . replace ( staticInjectMarkerRE , '$1("__VP_STATIC_START__$2__VP_STATIC_END__", $3)' ) ; return code ; } return null ; } , generateBundle ( _options , bundle ) { if ( ssr ) { for ( const name in bundle ) { if ( bundle [ name ] . type === 'asset' ) { delete bundle [ name ] ; } } } else { for ( const name in bundle ) { const chunk = bundle [ name ] ; if ( isPageChunk ( chunk ) ) { const hash = chunk . fileName . match ( hashRE ) ! [ 1 ] ; pageToHashMap ! [ chunk . name . toLowerCase ( ) ] = hash ; bundle [ name + '-lean' ] = { ... chunk , fileName : chunk . fileName . replace ( /\.js$/ , '.lean.js' ) , code : chunk . code . replace ( staticStripRE , `""` ) } ; chunk . code = chunk . code . replace ( staticRestoreRE , '' ) ; } } } } , async handleHotUpdate ( ctx ) { const { file , read , server } = ctx ; if ( file === configPath ) { const newData = await resolveSiteData ( root ) ; if ( newData . base !== siteData . base ) { console . warn ( `[vitepress]: config.base has changed. Please restart the dev server.` ) ; } siteData = newData ; return [ server . moduleGraph . getModuleById ( SITE_DATA_REQUEST_PATH ) ! ] ; } if ( file . endsWith ( '.md' ) ) { const content = await read ( ) ; const { pageData , vueSrc } = markdownToVue ( content , file , config . publicDir ) ; server . ws . send ( { type : 'custom' , event : 'vitepress:pageData' , data : { path : `/${ slash ( path . relative ( srcDir , file ) ) }` , pageData } } ) ; ctx . read = ( ) => vueSrc ; } } } ; return [ vitePressPlugin , vuePlugin , ... ( userViteConfig ?. plugins || [ ] ) , staticDataPlugin ] ; }