From 8cadbb1ce8a802203f0b172798d09e16fcf0e0cd Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Thu, 25 Nov 2021 21:25:10 +0800 Subject: [PATCH] feat: support native `.node` files, closes #303 skip-release --- src/esbuild/index.ts | 2 ++ src/esbuild/native-node-module.ts | 43 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/esbuild/native-node-module.ts diff --git a/src/esbuild/index.ts b/src/esbuild/index.ts index 473ca010..ce02082a 100644 --- a/src/esbuild/index.ts +++ b/src/esbuild/index.ts @@ -19,6 +19,7 @@ import { getBabel, truthy } from '../utils' import { PrettyError } from '../errors' import { transform } from 'sucrase' import { swcPlugin } from './swc' +import { nativeNodeModulesPlugin } from './native-node-module' const getOutputExtensionMap = ( pkgTypeField: string | undefined, @@ -106,6 +107,7 @@ export async function runEsbuild( tsconfigResolvePaths: options.tsconfigResolvePaths, }), options.tsconfigDecoratorMetadata && swcPlugin(), + nativeNodeModulesPlugin(), postcssPlugin({ css }), sveltePlugin({ css }), ...(options.esbuildPlugins || []), diff --git a/src/esbuild/native-node-module.ts b/src/esbuild/native-node-module.ts new file mode 100644 index 00000000..8daf2c82 --- /dev/null +++ b/src/esbuild/native-node-module.ts @@ -0,0 +1,43 @@ +import { Plugin } from 'esbuild' + +// Copied from https://github.com/evanw/esbuild/issues/1051#issuecomment-806325487 +export const nativeNodeModulesPlugin = (): Plugin => { + return { + name: 'native-node-modules', + setup(build) { + // If a ".node" file is imported within a module in the "file" namespace, resolve + // it to an absolute path and put it into the "node-file" virtual namespace. + build.onResolve({ filter: /\.node$/, namespace: 'file' }, (args) => ({ + path: require.resolve(args.path, { paths: [args.resolveDir] }), + namespace: 'node-file', + })) + + // Files in the "node-file" virtual namespace call "require()" on the + // path from esbuild of the ".node" file in the output directory. + build.onLoad({ filter: /.*/, namespace: 'node-file' }, (args) => ({ + contents: ` + import path from ${JSON.stringify(args.path)} + try { module.exports = require(path) } + catch {} + `, + })) + + // If a ".node" file is imported within a module in the "node-file" namespace, put + // it in the "file" namespace where esbuild's default loading behavior will handle + // it. It is already an absolute path since we resolved it to one above. + build.onResolve( + { filter: /\.node$/, namespace: 'node-file' }, + (args) => ({ + path: args.path, + namespace: 'file', + }) + ) + + // Tell esbuild's default loading behavior to use the "file" loader for + // these ".node" files. + const opts = build.initialOptions + opts.loader = opts.loader || {} + opts.loader['.node'] = 'file' + }, + } +}