import * as path from 'path' ; import { FileSystem , IPackageJson , JsonFile , } from '@rushstack/node-core-library' ; import execa from 'execa' ; import { success } from 'signale' ; import yaml from 'js-yaml' ; import { fs , logger , chalk } from '@modern-js/utils' ; import { WORKSPACE_FILE } from '../../constants' ; import { IPnpmWorkSpace } from '../../type' ; import { DagOperator } from '../../dag/operator' ; import { IProjectNode } from '../../projects/get-projects' ; import { PackageManagerType } from '../../parse-config' ; interface IDeployConfig { rootPath : string ; packageManager : PackageManagerType ; deployPath ? : string ; } const createCopyMap = ( rootPath , targetProject , copyProjects , deployDir ) => { const map = new Map < string , string > ( ) ; for ( const project of copyProjects ) { const relativePath = path . relative ( rootPath , project . extra . path ) ; const targetPath = path . join ( deployDir , relativePath ) ; map . set ( project . extra . path , targetPath ) ; } const targetProjectDeployPath = path . join ( deployDir , 'apps/app' ) ; map . set ( targetProject . extra . path , targetProjectDeployPath ) ; return map ; } ; const createCopyFromMonorepoToDeployDirFn = ( monorepoDir , deployDir ) => ( filename ) => { const sourcePath = path . join ( monorepoDir , filename ) ; const destinationPath = path . join ( deployDir , filename ) ; if ( FileSystem . exists ( sourcePath ) ) { FileSystem . copyFile ( { sourcePath , destinationPath , } ) ; } } ; const checkAndUpdatePMWorkspaces = ( deployDir ) => { const pnpmWp = path . join ( deployDir , WORKSPACE_FILE . PNPM ) ; if ( fs . existsSync ( pnpmWp ) ) { const pnpmWorkspace = yaml . load ( fs . readFileSync ( pnpmWp , 'utf-8' ) ) as IPnpmWorkSpace ; if ( pnpmWorkspace . packages && Array . isArray ( pnpmWorkspace . packages ) ) { pnpmWorkspace . packages . push ( 'apps/**' ) ; } fs . writeFileSync ( pnpmWp , yaml . dump ( pnpmWorkspace ) ) ; } const pkgPath = path . join ( deployDir , WORKSPACE_FILE . YARN ) ; const pkg = JsonFile . load ( pkgPath ) ; if ( pkg . workspaces ?. packages && Array . isArray ( pkg . workspaces . packages ) ) { pkg . workspaces . packages . push ( 'app/**' ) ; } } ; const generatorAndCopyRequiredFiles = ( rootPath , deployDir ) => { const copy = createCopyFromMonorepoToDeployDirFn ( rootPath , deployDir ) ; copy ( '.npmrc' ) ; copy ( 'package.json' ) ; copy ( 'pnpm-workspace.yaml' ) ; copy ( '.pnpmfile.cjs' ) ; copy ( 'tsconfig.json' ) ; copy ( 'modern.config.js' ) ; copy ( 'pnpm-lock.yaml' ) ; copy ( 'yarn.lock' ) ; copy ( 'package-lock.json' ) ; checkAndUpdatePMWorkspaces ( deployDir ) ; } ; const checkAndRunDeployCommand = async ( monorepoPath , targetProject , packageManager ) => { const scritps = ( targetProject . extra ?. scripts as IPackageJson [ 'scripts' ] ) || { } ; if ( scritps . deploy ) { logger . info ( `The 'deploy' command for the ${ targetProject . name } is detected, so 'deploy' will be executed` ) ; let runDeployCommands = [ 'run' , 'deploy' ] ; if ( packageManager === 'pnpm' ) { runDeployCommands = [ 'run' , 'deploy' , '--filter' , targetProject . name ] ; } else if ( packageManager === 'yarn' ) { runDeployCommands = [ 'workspace' , targetProject . name , 'run' , 'deploy' ] ; } const cwd = packageManager === 'npm' ? targetProject . extra . path : monorepoPath ; const childProcess = execa ( packageManager , runDeployCommands , { cwd , stdio : [ 'pipe' ] , } ) ; childProcess . stdout ?. pipe ( process . stdout ) ; childProcess . stderr ?. pipe ( process . stderr ) ; await childProcess ; } } ; const installDependency = async ( deployDir , packageManager ) => { const packageJsonPath = path . join ( deployDir , 'package.json' ) ; const packageJson = JsonFile . load ( packageJsonPath ) as IPackageJson ; const scripts = packageJson ?. scripts || { } ; let commands = [ ] ; if ( scripts . setup ) { logger . info ( `The 'setup' command is detected, execute '${ packageManager } run setup' to start installing the dependencies` ) ; commands = [ 'run' , 'setup' ] ; } else { logger . info ( `No 'setup' command detected, execute '${ packageManager } install' to start installing dependencies` ) ; commands = [ 'install' ] ; } logger . log ( chalk . rgb ( 218 , 152 , 92 ) ( 'Install Log:\n' ) ) ; const childProcess = execa ( packageManager , commands , { stdio : 'inherit' , cwd : deployDir , env : { NODE_ENV : undefined } , } ) ; await childProcess ; } ; const excludeDirs = ( filePath , dirs ) => dirs . some ( dir => filePath . includes ( dir ) ) ; const defaultDeployPath = 'output' ; export const deploy = async ( deployProjectNames , operator , config ) => { const { rootPath , packageManager , deployPath = defaultDeployPath } = config ; const realDeployPath = path . isAbsolute ( deployPath ) ? deployPath : path . join ( rootPath , deployPath ) ; FileSystem . deleteFolder ( realDeployPath ) ; for ( const deployProjectName of deployProjectNames ) { const currentProject = operator . getNodeData ( deployProjectName , { checkExist : true , } ) ; const alldeps = operator . getNodeAllDependencyData ( deployProjectName ) ; const copyMap = createCopyMap ( rootPath , currentProject , alldeps , realDeployPath ) ; await checkAndRunDeployCommand ( rootPath , currentProject , packageManager ) ; FileSystem . ensureFolder ( realDeployPath ) ; for ( const [ from , to ] of copyMap ) { FileSystem . copyFiles ( { sourcePath : from , destinationPath : to , dereferenceSymlinks : false , filter ( filePath ) { if ( excludeDirs ( filePath , [ 'node_modules' , 'dist' ] ) ) { return false ; } return true ; } , } ) ; } } generatorAndCopyRequiredFiles ( rootPath , realDeployPath ) ; await installDependency ( realDeployPath , packageManager ) ; success ( `Deploy success. The deploy dir is in '${ rootPath }/output'` ) ; } ;