license import { Analyzer , Document , FsUrlLoader , InMemoryOverlayUrlLoader , ResolvedUrl } from 'polymer-analyzer' ; import { getAnalysisDocument } from './analyzer-utils' ; import * as bundleManifestLib from './bundle-manifest' ; import { Bundle , BundleManifest , BundleStrategy , BundleUrlMapper } from './bundle-manifest' ; import * as depsIndexLib from './deps-index' ; import { DocumentCollection } from './document-collection' ; import { bundle as bundleEs6Module } from './es6-module-bundler' ; import { reserveBundleModuleExportNames } from './es6-module-utils' ; import { bundle as bundleHtmlFragment } from './html-bundler' ; import { resolvePath } from './url-utils' ; export * from './bundle-manifest' ; export interface Options { analyzer ? : Analyzer ; excludes ? : ResolvedUrl [ ] ; inlineCss ? : boolean ; inlineScripts ? : boolean ; rewriteUrlsInTemplates ? : boolean ; sourcemaps ? : boolean ; stripComments ? : boolean ; strategy ? : BundleStrategy ; treeshake ? : boolean ; urlMapper ? : BundleUrlMapper ; } export interface BundleResult { documents : DocumentCollection ; manifest : BundleManifest ; } export class Bundler { analyzer : Analyzer ; enableCssInlining : boolean ; enableScriptInlining : boolean ; excludes : ResolvedUrl [ ] ; rewriteUrlsInTemplates : boolean ; sourcemaps : boolean ; stripComments : boolean ; strategy : BundleStrategy ; treeshake : boolean ; urlMapper : BundleUrlMapper ; private _overlayUrlLoader : InMemoryOverlayUrlLoader ; constructor ( options ? ) { const opts = options ? options : { } ; if ( opts . analyzer ) { const analyzer = opts . analyzer ; this . _overlayUrlLoader = new InMemoryOverlayUrlLoader ( analyzer ) ; this . analyzer = analyzer . _fork ( { urlLoader : this . _overlayUrlLoader } ) ; } else { this . _overlayUrlLoader = new InMemoryOverlayUrlLoader ( new FsUrlLoader ( resolvePath ( '.' ) ) ) ; this . analyzer = new Analyzer ( { urlLoader : this . _overlayUrlLoader } ) ; } this . excludes = Array . isArray ( opts . excludes ) ? opts . excludes . map ( ( url ) => this . analyzer . resolveUrl ( url ) ! ) . filter ( Boolean ) : [ ] ; this . stripComments = Boolean ( opts . stripComments ) ; this . enableCssInlining = opts . inlineCss === undefined ? true : opts . inlineCss ; this . enableScriptInlining = opts . inlineScripts === undefined ? true : opts . inlineScripts ; this . rewriteUrlsInTemplates = Boolean ( opts . rewriteUrlsInTemplates ) ; this . sourcemaps = Boolean ( opts . sourcemaps ) ; this . strategy = opts . strategy || bundleManifestLib . generateSharedDepsMergeStrategy ( ) ; this . treeshake = Boolean ( opts . treeshake ) ; this . urlMapper = opts . urlMapper || bundleManifestLib . generateCountingSharedBundleUrlMapper ( this . analyzer . resolveUrl ( 'shared_bundle_' ) ! ) ; } /**
     * Analyze an HTML URL using the given contents in place of what would
     * otherwise have been loaded.
     */ async analyzeContents ( url , contents , permanent ? ) { this . _overlayUrlLoader . urlContentsMap . set ( url , contents ) ; await this . analyzer . filesChanged ( [ url ] ) ; const analysis = await this . analyzer . analyze ( [ url ] ) ; const document = getAnalysisDocument ( analysis , url ) ; if ( ! permanent ) { this . _overlayUrlLoader . urlContentsMap . delete ( url ) ; await this . analyzer . filesChanged ( [ url ] ) ; } return document ; } param manifest async bundle ( manifest ) { const documents = new DocumentCollection ( ) ; manifest = manifest . fork ( ) ; reserveBundleModuleExportNames ( this . analyzer , manifest ) ; for ( const [ url , { type : bundleType } ] of manifest . bundles ) { switch ( bundleType ) { case 'html-fragment' : documents . set ( url , await bundleHtmlFragment ( this , manifest , url ) ) ; break ; case 'es6-module' : documents . set ( url , await bundleEs6Module ( this , manifest , url ) ) ; break ; } } return { manifest , documents } ; } param entrypoints param strategy param mapper async generateManifest ( entrypoints ) { const dependencyIndex = await depsIndexLib . buildDepsIndex ( entrypoints , this . analyzer ) ; let bundles = bundleManifestLib . generateBundles ( dependencyIndex ) ; if ( this . enableScriptInlining ) { bundleManifestLib . mergeSingleEntrypointSubBundles ( bundles ) ; } this . _filterExcludesFromBundles ( bundles ) ; bundles = this . strategy ( bundles ) ; return new BundleManifest ( bundles , this . urlMapper ) ; } /**
     * Given an array of Bundles, remove all files from bundles which are in the
     * "excludes" set.  Remove any bundles which are left empty after excluded
     * files are removed.
     */ private _filterExcludesFromBundles ( bundles ) { for ( const bundle of bundles ) { for ( const exclude of this . excludes ) { bundle . files . delete ( exclude ) ; const excludeAsFolder = exclude . endsWith ( '/' ) ? exclude : exclude + '/' ; for ( const file of bundle . files ) { if ( file . startsWith ( excludeAsFolder ) ) { bundle . files . delete ( file ) ; } } } } let b = 0 ; while ( b < bundles . length ) { if ( bundles [ b ] . files . size <= 0 ) { bundles . splice ( b , 1 ) ; continue ; } ++ b ; } } }