import { systemPackages , TCromwellaConfig , TFrontendDependency , TPackageJson } from '@cromwell/core' ; import { getNodeModuleDir , readCmsModules } from '@cromwell/core-backend' ; import { each as asyncEach } from 'async' ; import colorsdef from 'colors/safe' ; import fs from 'fs-extra' ; import glob from 'glob' ; import importFrom from 'import-from' ; import path , { resolve } from 'path' ; import { TDependency , TGetDeps , THoistedDeps , TLocalSymlink , TModuleInfo , TNonHoisted , TPackage } from './types' ; const colors = colorsdef ; export const getNodeModuleVersion = ( moduleName , importFromPath ? ) : string | undefined => { const pckgImportName = `${ moduleName }/package.json` ; let modulePackageJson ; try { modulePackageJson = importFromPath ? importFrom ( importFromPath , pckgImportName ) as any : require ( pckgImportName ) ; } catch ( e ) { console . log ( `Cromwell bundler: Failed to require package ${ pckgImportName } ${ importFromPath ? 'from ' + importFromPath : '' }. You may need to reconfigure your frontendDependencies` ) ; } return modulePackageJson ?. version ; } ; export const getNodeModuleNameWithVersion = ( moduleName ) : string | undefined => { const ver = getNodeModuleVersion ( moduleName ) ; if ( ver ) return `${ moduleName }@${ ver }` ; } ; export const getDepVersion = ( pckg , depName ) : string | undefined => { let ver = pckg ?. dependencies ?. [ depName ] ?? pckg ?. devDependencies ?. [ depName ] ?? pckg ?. peerDependencies ?. [ depName ] ; if ( ver ) return ver ; const frontDeps = pckg ?. cromwell ?. frontendDependencies ; if ( frontDeps ) { frontDeps . forEach ( dep => { if ( typeof dep === 'object' && dep . version ) ver = dep . version ; } ) ; } return ver ; } ; export const getDepNameWithVersion = ( pckg , depName ) : string | undefined => { const ver = getDepVersion ( pckg , depName ) ; if ( ver ) return `${ depName }@${ ver }` ; } ; const modulesExportKeys = { } ; export const getModuleInfo = ( moduleName , moduleVer ? , from ? ) : TModuleInfo => { let exportKeys ; let exactVersion ; if ( ! modulesExportKeys [ moduleName ] ) { try { const imported = from ? importFrom ( from , moduleName ) : require ( moduleName ) ; const keys = Object . keys ( imported ) ; if ( ! keys . includes ( 'default' ) ) keys . unshift ( 'default' ) ; if ( keys ) exportKeys = keys ; if ( ! exportKeys ) throw new Error ( '!exportKeys' ) ; } catch ( e ) { try { const imported = require ( moduleName ) ; const keys = Object . keys ( imported ) ; if ( keys && ! keys . includes ( 'default' ) ) keys . unshift ( 'default' ) ; if ( keys ) exportKeys = keys ; } catch ( e ) { console . log ( colors . brightYellow ( `Cromwell: Failed to require() module: ${ moduleName }` ) ) ; } } try { const modulePackageJson = from ? importFrom ( from , `${ moduleName }/package.json` ) as any : require ( `${ moduleName }/package.json` ) ; exactVersion = modulePackageJson ?. version ; if ( ! exactVersion ) throw new Error ( '!exactVersion' ) ; } catch ( e ) { console . log ( colors . brightYellow ( `Cromwell: Failed to require() package.json of module: ${ moduleName }` ) ) ; } const info = { exportKeys , exactVersion } ; modulesExportKeys [ moduleName ] = info ; return info ; } else { return modulesExportKeys [ moduleName ] ; } } ; param packageDeps param packagePath param store const collectDeps = ( packageDeps , packagePath , store ) : TDependency [ ] => { for ( const [ moduleName , moduleVer ] of Object . entries ( packageDeps ) ) { let index = null ; store . forEach ( ( dep , i ) => { if ( dep . name === moduleName ) { index = i ; } } ) ; if ( index != null ) { store [ index ] . versions [ moduleVer ] = ( store [ index ] . versions [ moduleVer ] || 0 ) + 1 ; store [ index ] . packages [ packagePath ] = moduleVer ; } else { store . push ( { name : moduleName , versions : { [ moduleVer ] : 1 } , packages : { [ packagePath ] : moduleVer } } ) ; } } return store ; } ; param store param packages const hoistDeps = ( store , packages , isProduction , forceInstall ) : THoistedDeps => { const hoisted = { } ; const nonHoisted = { } ; const localSymlinks = [ ] ; for ( const module of store ) { const versions = Object . entries ( module . versions ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) . map ( entry => entry [ 0 ] ) ; const hoistedVersion = versions [ 0 ] ; let localPckIndex = null ; packages . forEach ( ( pkg , i ) => { if ( pkg . name == module . name ) localPckIndex = i ; } ) ; if ( localPckIndex != null ) { Object . keys ( module . packages ) . forEach ( dependentPckg => { if ( localPckIndex == null ) return ; const includeInPath = resolve ( path . dirname ( dependentPckg ) , 'node_modules' , module . name ) ; const includeToPath = path . dirname ( packages [ localPckIndex ] . path || '' ) ; localSymlinks . push ( { linkPath : includeInPath , referredDir : includeToPath } ) ; } ) ; } else { hoisted [ module . name ] = hoistedVersion ; } if ( versions . length > 1 && localPckIndex == null ) { for ( let i = 1 ; i < versions . length ; i ++ ) { const ver = versions [ i ] ; Object . entries ( module . packages ) . forEach ( ( [ packagePath , packageVer ] ) => { if ( packageVer === ver ) { const packageName = packages ?. find ( p => p . path === packagePath ) ?. name ; console . log ( colors . brightYellow ( `\nCromwell:: Local package ${ packageName } at ${ packagePath } dependent on different version of hoisted package ${ module . name }. \nHoisted (commonly used) ${ module . name } is "${ hoistedVersion }", but dependent is "${ ver }".\n` ) ) ; if ( ! forceInstall ) { console . log ( colors . brightRed ( `Cromwell:: Error. Abort operation. Please fix "${ module . name }": "${ ver }" or run command in force mode (add -f flag).\n` ) ) ; process . exit ( ) ; } if ( ! nonHoisted [ path . dirname ( packagePath ) ] ) { nonHoisted [ path . dirname ( packagePath ) ] = { name : packageName || '' , modules : { [ module . name ] : ver } } ; } else { nonHoisted [ path . dirname ( packagePath ) ] . modules [ module . name ] = ver ; } } } ) ; } } } return { hoisted , nonHoisted , localSymlinks } ; } ; export const getCromwellaConfigSync = ( projectRootDir , canLog ? ) : TCromwellaConfig | undefined => { const cromwellaConfigPath = resolve ( projectRootDir , 'cromwella.json' ) ; let cromwellaConfig = undefined ; try { cromwellaConfig = JSON . parse ( fs . readFileSync ( cromwellaConfigPath ) . toString ( ) ) ; } catch ( e ) { if ( canLog ) console . log ( e ) ; } if ( ! cromwellaConfig || ! cromwellaConfig . packages ) { if ( canLog ) console . log ( colors . brightRed ( `\nCromwella:: Error. Failed to read config in ${ cromwellaConfigPath }\n` ) ) ; } return cromwellaConfig ; } ; export const globPackages = async ( projectRootDir ) : Promise < string [ ] > => { const globOptions = { } ; const cromwellaConfig = getCromwellaConfigSync ( projectRootDir ) ; const packageGlobs = cromwellaConfig ?. packages ?? [ '' ] ; const packagePaths = [ ] ; await new Promise ( done => { asyncEach ( packageGlobs , function ( pkgPath , callback ) { const globPath = resolve ( projectRootDir , pkgPath , 'package.json' ) ; glob ( globPath , globOptions , function ( er , files ) { files . forEach ( f => packagePaths . push ( resolve ( projectRootDir , f ) ) ) ; callback ( ) ; } ) ; } , ( ) => done ( true ) ) ; } ) ; const cmsModules = await readCmsModules ( ) ; const addDir = async ( mod ) => { const dir = await getNodeModuleDir ( mod ) ; if ( dir ) { packagePaths . push ( resolve ( dir , 'package.json' ) ) ; } } ; for ( const p of cmsModules . plugins ) { await addDir ( p ) ; } for ( const p of cmsModules . themes ) { await addDir ( p ) ; } for ( const p of systemPackages ) { await addDir ( p ) ; } return packagePaths ; } ; export const collectPackagesInfo = ( packagePaths ) : TPackage [ ] => { packagePaths = Array . from ( new Set ( packagePaths ) ) ; if ( packagePaths . length === 0 ) { console . log ( colors . brightYellow ( `\nCromwell:: No local packages found\n` ) ) ; return [ ] ; } const packages = [ ] ; for ( const pkgPath of packagePaths ) { try { const pkgJson = JSON . parse ( fs . readFileSync ( pkgPath ) . toString ( ) ) ; const pckg = { name : pkgJson . name , path : pkgPath , dependencies : pkgJson . dependencies , devDependencies : pkgJson . devDependencies , peerDependencies : pkgJson . peerDependencies , cromwell : { frontendDependencies : pkgJson ?. cromwell ?. frontendDependencies } } ; packages . push ( pckg ) ; } catch ( e ) { console . log ( e ) ; } } return packages ; } ; export const hoistDependencies = ( packages , isProduction , forceInstall ) : TGetDeps => { const dependencies = [ ] ; const devDependencies = [ ] ; for ( const pkg of packages ) { if ( pkg . dependencies && pkg . path ) { collectDeps ( pkg . dependencies , pkg . path , dependencies ) ; } } for ( const pkg of packages ) { if ( pkg . devDependencies && pkg . path ) { collectDeps ( pkg . devDependencies , pkg . path , devDependencies ) ; } } const hoistedDependencies = JSON . parse ( JSON . stringify ( hoistDeps ( dependencies , packages , isProduction , forceInstall ) ) ) ; const hoistedDevDependencies = JSON . parse ( JSON . stringify ( hoistDeps ( devDependencies , packages , isProduction , forceInstall ) ) ) ; return { packages , hoistedDependencies , hoistedDevDependencies } ; } ; export const parseFrontendDeps = async ( dependencies ) : Promise < TFrontendDependency [ ] > => { const packagePaths = await globPackages ( process . cwd ( ) ) ; const packages = collectPackagesInfo ( packagePaths ) ; let allDeps = { } ; packages . forEach ( p => { allDeps = { ... allDeps , ... ( p . dependencies ?? { } ) , ... ( p . devDependencies ?? { } ) , ... ( p . peerDependencies ?? { } ) , } ; } ) ; return dependencies . map ( dep => { if ( typeof dep === 'object' ) { if ( ! dep . version ) dep . version = allDeps [ dep . name ] ; return dep ; } return { name : dep , version : allDeps [ dep ] , } ; } ) ; } ; export const collectFrontendDependencies = async ( packages , forceInstall ? ) : Promise < TFrontendDependency [ ] > => { const frontendDependencies = await parseFrontendDeps ( defaultFrontendDeps ) ; packages . forEach ( pckg => { const pckheDeps = pckg ?. cromwell ?. frontendDependencies ; if ( pckheDeps && Array . isArray ( pckheDeps ) ) { pckheDeps . forEach ( dep => { const depName = typeof dep === 'object' ? dep . name : dep ; const depVersion = getDepVersion ( pckg , depName ) ; if ( ! depVersion ) return ; const frontendDep = typeof dep === 'object' ? dep : { name : dep , version : depVersion } ; if ( ! frontendDep . version ) frontendDep . version = depVersion ; let index = undefined ; frontendDependencies . every ( ( mainDep , i ) => { if ( mainDep . name === frontendDep . name ) { if ( mainDep . version !== frontendDep . version ) { console . log ( colors . brightYellow ( `\nCromwell:: Local package ${ pckg . name } at ${ pckg . path } dependent on different version of used frontend module ${ frontendDep . name }. \nAlready used ${ frontendDep . name } is "${ mainDep . version }", but dependent is "${ frontendDep . version }".\n` ) ) ; if ( ! forceInstall ) { console . log ( colors . brightRed ( `Cromwell:: Error. Please fix "${ frontendDep . name }": "${ frontendDep . version }"\n` ) ) ; } } else { index = i ; } return false ; } return true ; } ) ; if ( index !== undefined ) { if ( typeof dep === 'object' ) { frontendDependencies [ index ] = frontendDep ; } } else { frontendDependencies . push ( frontendDep ) ; } } ) ; } } ) ; return frontendDependencies ; } ; export const interopDefaultContent = `
const interopDefault = (lib, importName) => {
    if (lib && typeof lib === 'object' && 'default' in lib) {

        if (importName !== 'default') {
            return lib.default;
        }

        if (typeof lib.default === 'object' || typeof lib.default === 'function') {
            if (Object.keys(lib).length === 1) {
                return lib.default;
            } else if ('default' in lib.default && Object.keys(lib).length === Object.keys(lib.default).length) {
                return lib.default;
            } else if (Object.keys(lib).length === Object.keys(lib.default).length + 1) {
                return lib.default;
            }
        } 
    }
    return lib;
}
` ; export const jsOperators = [ 'let' , 'var' , 'const' , 'function' , 'class' , 'new' , 'delete' , 'import' , 'export' , 'default' , 'typeof' , 'in' , 'of' , 'instanceof' , 'void' , 'return' , 'try' , 'catch' , 'throw' , 'if' , 'else' , 'switch' , 'case' , 'continue' , 'do' , 'while' ] ; export const cromwellStoreModulesPath = 'CromwellStore.nodeModules.modules' ; export const cromwellStoreStatusesPath = 'CromwellStore.nodeModules.importStatuses' ; export const cromwellStoreImportsPath = 'CromwellStore.nodeModules.imports' ; export const getGlobalModuleStr = ( moduleName ) => `${ cromwellStoreModulesPath }['${ moduleName }']` ; export const getGlobalModuleStatusStr = ( moduleName ) => `${ cromwellStoreStatusesPath }['${ moduleName }']` ; export const tempPckgName = '@cromwell/temp-bundler' ; export const defaultFrontendDeps = [ "@apollo/client" , "@cromwell/core" , "@cromwell/admin-panel" , { "name" : "@cromwell/core-frontend" , "externals" : [ { "usedName" : "next/head" } , { "usedName" : "next/router" } , { "usedName" : "next/link" } , { "usedName" : "next/dynamic" } , { "usedName" : "next/document" } ] } , "@loadable/component" , "clsx" , "throttle-debounce" , "query-string" , "react" , "react-dom" , "react-is" , "react-number-format" , "react-router-dom" , "react-toastify" , "tslib" , "react-resize-detector" , { name : "pure-react-carousel" , bundledCss : [ 'pure-react-carousel/dist/react-carousel.es.css' , ] } , { name : "react-image-lightbox" , bundledCss : [ 'react-image-lightbox/style.css' , ] } , ] ;