UNPKG

@speedy-js/config-loader

Version:

An out-of-box config loader with TypeScript support.

116 lines (104 loc) 3.2 kB
/** * Forked from https://github.com/egoist/bundle-require/blob/main/src/index.ts * Thanks for the great work from @egoist. */ import fs from 'fs'; import path from 'path'; import { build, Loader, Plugin, BuildOptions } from 'esbuild'; import { getPackagesFromNodeModules } from './utils'; const JS_EXT_RE = /\.(mjs|cjs|ts|js|tsx|jsx)$/; function inferLoader(ext: string): Loader { if (ext === '.mjs' || ext === '.cjs') return 'js'; return ext.slice(1) as Loader; } export interface Options { /** * The filepath to bundle and require */ filepath: string /** * 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 */ // eslint-disable-next-line no-unused-vars 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 /** * Whether the keep the temporary file. */ preserveTemporaryFile?: boolean; } const defaultGetOutputFile = (filepath: string) => filepath.replace(JS_EXT_RE, '.bundled.cjs'); export async function bundleRequire(options: Options) { if (!JS_EXT_RE.test(options.filepath)) { throw new Error(`${options.filepath} is not a valid JS file`); } const getOutputFile = options.getOutputFile || defaultGetOutputFile; const outfile = getOutputFile(options.filepath); const packageNames = getPackagesFromNodeModules(); await build({ entryPoints: [options.filepath], outfile, format: 'cjs', platform: 'node', bundle: true, ...options.esbuildOptions, plugins: [ ...(options.esbuildPlugins || []), { name: 'replace-path', setup(ctx) { ctx.onResolve({ filter: /.*/ }, args => { const isPackage = packageNames.some(name => args.path === name || args.path.startsWith(`${name}/`)); if (isPackage) { return { path: args.path, external: true, }; } }); ctx.onLoad({ filter: JS_EXT_RE }, async args => { const contents = await fs.promises.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)), }; }); }, }, ], }); let mod: any; const req = options.require || require; try { mod = await req(outfile); } finally { if (!options.preserveTemporaryFile) { // Remove the outfile after executed await fs.promises.unlink(outfile); } } return mod; }