/**
 * refactor from https://github.com/sorrycc/esbuild-webpack-plugin/blob/master/src/index.ts
 * support webpack 5 and esbuild ^0.12.22
 */ import webpack , { ModuleFilenameHelpers , Compiler , Compilation } from 'webpack' ; import { transform , TransformOptions , TransformResult } from 'esbuild' ; export type ESBuildPluginOptions = Omit < TransformOptions , 'minify' | 'sourcemap' | 'sourcefile' > ; export class ESBuildPlugin { private readonly options : ESBuildPluginOptions ; constructor ( options = { } ) { this . options = options ; } async transformCode ( { source , file , devtool , } ) { let result ; let pluginOptions ; if ( /\.css(\?.*)?$/i . test ( file ) ) { pluginOptions = { ... this . options , loader : 'css' } ; } else { pluginOptions = this . options ; } const _transform = async ( options ) => transform ( source , { ... options , minify : true , sourcemap : Boolean ( devtool ) , sourcefile : file , } ) ; try { result = await _transform ( pluginOptions ) ; } catch ( e ) { console . error ( e ) ; } return result ; } apply ( compiler ) { const { devtool } = compiler . options ; const isWebpack5 = webpack . version . startsWith ( '5' ) ; const plugin = 'ESBuild Plugin' ; compiler . hooks . compilation . tap ( plugin , ( compilation ) => { if ( isWebpack5 ) { const { Compilation } = compiler . webpack ; compilation . hooks . processAssets . tapPromise ( { name : plugin , stage : Compilation . PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE , } , async ( assets ) => { await this . updateAssets ( compilation , Object . keys ( assets ) , devtool ) ; } ) ; } else { compilation . hooks . optimizeChunkAssets . tapPromise ( plugin , async ( chunks ) => { for ( const chunk of chunks ) { await this . updateAssets ( compilation , Array . from ( chunk . files ) , devtool ) ; } } ) ; } } ) ; } async updateAssets ( compilation , files , devtool ) { const matchObject = ModuleFilenameHelpers . matchObject . bind ( undefined , { } ) ; for ( const file of files ) { if ( ! matchObject ( file ) ) { continue ; } if ( ! /\.(m?js|css)(\?.*)?$/i . test ( file ) ) { continue ; } const assetSource = compilation . assets [ file ] ; const { source , map } = assetSource . sourceAndMap ( ) ; const result = await this . transformCode ( { source : source . toString ( ) , file , devtool , } ) ; compilation . updateAsset ( file , ( ) => { const { SourceMapSource , RawSource } = compilation . compiler . webpack . sources ; if ( devtool ) { return new SourceMapSource ( result ?. code || '' , file , result ?. map as any , source . toString ( ) , map as any , true ) as any ; } else { return new RawSource ( result ?. code || '' ) as any ; } } ) ; } } }