import path from 'path' ; import fs from 'fs-extra' ; import { createPlugin , useAppContext , useResolvedConfigContext , } from '@modern-js/core' ; import { compiler } from '@modern-js/babel-compiler' ; import { PLUGIN_SCHEMAS , normalizeOutputPath , API_DIR } from '@modern-js/utils' ; import { resolveBabelConfig } from '@modern-js/server-utils' ; import type { Configuration } from 'webpack' ; import type Chain from 'webpack-chain' ; import type { ServerRoute } from '@modern-js/types' ; declare module '@modern-js/core' { interface UserConfig { bff : { prefix ? : string ; requestCreator ? : string ; fetcher ? : string ; proxy : Record < string , any > ; } ; } } const DEFAULT_API_PREFIX = '/api' ; const TS_CONFIG_FILENAME = 'tsconfig.json' ; const FILE_EXTENSIONS = [ '.js' , '.ts' , '.mjs' , '.ejs' ] ; export default createPlugin ( ( ) => ( { validateSchema ( ) { return PLUGIN_SCHEMAS [ '@modern-js/plugin-bff' ] ; } , config ( ) { return { tools : { webpack : ( _config , { chain } ) => { const { appDirectory , port } = useAppContext ( ) ; const modernConfig = useResolvedConfigContext ( ) ; const { bff } = modernConfig || { } ; const { fetcher } = bff || { } ; const prefix = bff ?. prefix || DEFAULT_API_PREFIX ; const rootDir = path . resolve ( appDirectory , API_DIR ) ; chain . resolve . alias . set ( '@api' , rootDir ) ; const apiRegexp = new RegExp ( normalizeOutputPath ( `${ appDirectory }${ path . sep }api${ path . sep }.*(.[tj]s)$` ) ) ; chain . module . rule ( 'loaders' ) . oneOf ( 'bff-client' ) . before ( 'fallback' ) . test ( apiRegexp ) . use ( 'custom-loader' ) . loader ( require . resolve ( './loader' ) . replace ( /\\/g , '/' ) ) . options ( { prefix , apiDir : rootDir , port , fetcher , target : _config . name , requestCreator : bff ?. requestCreator , } ) ; } , } , source : { moduleScopes : [ `./${ API_DIR }` , /create-request/ ] , } , } ; } , modifyServerRoutes ( { routes } ) { const modernConfig = useResolvedConfigContext ( ) ; const { bff } = modernConfig || { } ; const prefix = bff ?. prefix || '/api' ; const prefixList = [ ] ; if ( Array . isArray ( prefix ) ) { prefixList . push ( ... prefix ) ; } else { prefixList . push ( prefix ) ; } const apiServerRoutes = prefixList . map ( pre => ( { urlPath : pre , isApi : true , entryPath : '' , isSPA : false , isSSR : false , } ) ) as ServerRoute [ ] ; return { routes : routes . concat ( apiServerRoutes ) } ; } , async afterBuild ( ) { const { appDirectory , distDirectory } = useAppContext ( ) ; const modernConfig = useResolvedConfigContext ( ) ; const rootDir = path . resolve ( appDirectory , API_DIR ) ; const distDir = path . resolve ( distDirectory , API_DIR ) ; const sourceAbsDir = path . resolve ( appDirectory , API_DIR ) ; const tsconfigPath = path . resolve ( appDirectory , TS_CONFIG_FILENAME ) ; const babelConfig = resolveBabelConfig ( appDirectory , modernConfig , { tsconfigPath , syntax : 'es6+' , type : 'commonjs' , } ) ; const result = await compiler ( { rootDir , distDir , sourceDir : sourceAbsDir , extensions : FILE_EXTENSIONS , ignore : [ `**/__tests__/**` , '**/typings/**' , '*.d.ts' ] , } , babelConfig ) ; if ( await fs . pathExists ( rootDir ) ) { await fs . copy ( rootDir , distDir , { filter : src => ! [ '.ts' , '.js' ] . includes ( path . extname ( src ) ) && src !== tsconfigPath , } ) ; } if ( result . code === 1 ) { throw new Error ( result . message ) ; } } , } ) , { name : '@modern-js/plugin-bff' } ) as any ;