import { Compilation , Compiler , WebpackPluginInstance } from 'webpack' ; import { ScriptDescriptor , ScriptFactoryContext , ScriptFactory , Options , InlineScriptDescriptor } from './interface' ; export type { ScriptFactory , ScriptFactoryContext , ScriptDescriptor , Options } ; const findHtmlWebpackPlugin = ( compilation ) => { const isResolved = ( plugin ) => { return plugin . constructor && plugin . constructor . name === 'HtmlWebpackPlugin' ; } ; const instance = ( compilation . compiler . options . plugins || [ ] ) . find ( isResolved ) ; return instance ? instance . constructor : null ; } ; const isInline = ( descriptor ) : descriptor is InlineScriptDescriptor => ! ! ( descriptor as any ) . content ; const toTagDescription = ( descriptor ) => { if ( isInline ( descriptor ) ) { return { tagName : 'script' , voidTag : false , attributes : { } , innerHTML : ( descriptor . content ) . replace ( /(<)(\/script>)/g , '\\x3C$2' ) , } ; } return { tagName : 'script' , voidTag : false , attributes : { src : descriptor . src , async : descriptor . async , ... ( descriptor . crossOrigin ? { crossorigin : true } : null ) , } , } ; } ; export default class ExtraScriptWebpackPlugin { readonly createScripts : ( context : ScriptFactoryContext ) => ScriptDescriptor [ ] ; readonly options : Options ; constructor ( scriptOrFactory , options = { } ) { this . createScripts = ( context ) => { const extra = typeof scriptOrFactory === 'function' ? scriptOrFactory ( context ) : scriptOrFactory ; return Array . isArray ( extra ) ? extra : [ extra ] ; } ; this . options = options ; } apply ( compiler ) { compiler . hooks . compilation . tap ( 'extra-script-webpack-plugin' , compilation => this . injectScripts ( compilation ) ) ; } injectScripts ( compilation ) { const HtmlWebpackPlugin = findHtmlWebpackPlugin ( compilation ) ; if ( ! HtmlWebpackPlugin ) { throw new Error ( 'No html-webpack-plugin instance found' ) ; } const { alterAssetTags } = ( HtmlWebpackPlugin as any ) . getHooks ( compilation ) ; alterAssetTags . tapAsync ( 'extra-script-webpack-plugin' , ( data , callback ) => { const newData = this . addExtraScripts ( data ) ; callback ( null , newData ) ; } ) ; } addExtraScripts ( data ) { const scripts = this . createScripts ( data . plugin . options ) ; const tags = scripts . map ( toTagDescription ) ; return { ... data , assetTags : { ... data . assetTags , scripts : this . options . prepend ? [ ... tags , ... data . assetTags . scripts ] : [ ... data . assetTags . scripts , ... tags ] , } , } ; } }