import * as path from 'path' ; import fs from 'fs-extra' ; import type { Plugin , IndexHtmlTransformContext } from 'vite' ; import type { MdxPlugin } from 'vite-plugin-mdx/dist/types' ; import { DefaultPageStrategy , defaultFileHandler , } from './page-strategy/DefaultPageStrategy' ; import { renderPageList , renderPageListInSSR , renderOnePageData , } from './page-strategy/pageUtils' ; import { PageStrategy } from './page-strategy' ; import { resolveTheme } from './virtule-module-plugins/theme' ; import { DemoModuleManager , DemoMdxPlugin , } from './virtule-module-plugins/demo-modules' ; import { TsInfoModuleManager , TsInfoMdxPlugin , } from './virtule-module-plugins/ts-info-module' ; import { injectHTMLTag } from './utils/injectHTMLTag' ; import { VirtualModulesManager } from './utils/virtual-module' ; import { ImageMdxPlugin } from './utils/mdx-plugin-image' ; import { FileTextMdxPlugin } from './utils/mdx-plugin-file-text' ; /**
 * This is a public API that users use in their index.html.
 * Changing this would introduce breaking change for users.
 */ const appEntryId = '/@pages-infra/main.js' ; /**
 * This is a private prefix an users should not use them
 */ const modulePrefix = '/@react-pages/' ; const pagesModuleId = modulePrefix + 'pages' ; const themeModuleId = modulePrefix + 'theme' ; const ssrDataModuleId = modulePrefix + 'ssrData' ; const tsInfoQueryReg = /\?tsInfo=(.*)$/ ; export default function pluginFactory ( opts = { } ) { const { useHashRouter = false , staticSiteGeneration } = opts ; let isBuild ; let pagesDir ; let pageStrategy ; /** used as data source for PageStrategy and other dynamic-modules */ const virtualModulesManager = new VirtualModulesManager ( ) ; const demoModuleManager = new DemoModuleManager ( ) ; const tsInfoModuleManager = new TsInfoModuleManager ( ) ; return { name : 'vite-plugin-react-pages' , enforce : 'pre' , config : ( ) => ( { optimizeDeps : { include : [ 'react' , 'react-dom' , 'react-router-dom' , '@mdx-js/react' ] , exclude : [ 'vite-plugin-react-pages' ] , } , define : { __HASH_ROUTER__ : ! ! useHashRouter , } , build : { rollupOptions : { output : { manualChunks : undefined , } , } , } , } ) , configResolved ( { root , plugins , logger , command } ) { isBuild = command === 'build' ; pagesDir = opts . pagesDir ?? path . resolve ( root , 'pages' ) ; if ( opts . pageStrategy ) { pageStrategy = opts . pageStrategy ; } else { pageStrategy = new DefaultPageStrategy ( ) ; } const mdxPlugin = plugins . find ( ( plugin ) => plugin . name === 'vite-plugin-mdx' ) as MdxPlugin | undefined ; if ( mdxPlugin ?. mdxOptions ) { mdxPlugin . mdxOptions . remarkPlugins . push ( ... getRemarkPlugins ( root ) ) ; } else { logger . warn ( '[react-pages] Please install vite-plugin-mdx@3.1 or higher' ) ; } } , configureServer ( { watcher , moduleGraph } ) { const reloadVirtualModule = ( moduleId ) => { const module = moduleGraph . getModuleById ( moduleId ) ; if ( module ) { moduleGraph . invalidateModule ( module ) ; watcher . emit ( 'change' , moduleId ) ; } } ; pageStrategy . on ( 'page-list' , ( ) => reloadVirtualModule ( pagesModuleId ) ) . on ( 'page' , ( pageIds ) => { pageIds . forEach ( ( pageId ) => { reloadVirtualModule ( pagesModuleId + pageId ) ; } ) ; } ) ; demoModuleManager . onUpdate ( reloadVirtualModule ) ; tsInfoModuleManager . onUpdate ( reloadVirtualModule ) ; } , buildStart ( ) { pageStrategy . start ( pagesDir , virtualModulesManager ) ; } , async resolveId ( id , importer ) { if ( id === appEntryId ) return id ; if ( id . startsWith ( modulePrefix ) ) return id ; if ( id . endsWith ( '?demo' ) ) { const bareImport = id . slice ( 0 , 0 - '?demo' . length ) ; const resolved = await this . resolve ( bareImport , importer ) ; if ( ! resolved || resolved . external ) throw new Error ( `can not resolve demo: ${ id }. importer: ${ importer }` ) ; return demoModuleManager . registerProxyModule ( resolved . id ) ; } const matchTsInfo = id . match ( tsInfoQueryReg ) ; if ( matchTsInfo ) { const bareImport = id . replace ( tsInfoQueryReg , '' ) ; const resolved = await this . resolve ( bareImport , importer ) ; if ( ! resolved || resolved . external ) throw new Error ( `can not resolve tsInfo: ${ id }. importer: ${ importer }` ) ; const exportName = matchTsInfo [ 1 ] ; return tsInfoModuleManager . registerProxyModule ( resolved . id , exportName ) ; } return undefined ; } , async load ( id ) { if ( id === appEntryId ) return `import "vite-plugin-react-pages/dist/client/main.js";` ; if ( id === pagesModuleId ) { return renderPageList ( await pageStrategy . getPages ( ) , isBuild ) ; } if ( id . startsWith ( pagesModuleId + '/' ) ) { let pageId = id . slice ( pagesModuleId . length ) ; if ( pageId === '/index__' ) pageId = '/' ; const page = await pageStrategy . getPage ( pageId ) ; if ( ! page ) { throw Error ( `Page not found: ${ pageId }` ) ; } return renderOnePageData ( page . data ) ; } if ( id === themeModuleId ) { return `export { default } from "${ await resolveTheme ( pagesDir ) }";` ; } if ( id === ssrDataModuleId ) { return renderPageListInSSR ( await pageStrategy . getPages ( ) ) ; } if ( demoModuleManager . isProxyModuleId ( id ) ) { return demoModuleManager . loadProxyModule ( id ) ; } if ( tsInfoModuleManager . isProxyModuleId ( id ) ) { return tsInfoModuleManager . loadProxyModule ( id ) ; } } , vitePagesStaticSiteGeneration : staticSiteGeneration , closeBundle ( ) { virtualModulesManager . close ( ) ; demoModuleManager . close ( ) ; tsInfoModuleManager . close ( ) ; } , transformIndexHtml ( html , ctx ) { return moveScriptTagToBodyEnd ( html , ctx ) ; } , } ; } export type { Theme , LoadState , PagesLoaded , PagesStaticData , TsInterfaceInfo , TsInterfacePropertyInfo , } from '../../clientTypes' ; export type { FileHandler } from './page-strategy/types.doc' ; export { extractStaticData , File } from './utils/virtual-module' ; export { PageStrategy } ; export { DefaultPageStrategy , defaultFileHandler } ; function getRemarkPlugins ( root ) { const result = [ DemoMdxPlugin , TsInfoMdxPlugin , ImageMdxPlugin , FileTextMdxPlugin , ] ; const pkgJsonPath = path . join ( root , 'package.json' ) ; const hasPkgJson = fs . pathExistsSync ( pkgJsonPath ) ; const { devDependencies = { } , dependencies = { } } = hasPkgJson ? require ( pkgJsonPath ) : { } ; if ( ! devDependencies [ 'remark-frontmatter' ] && ! dependencies [ 'remark-frontmatter' ] ) { result . push ( require ( 'remark-frontmatter' ) ) ; } return result ; } /**
 * vite put script before style, which cause style problem for antd
 * so we move the script tag to the end of the body
 * https://github.com/vitejs/vite/blob/4112c5d103673b83c50d446096086617dfaac5a3/packages/vite/src/node/plugins/html.ts#L352
 */ function moveScriptTagToBodyEnd ( html , ctx ) { if ( ctx . chunk ) { const reg = new RegExp ( `<script\\s[^>]*?${ ctx . chunk . fileName }[^<]*?<\\/script>` ) ; const match = html . match ( reg ) ; if ( match ) { const script = match [ 0 ] ; html = html . replace ( script , '' ) ; return injectHTMLTag ( html , script ) ; } } }