import { join , dirname } from 'path' ; import { uglifyJsFile } from './minify-sources' ; import { buildConfig } from './build-config' ; import { BuildPackage } from './build-package' ; import { rollupRemoveLicensesPlugin } from './rollup-remove-licenses' ; import { rollupGlobals , dashCaseToCamelCase } from './rollup-globals' ; import { remapSourcemap } from './sourcemap-remap' ; const rollup = require ( 'rollup' ) ; const rollupNodeResolutionPlugin = require ( 'rollup-plugin-node-resolve' ) ; const rollupAlias = require ( 'rollup-plugin-alias' ) ; /** Directory where all bundles will be created in. */ const bundlesDir = join ( buildConfig . outputDir , 'bundles' ) ; /** Utility for creating bundles from raw ngc output. */ export class PackageBundler { constructor ( private buildPackage ) { } /** Creates all bundles for the package and all associated entry points (UMD, ES5, ES2015). */ async createBundles ( ) { for ( const entryPoint of this . buildPackage . secondaryEntryPoints ) { await this . bundleSecondaryEntryPoint ( entryPoint ) ; } await this . bundlePrimaryEntryPoint ( ) ; } /** Bundles the primary entry-point w/ given entry file, e.g. @angular/cdk */ private async bundlePrimaryEntryPoint ( ) { const packageName = this . buildPackage . name ; return this . bundleEntryPoint ( { entryFile : this . buildPackage . entryFilePath , esm5EntryFile : join ( this . buildPackage . esm5OutputDir , 'index.js' ) , importName : `@angular/${ this . buildPackage . name }` , moduleName : `ng.${ this . buildPackage . name }` , esm2015Dest : join ( bundlesDir , `${ packageName }.js` ) , esm5Dest : join ( bundlesDir , `${ packageName }.es5.js` ) , umdDest : join ( bundlesDir , `${ packageName }.umd.js` ) , umdMinDest : join ( bundlesDir , `${ packageName }.umd.min.js` ) , } ) ; } /** Bundles a single secondary entry-point w/ given entry file, e.g. @angular/cdk/a11y */ private async bundleSecondaryEntryPoint ( entryPoint ) { const packageName = this . buildPackage . name ; const entryFile = join ( this . buildPackage . outputDir , entryPoint , 'index.js' ) ; const esm5EntryFile = join ( this . buildPackage . esm5OutputDir , entryPoint , 'index.js' ) ; const dashedEntryName = dashCaseToCamelCase ( entryPoint ) ; return this . bundleEntryPoint ( { entryFile , esm5EntryFile , importName : `@angular/${ this . buildPackage . name }/${ dashedEntryName }` , moduleName : `ng.${ packageName }.${ dashedEntryName }` , esm2015Dest : join ( bundlesDir , `${ packageName }` , `${ entryPoint }.js` ) , esm5Dest : join ( bundlesDir , `${ packageName }` , `${ entryPoint }.es5.js` ) , umdDest : join ( bundlesDir , `${ packageName }-${ entryPoint }.umd.js` ) , umdMinDest : join ( bundlesDir , `${ packageName }-${ entryPoint }.umd.min.js` ) , } ) ; } param config private async bundleEntryPoint ( config ) { await this . createRollupBundle ( { importName : config . importName , moduleName : config . moduleName , entry : config . entryFile , dest : config . esm2015Dest , format : 'es' , } ) ; await this . createRollupBundle ( { importName : config . importName , moduleName : config . moduleName , entry : config . esm5EntryFile , dest : config . esm5Dest , format : 'es' , } ) ; await this . createRollupBundle ( { importName : config . importName , moduleName : config . moduleName , entry : config . esm5Dest , dest : config . umdDest , format : 'umd' } ) ; uglifyJsFile ( config . umdDest , config . umdMinDest ) ; await remapSourcemap ( config . esm2015Dest ) ; await remapSourcemap ( config . esm5Dest ) ; await remapSourcemap ( config . umdDest ) ; await remapSourcemap ( config . umdMinDest ) ; } /** Creates a rollup bundle of a specified JavaScript file. */ private async createRollupBundle ( config ) { const bundleOptions = { context : 'this' , external : Object . keys ( rollupGlobals ) , input : config . entry , onwarn : ( warning ) => { if ( /but never used/ . test ( warning . message ) ) { return false ; } if ( warning . code === 'CIRCULAR_DEPENDENCY' ) { throw Error ( warning . message ) ; } console . warn ( warning . message ) ; } , plugins : [ rollupRemoveLicensesPlugin , ] } ; const writeOptions = { name : config . moduleName || 'ng.material' , amd : { id : config . importName } , banner : buildConfig . licenseBanner , format : config . format , file : config . dest , globals : rollupGlobals , sourcemap : true } ; if ( config . format === 'umd' ) { bundleOptions . plugins . push ( rollupNodeResolutionPlugin ( ) ) ; let external = Object . keys ( rollupGlobals ) ; external . splice ( external . indexOf ( 'tslib' ) , 1 ) ; if ( this . buildPackage . exportsSecondaryEntryPointsAtRoot && config . moduleName === `ng.${ this . buildPackage . name }` ) { const importRegex = new RegExp ( `@angular/${ this . buildPackage . name }/.+` ) ; external = external . filter ( e => ! importRegex . test ( e ) ) ; bundleOptions . plugins . push ( rollupAlias ( this . getResolvedSecondaryEntryPointImportPaths ( config . dest ) ) ) ; } bundleOptions . external = external ; } return rollup . rollup ( bundleOptions ) . then ( ( bundle ) => bundle . write ( writeOptions ) ) ; } param bundleOutputDir returns private getResolvedSecondaryEntryPointImportPaths ( bundleOutputDir ) { return this . buildPackage . secondaryEntryPoints . reduce ( ( map , p ) => { map [ `@angular/${ this . buildPackage . name }/${ p }` ] = join ( dirname ( bundleOutputDir ) , this . buildPackage . name , `${ p }.es5.js` ) ; return map ; } , { } as { [ key ] : string ; } ) ; } } /** Configuration for creating library bundles. */ interface BundlesConfig { entryFile : string ; esm5EntryFile : string ; importName : string ; moduleName : string ; esm2015Dest : string ; esm5Dest : string ; umdDest : string ; umdMinDest : string ; } /** Configuration for creating a bundle via rollup. */ interface RollupBundleConfig { entry : string ; dest : string ; format : string ; moduleName : string ; importName : string ; }