import path from 'path' ; import { fs } from '@modern-js/utils' ; import { IAppContext , mountHook , NormalizedConfig } from '@modern-js/core' ; import type { Entrypoint } from '@modern-js/types' ; import * as templates from './templates' ; import { getClientRoutes } from './getClientRoutes' ; import { FILE_SYSTEM_ROUTES_FILE_NAME , ENTRY_POINT_FILE_NAME , } from './constants' ; import { getDefaultImports } from './utils' ; export interface ImportSpecifier { local ? : string ; imported ? : string ; } export interface ImportStatement { specifiers : ImportSpecifier [ ] ; value : string ; initialize ? : string ; } const createImportSpecifier = ( specifiers ) : string => { let defaults = '' ; const named = [ ] ; for ( const { local , imported } of specifiers ) { if ( local && imported ) { named . push ( `${ imported } as ${ local }` ) ; } else if ( local ) { defaults = local ; } else { named . push ( imported ) ; } } if ( defaults && named . length ) { return `${ defaults }, { ${ named . join ( ', ' ) } }` ; } else if ( defaults ) { return defaults ; } else { return `{ ${ named . join ( ', ' ) } }` ; } } ; const createImportStatements = ( statements ) : string => { const deDuplicated = [ ] ; const seen = new Map ( ) ; for ( const { value , specifiers , initialize } of statements ) { if ( ! seen . has ( value ) ) { deDuplicated . push ( { value , specifiers , initialize , } ) ; seen . set ( value , specifiers ) ; } else { seen . get ( value ) . push ( ... specifiers ) ; const modifyIndex = deDuplicated . findIndex ( v => v . value === value ) ; const originInitialize = deDuplicated [ modifyIndex ] ?. initialize ?? '' ; deDuplicated [ modifyIndex ] . initialize = originInitialize . concat ( `\n${ initialize || '' }` ) ; } } return deDuplicated . map ( ( { value , specifiers , initialize } ) => `import ${ createImportSpecifier ( specifiers ) } from '${ value }';\n${ initialize || '' }` ) . join ( '\n' ) ; } ; export const generateCode = async ( appContext , config , entrypoints ) => { const { internalDirectory , srcDirectory } = appContext ; const { output : { mountId } , } = config ; for ( const entrypoint of entrypoints ) { const { entryName , isAutoMount , customBootstrap , fileSystemRoutes } = entrypoint ; if ( isAutoMount ) { if ( fileSystemRoutes ) { const initialRoutes = getClientRoutes ( { entrypoint , srcDirectory , internalDirectory , } ) ; const { routes } = await ( mountHook ( ) as any ) . modifyFileSystemRoutes ( { entrypoint , routes : initialRoutes , } ) ; fs . outputFileSync ( path . resolve ( internalDirectory , `./${ entryName }/${ FILE_SYSTEM_ROUTES_FILE_NAME }` ) , templates . fileSystemRoutes ( { routes } ) , 'utf8' ) ; } const { imports : importStatements } = await ( mountHook ( ) as any ) . modifyEntryImports ( { entrypoint , imports : getDefaultImports ( { entrypoint , srcDirectory , } ) , } ) ; const { plugins } = await ( mountHook ( ) as any ) . modifyEntryRuntimePlugins ( { entrypoint , plugins : [ ] , } ) ; const { code : renderFunction } = await ( mountHook ( ) as any ) . modifyEntryRenderFunction ( { entrypoint , code : templates . renderFunction ( { plugins , customBootstrap , fileSystemRoutes , } ) , } ) ; const { exportStatement } = await ( mountHook ( ) as any ) . modifyEntryExport ( { entrypoint , exportStatement : 'export default AppWrapper;' , } ) ; const code = templates . index ( { mountId : mountId as string , imports : createImportStatements ( importStatements ) , renderFunction , exportStatement , } ) ; const entryFile = path . resolve ( internalDirectory , `./${ entryName }/${ ENTRY_POINT_FILE_NAME }` ) ; entrypoint . entry = entryFile ; fs . outputFileSync ( entryFile , code , 'utf8' ) ; } } } ;