import { AngularPackage } from './angular-package' ; import { loggerSymbols } from './logger/logger-symbols' ; import { flattenArray } from './utilities/flatten-array' ; /**
 * Angular Package Orchestrator
 */ export class AngularPackageOrchestrator { param angularPackages returns public static orchestrateAngularPackagesBuild ( angularPackages ) { const angularPackageBuilds = this . findBuildOrderForAngularPackagesWithSubPackages ( angularPackages ) ; const angularPackageAndSubPackageBuilds = angularPackageBuilds . map ( ( angularPackagesOrdered ) : Array < Array < Array < AngularPackage > > > => { return angularPackagesOrdered . map ( ( angularPackageOrdered ) : Array < Array < AngularPackage > > => { return this . findBuildOrderForAngularPackages ( angularPackageOrdered ) ; } ) ; } ) ; return angularPackageAndSubPackageBuilds ; } param angularPackages returns private static findBuildOrderForAngularPackagesWithSubPackages ( angularPackages ) { const angularPackageNames = angularPackages . map ( ( angularPackages ) : string => { return angularPackages [ 0 ] . packageName ; } ) ; let angularPackagesNotYetInBuilds = angularPackages ; const angularPackageNamesAlreadyInBuilds = [ ] ; const builds = [ ] ; while ( angularPackagesNotYetInBuilds . length !== 0 ) { const angularPackagesForNextBuild = [ ] ; angularPackagesNotYetInBuilds = angularPackagesNotYetInBuilds . filter ( ( angularPackage ) : boolean => { const angularPackageDependenciesNotYetInBuilds = Object . keys ( angularPackage [ 0 ] . dependencies ) . filter ( ( dependency ) : boolean => { return angularPackageNames . indexOf ( dependency ) !== - 1 ; } ) . filter ( ( dependency ) : boolean => { return angularPackageNamesAlreadyInBuilds . indexOf ( dependency ) === - 1 ; } ) ; if ( angularPackageDependenciesNotYetInBuilds . length === 0 ) { angularPackagesForNextBuild . push ( angularPackage ) ; return false ; } else { return true ; } } ) ; if ( angularPackagesForNextBuild . length === 0 ) { this . handleAngularPackagesWithSubPackagesCircularDependencyError ( angularPackagesNotYetInBuilds ) ; } builds . push ( angularPackagesForNextBuild ) ; const angularPackageNamesForNextBuild = angularPackagesForNextBuild . map ( ( angularPackageReadyForBuild ) : string => { return angularPackageReadyForBuild [ 0 ] . packageName ; } ) ; angularPackageNamesAlreadyInBuilds . push ( ... angularPackageNamesForNextBuild ) ; } return builds ; } param angularPackages returns private static findBuildOrderForAngularPackages ( angularPackages ) { const angularPackageNames = angularPackages . map ( ( angularPackages ) : string => { return angularPackages . packageName ; } ) ; let angularPackagesNotYetInBuilds = angularPackages ; const externalImportSourcesAlreadyInBuilds = [ ] ; const builds = [ ] ; while ( angularPackagesNotYetInBuilds . length !== 0 ) { const angularPackagesForNextBuild = [ ] ; angularPackagesNotYetInBuilds = angularPackagesNotYetInBuilds . filter ( ( angularPackage ) : boolean => { const externalImportSourcesNotYetInBuilds = angularPackage . externalImportSources . filter ( ( externalImportSource ) : boolean => { return angularPackageNames . indexOf ( externalImportSource ) !== - 1 ; } ) . filter ( ( externalImportSource ) : boolean => { return externalImportSourcesAlreadyInBuilds . indexOf ( externalImportSource ) === - 1 ; } ) ; if ( externalImportSourcesNotYetInBuilds . length === 0 ) { angularPackagesForNextBuild . push ( angularPackage ) ; return false ; } else { return true ; } } ) ; if ( angularPackagesForNextBuild . length === 0 ) { this . handleAngularPackageCircularDependencyError ( angularPackagesNotYetInBuilds ) ; } builds . push ( angularPackagesForNextBuild ) ; const packageNamesReadyForBuild = angularPackagesForNextBuild . map ( ( angularPackageReadyForBuild ) : string => { return angularPackageReadyForBuild . packageName ; } ) ; externalImportSourcesAlreadyInBuilds . push ( ... packageNamesReadyForBuild ) ; } return builds ; } param angularPackages private static handleAngularPackagesWithSubPackagesCircularDependencyError ( angularPackages ) { const angularPackagesWithCircularDependencies = angularPackages . map ( ( angularPackage ) : AngularPackage => { return angularPackage [ 0 ] ; } ) ; const angularPackageNames = angularPackagesWithCircularDependencies . map ( ( angularPackage ) : string => { return angularPackage . packageName ; } ) ; const errorDetails = flattenArray ( angularPackagesWithCircularDependencies . map ( ( angularPackage ) : Array < string > => { return [ `Package "${ angularPackage . packageName }"` , ... Object . keys ( angularPackage . dependencies ) . filter ( ( dependency ) : boolean => { return angularPackageNames . indexOf ( dependency ) !== - 1 ; } ) . map ( ( dependency ) : string => { return `  ${ loggerSymbols . arrow } depends on "${ dependency }"` ; } ) ] ; } ) ) ; const errorMessage = [ 'An error occured while starting the build.' , '' , 'Message:    Circular dependencies between differetn packages detected.' , '' , 'Caused by:  Angular Package Builder' , ... errorDetails . map ( ( errorLine , index ) : string => { return index === 0 ? `Details:    ${ errorLine }` : `            ${ errorLine }` ; } ) , '' , 'Tip: Verify that all dependencies are defined correctly, and remove any circular dependencies between them.' , '' , '' ] . join ( '\n' ) ; throw new Error ( errorMessage ) ; } param angularPackages private static handleAngularPackageCircularDependencyError ( angularPackages ) { const angularPackageNames = angularPackages . map ( ( angularPackage ) : string => { return angularPackage . packageName ; } ) ; const errorDetails = flattenArray ( angularPackages . map ( ( angularPackage ) : Array < string > => { return [ `Package "${ angularPackage . packageName }"` , ... angularPackage . externalImportSources . filter ( ( dependency ) : boolean => { return angularPackageNames . indexOf ( dependency ) !== - 1 ; } ) . map ( ( dependency ) : string => { return `  ${ loggerSymbols . arrow } depends on "${ dependency }"` ; } ) ] ; } ) ) ; const errorMessage = [ 'An error occured while starting the build.' , '' , 'Message:    Circular dependencies within the same package detected.' , '' , 'Caused by:  Angular Package Builder' , ... errorDetails . map ( ( errorLine , index ) : string => { return index === 0 ? `Details:    ${ errorLine }` : `            ${ errorLine }` ; } ) , '' , 'Tip: Remove any circular dependencies between the primary / all secondary entry packages.' , '' , '' ] . join ( '\n' ) ; throw new Error ( errorMessage ) ; } }