license import { Analyzer , Document , Import , ResolvedUrl } from 'polymer-analyzer' ; import { ScriptTagImport } from 'polymer-analyzer/lib/html/html-script-tag' ; import { getAnalysisDocument } from './analyzer-utils' ; export type DepsIndex = Map < ResolvedUrl , Set < ResolvedUrl > > ; param entrypoints param analyzer return export async function buildDepsIndex ( entrypoints , analyzer ) { const depsIndex = new Map < ResolvedUrl , Set < ResolvedUrl > > ( ) ; const analysis = await analyzer . analyze ( entrypoints ) ; const allEntrypoints = new Set < ResolvedUrl > ( entrypoints ) ; const inlineDocuments = new Map < ResolvedUrl , Document > ( ) ; for ( const entrypoint of allEntrypoints ) { let document ; try { document = inlineDocuments . has ( entrypoint ) ? inlineDocuments . get ( entrypoint ) ! : getAnalysisDocument ( analysis , entrypoint ) ; } catch ( e ) { console . warn ( e . message ) ; } if ( document ) { const deps = getDependencies ( analyzer , document ) ; depsIndex . set ( entrypoint , new Set ( [ ... ( document . isInline ? [ ] : [ document . url ] ) , ... deps . eagerDeps ] ) ) ; for ( const dep of deps . lazyImports ) { allEntrypoints . add ( dep ) ; } for ( const [ id , imported ] of deps . moduleScriptImports ) { const subBundleUrl = getSubBundleUrl ( document . url , id ) ; allEntrypoints . add ( subBundleUrl ) ; inlineDocuments . set ( subBundleUrl , imported ) ; } } } return depsIndex ; } /**
 * Constructs a ResolvedUrl to identify a sub bundle, which is a concatenation
 * of the super bundle or containing file's URL and an id for the sub-bundle.
 */ export function getSubBundleUrl ( superBundleUrl , id ) { return `${ superBundleUrl }>${ id }` as ResolvedUrl ; } /**
 * Strips the sub-bundle id off the end of a URL to return the super bundle or
 * containing file's URL.  If there is no sub-bundle id on the provided URL, the
 * result is essentially a NOOP, since nothing will have been stripped.
 *
 * Sub-Bundle URL for an inline ES6 module document:
 *     file:///my-project/src/page.html>inline#1>es6-module
 *
 * Super-bundle URL for the containing HTML document:
 *     file:///my-project/src/page.html
 */ export function getSuperBundleUrl ( subBundleUrl ) { return subBundleUrl . split ( '>' ) . shift ( ) ! as ResolvedUrl ; } type DependencyMapEntry = { deps : Set < ResolvedUrl > ; eagerDeps : Set < ResolvedUrl > ; lazyImports : Set < ResolvedUrl > ; moduleScriptImports : Map < string , Document > ; } ; /**
 * These are the options included in every `Document#getFeatures` call, DRY'd up
 * here for brevity and consistency.
 */ const getFeaturesOptions = { imported : false , externalPackages : true , excludeBackreferences : true , } ; /**
 * For a given document, return a set of transitive dependencies, including
 * all eagerly-loaded dependencies and lazy html imports encountered.
 */ function getDependencies ( analyzer , document ) { const deps = new Set < ResolvedUrl > ( ) ; const eagerDeps = new Set < ResolvedUrl > ( ) ; const lazyImports = new Set < ResolvedUrl > ( ) ; const moduleScriptImports = new Map < string , Document > ( ) ; _getDependencies ( document , true ) ; return { deps , eagerDeps , lazyImports , moduleScriptImports } ; function _getDependencies ( document , viaEager ) { if ( document . kinds . has ( 'html-document' ) ) { _getHtmlExternalModuleDependencies ( document ) ; _getHtmlInlineModuleDependencies ( document ) ; _getImportDependencies ( document . getFeatures ( { kind : 'html-import' , ... getFeaturesOptions } ) , viaEager ) ; } if ( document . kinds . has ( 'js-document' ) ) { _getImportDependencies ( document . getFeatures ( { kind : 'js-import' , ... getFeaturesOptions } ) as Set < Import > , viaEager ) ; } } function _getHtmlExternalModuleDependencies ( document ) { let externalModuleCount = 0 ; const htmlScripts = [ ... document . getFeatures ( { kind : 'html-script' , ... getFeaturesOptions } ) ] . filter ( ( i ) => i . document !== undefined && i instanceof ScriptTagImport && i . isModule ) ; for ( const htmlScript of htmlScripts ) { const relativeUrl = analyzer . urlResolver . relative ( document . url , htmlScript . document ! . url ) ; moduleScriptImports . set ( `external#${ ++ externalModuleCount }>${ relativeUrl }>es6-module` , htmlScript . document ! ) ; } } function _getHtmlInlineModuleDependencies ( document ) { let jsDocumentCount = 0 ; const jsDocuments = [ ... document . getFeatures ( { kind : 'js-document' , ... getFeaturesOptions } ) ] . filter ( ( d ) => d . kinds . has ( 'inline-document' ) && d . parsedDocument . parsedAsSourceType === 'module' ) ; for ( const jsDocument of jsDocuments ) { moduleScriptImports . set ( `inline#${ ++ jsDocumentCount }>es6-module` , jsDocument ) ; } } function _getImportDependencies ( imports , viaEager ) { for ( const imprt of imports ) { if ( imprt . document === undefined ) { continue ; } const importUrl = imprt . document . url ; if ( imprt . lazy ) { lazyImports . add ( importUrl ) ; } if ( eagerDeps . has ( importUrl ) ) { continue ; } const isEager = viaEager && ! lazyImports . has ( importUrl ) ; if ( isEager ) { eagerDeps . add ( importUrl ) ; } else if ( deps . has ( importUrl ) ) { continue ; } deps . add ( importUrl ) ; _getDependencies ( imprt . document , isEager ) ; } } }