From f2ee9ff7e319613831c6bce3cad59238f4315de5 Mon Sep 17 00:00:00 2001 From: Ben Williams Date: Mon, 10 Aug 2026 15:59:44 -0700 Subject: [PATCH] Fix TypeScript native module transforms --- package.json | 2 +- src/index.ts | 13 +++- test/vite-static-import-compat.test.ts | 86 ++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f4ec09e..51a934d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vite-plugin-native-modules", - "version": "2.4.0", + "version": "2.4.1", "description": "A Vite plugin for integrating Node.js native modules into your Vite project", "keywords": [ "electron", diff --git a/src/index.ts b/src/index.ts index 7b0cd4a..1fe8855 100644 --- a/src/index.ts +++ b/src/index.ts @@ -816,8 +816,6 @@ export default nativeModule; name, - enforce: "pre", - async resolveId(source, importer) { // Check if enabled const enabled = options.forced ?? command === "build"; @@ -1933,6 +1931,17 @@ export default nativeModule; }, }; + const resolveId = plugin.resolveId; + if (typeof resolveId === "function") { + plugin.resolveId = { + call(context: unknown, ...args: unknown[]) { + return Reflect.apply(resolveId, context, args); + }, + handler: resolveId, + order: "pre", + } as Plugin["resolveId"]; + } + const generateBundle = plugin.generateBundle; if (typeof generateBundle === "function") { plugin.generateBundle = { diff --git a/test/vite-static-import-compat.test.ts b/test/vite-static-import-compat.test.ts index 71f0cf5..af5780f 100644 --- a/test/vite-static-import-compat.test.ts +++ b/test/vite-static-import-compat.test.ts @@ -6,10 +6,33 @@ import { build as buildVite7 } from "vite7"; import vite7Package from "vite7/package.json"; import { build as buildVite8 } from "vite8"; import vite8Package from "vite8/package.json"; +import type { Rollup } from "vite"; import nativeFilePlugin from "../src/index.js"; type ViteBuild = (config: unknown) => Promise; +function outputs( + result: Rollup.RollupOutput | Rollup.RollupOutput[], +): Array { + return (Array.isArray(result) ? result : [result]).flatMap((output) => output.output); +} + +function expectNativeAddonOutput(result: Rollup.RollupOutput | Rollup.RollupOutput[]): void { + const generated = outputs(result); + const bundledCode = generated + .filter((output): output is Rollup.OutputChunk => output.type === "chunk") + .map((output) => output.code) + .join("\n"); + + expect( + generated.some( + (output) => output.type === "asset" && /addon-[A-F0-9]{8}\.node/.test(output.fileName), + ), + ).toBe(true); + expect(bundledCode).toMatch(/addon-[A-F0-9]{8}\.node/); + expect(bundledCode).not.toContain('"./addon.node"'); +} + const viteVersions = [ { build: buildVite7 as ViteBuild, major: 7, version: vite7Package.version }, { build: buildVite8 as ViteBuild, major: 8, version: vite8Package.version }, @@ -47,5 +70,68 @@ describe.each(viteVersions)( root: tempDir, }); }); + + it("bundles a TypeScript module that loads a native addon with require", async () => { + const entryPath = path.join(tempDir, "index.ts"); + fs.writeFileSync(path.join(tempDir, "addon.node"), Buffer.from("fake native module")); + fs.writeFileSync( + entryPath, + ` + export interface NativeAddon { + ping(): string; + } + + const addon: NativeAddon = require("./addon.node"); + export const ping = () => addon.ping(); + `, + ); + + const result = (await build({ + build: { + rollupOptions: { input: entryPath, output: { format: "es" } }, + ssr: true, + write: false, + }, + configFile: false, + logLevel: "silent", + plugins: [nativeFilePlugin()], + root: tempDir, + })) as Rollup.RollupOutput | Rollup.RollupOutput[]; + + expectNativeAddonOutput(result); + }); + + it("bundles an ESM TypeScript module that loads a native addon with createRequire", async () => { + const entryPath = path.join(tempDir, "index.ts"); + fs.writeFileSync(path.join(tempDir, "addon.node"), Buffer.from("fake native module")); + fs.writeFileSync( + entryPath, + ` + import { createRequire } from "node:module"; + + type NativeAddon = { + ping(): string; + }; + + const require = createRequire(import.meta.url); + const addon: NativeAddon = require("./addon.node"); + export const ping = () => addon.ping(); + `, + ); + + const result = (await build({ + build: { + rollupOptions: { input: entryPath, output: { format: "es" } }, + ssr: true, + write: false, + }, + configFile: false, + logLevel: "silent", + plugins: [nativeFilePlugin()], + root: tempDir, + })) as Rollup.RollupOutput | Rollup.RollupOutput[]; + + expectNativeAddonOutput(result); + }); }, );