import fs from 'fs/promises' ; import path from 'path' ; import { randomUUID } from 'crypto' ; import { build , Loader , Plugin , BuildOptions } from 'esbuild' ; const JS_EXT_RE = /\.(mjs|cjs|ts|js|tsx|jsx)$/ ; export const EXTERNAL_REGEXP = /^[^./]|^\.[^./]|^\.\.[^/]/ ; const CACHE_DIR = path . relative ( process . cwd ( ) , './node_modules/.node-bundle-require' ) ; function inferLoader ( ext ) { if ( ext === '.mjs' || ext === '.cjs' ) { return 'js' ; } return ext . slice ( 1 ) as Loader ; } export interface Options { /**
     * The `require` function that is used to load the output file
     * Default to the global `require` function
     * This function can be asynchronous, i.e. returns a Promise
     */ require ? : ( outfile : string ) => any ; /**
     * esbuild options
     */ esbuildOptions ? : BuildOptions ; /**
     * esbuild plugin
     */ esbuildPlugins ? : Plugin [ ] ; /**
     * Get the path to the output file
     * By default we simply replace the extension with `.bundled.cjs`
     */ getOutputFile ? : ( filepath : string ) => string ; } const defaultGetOutputFile = ( filepath ) => path . resolve ( CACHE_DIR , `${ filepath }-${ Date . now ( ) }.${ randomUUID ( { disableEntropyCache : true , } ) }.bundled.cjs` ) ; export async function bundleRequire ( filepath , options ? ) { if ( ! JS_EXT_RE . test ( filepath ) ) { throw new Error ( `${ filepath } is not a valid JS file` ) ; } const getOutputFile = options ?. getOutputFile || defaultGetOutputFile ; const outfile = getOutputFile ( filepath ) ; await build ( { entryPoints : [ filepath ] , outfile , format : 'cjs' , platform : 'node' , bundle : true , ... options ?. esbuildOptions , plugins : [ ... ( options ?. esbuildPlugins || [ ] ) , { name : 'native-node-modules' , setup ( build ) { build . onResolve ( { filter : /\.node$/ , namespace : 'file' } , args => ( { path : require . resolve ( args . path , { paths : [ args . resolveDir ] } ) , namespace : 'node-file' , } ) ) ; build . onLoad ( { filter : /.*/ , namespace : 'node-file' } , args => ( { contents : `
              import path from ${ JSON . stringify ( args . path ) }
              try { module.exports = require(path) }
              catch {}
            ` , } ) ) ; build . onResolve ( { filter : /\.node$/ , namespace : 'node-file' } , args => ( { path : args . path , namespace : 'file' , } ) ) ; const opts = build . initialOptions ; opts . loader = opts . loader || { } ; opts . loader [ '.node' ] = 'file' ; } , } , { name : 'replace-path' , setup ( ctx ) { ctx . onLoad ( { filter : JS_EXT_RE } , async ( args ) => { const contents = await fs . readFile ( args . path , 'utf-8' ) ; return { contents : contents . replace ( /\b__filename\b/g , JSON . stringify ( args . path ) ) . replace ( /\b__dirname\b/g , JSON . stringify ( path . dirname ( args . path ) ) ) . replace ( /\bimport\.meta\.url\b/g , JSON . stringify ( `file://${ args . path }` ) ) , loader : inferLoader ( path . extname ( args . path ) ) , } ; } ) ; } , } , { name : 'make-all-packages-external' , setup ( _build ) { _build . onResolve ( { filter : EXTERNAL_REGEXP } , args => { let external = true ; if ( args . kind === 'entry-point' ) { external = false ; } return { path : args . path , external , } ; } ) ; } , } , ] , } ) ; let mod ; const req = options ?. require || require ; try { mod = await req ( outfile ) ; } finally { await fs . unlink ( outfile ) ; } return mod ; }