From fab5016ba867ce7a73f475251924bba7177e4760 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:26:13 +0900 Subject: [PATCH 1/4] fix: use inject plugin instead of `esbuild.banner` --- src/index.ts | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index 35f4aed..e20fd9d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,8 @@ import esbuildPlugin from 'node-stdlib-browser/helpers/esbuild/plugin' import type { Plugin } from 'vite' import { compareModuleNames, isEnabled, isNodeProtocolImport, toRegExp, withoutNodeProtocol } from './utils' +type TransformHook = Extract + export type BuildTarget = 'build' | 'dev' export type BooleanOrBuildTarget = boolean | BuildTarget export type ModuleName = keyof typeof stdLibBrowser @@ -130,7 +132,7 @@ const globalShimBanners = { * }) * ``` */ -export const nodePolyfills = (options: PolyfillOptions = {}): Plugin => { +export const nodePolyfills = (options: PolyfillOptions = {}): Plugin[] => { const optionsResolved: PolyfillOptionsResolved = { include: [], exclude: [], @@ -199,7 +201,23 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin => { ``, ].join('\n') - return { + let rawInjectPlugin: Plugin | false | undefined + function transform(this: ThisParameterType, code: string, id: string, opts: Parameters[2]) { + if (rawInjectPlugin === undefined) { + throw new Error('transform called before inject plugin initialization') + } + if (rawInjectPlugin === false) { + return + } + return (rawInjectPlugin.transform as TransformHook).call(this, code, id, opts) + } + const injectPlugin: Plugin = { + name: 'vite-plugin-node-polyfills:inject', + enforce: 'post', + transform, + } + + const plugin: Plugin = { name: 'vite-plugin-node-polyfills', config(config, env) { const isDev = env.command === 'serve' @@ -220,6 +238,17 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin => { ...(isEnabled(optionsResolved.globals.process, 'build') ? { process: 'vite-plugin-node-polyfills/shims/process' } : {}), } + const isNativeInjectAvailable = env.command === 'build' && isRolldownVite + rawInjectPlugin = (Object.keys(shimsToInject).length > 0 && !isNativeInjectAvailable) ? inject(shimsToInject) as Plugin : false + if (rawInjectPlugin === false) { + delete injectPlugin.transform + } else { + // hook filters are only supported in Vite 6.3.0+ + (transform as any).filter = { + code: new RegExp(globalShimPaths.map((re) => `\b${re}\b`).join('|')), + } + } + return { build: { rollupOptions: { @@ -232,17 +261,11 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin => { rollupWarn(warning) }) }, - ...Object.keys(shimsToInject).length > 0 - ? isRolldownVite - ? { transform: { inject: shimsToInject } } - : { plugins: [inject(shimsToInject)] } + ...(Object.keys(shimsToInject).length > 0 && isRolldownVite) + ? { transform: { inject: shimsToInject } } : {}, }, }, - esbuild: { - // In dev, the global polyfills need to be injected as a banner in order for isolated scripts (such as Vue SFCs) to have access to them. - banner: isDev ? globalShimsBanner : undefined, - }, optimizeDeps: { exclude: [ ...globalShimPaths, @@ -308,4 +331,8 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin => { } }, } + return [ + injectPlugin, + plugin, + ] } From d3e64f1ccc2f3e1422d2bfb4f10c442d7571e4bb Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:20:53 +0900 Subject: [PATCH 2/4] test: update expected output --- .../global-references/index.test.ts | 30 ++-------- test/integration/import-globals/index.test.ts | 56 ------------------- 2 files changed, 6 insertions(+), 80 deletions(-) diff --git a/test/integration/global-references/index.test.ts b/test/integration/global-references/index.test.ts index 8bace33..dc139d4 100644 --- a/test/integration/global-references/index.test.ts +++ b/test/integration/global-references/index.test.ts @@ -7,12 +7,7 @@ describe('import globals', () => { const result = await transformDev(`Buffer.from('test')`) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill + import { default as Buffer } from "/shims/buffer/dist/index.js"; Buffer.from("test"); `)) @@ -28,8 +23,7 @@ describe('import globals', () => { }) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill + import { default as Buffer } from "/shims/buffer/dist/index.js"; Buffer.from("test"); `)) @@ -41,12 +35,7 @@ describe('import globals', () => { const result = await transformDev(`console.log(global)`) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill + import { default as global } from "/shims/global/dist/index.js"; console.log(global); `)) @@ -62,8 +51,7 @@ describe('import globals', () => { }) expect(result?.code).toEqual(formatWhitespace(` - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill + import { default as global } from "/shims/global/dist/index.js"; console.log(global); `)) @@ -75,12 +63,7 @@ describe('import globals', () => { const result = await transformDev(`console.log(process)`) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill + import { default as process } from "/shims/process/dist/index.js"; console.log(process); `)) @@ -96,8 +79,7 @@ describe('import globals', () => { }) expect(result?.code).toEqual(formatWhitespace(` - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill + import { default as process } from "/shims/process/dist/index.js"; console.log(process); `)) diff --git a/test/integration/import-globals/index.test.ts b/test/integration/import-globals/index.test.ts index 90ef455..7fa8173 100644 --- a/test/integration/import-globals/index.test.ts +++ b/test/integration/import-globals/index.test.ts @@ -10,13 +10,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import Buffer from "/shims/buffer/dist/index.js"; console.log(Buffer); `)) @@ -29,13 +22,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import Buffer from "/shims/buffer/dist/index.js"; console.log(Buffer); `)) @@ -48,13 +34,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import Buffer from "/shims/buffer/dist/index.js"; console.log(Buffer); `)) @@ -67,13 +46,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import Buffer from "/shims/buffer/dist/index.js"; console.log(Buffer); `)) @@ -88,13 +60,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import process from "/shims/process/dist/index.js"; console.log(process); `)) @@ -107,13 +72,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import process from "/shims/process/dist/index.js"; console.log(process); `)) @@ -126,13 +84,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import process from "/shims/process/dist/index.js"; console.log(process); `)) @@ -145,13 +96,6 @@ describe('import globals', () => { `) expect(result?.code).toEqual(formatWhitespace(` - import __buffer_polyfill from "/shims/buffer/dist/index.js" - globalThis.Buffer = globalThis.Buffer || __buffer_polyfill - import __global_polyfill from "/shims/global/dist/index.js" - globalThis.global = globalThis.global || __global_polyfill - import __process_polyfill from "/shims/process/dist/index.js" - globalThis.process = globalThis.process || __process_polyfill - import process from "/shims/process/dist/index.js"; console.log(process); `)) From 5f46eb497a567fdd5c6b1e552bb2c0cbfed2b20b Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:46:30 +0900 Subject: [PATCH 3/4] fix: incorrect filter --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index e20fd9d..3216043 100644 --- a/src/index.ts +++ b/src/index.ts @@ -245,7 +245,7 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin[] => { } else { // hook filters are only supported in Vite 6.3.0+ (transform as any).filter = { - code: new RegExp(globalShimPaths.map((re) => `\b${re}\b`).join('|')), + code: new RegExp(Object.keys(shimsToInject).join('|')), } } From 4474bcaffa3da34fa742c1dfb0ee4189c4b91b7e Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 30 Mar 2026 12:08:34 +0900 Subject: [PATCH 4/4] fix: support imports with trailing slash in Vite 8 --- .github/workflows/ci.yml | 4 ++-- src/index.ts | 39 +++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7491ef1..591bf24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,10 +32,10 @@ jobs: - run: pnpm playwright install --with-deps - name: update overrides to use vite 8 and re-install run: | - jq '.pnpm.overrides.vite = "8.0.1"' package.json > package.tmp.json + jq '.pnpm.overrides.vite = "8.0.1" | .pnpm.overrides.vitest = "^4.1.2"' package.json > package.tmp.json mv package.tmp.json package.json pnpm i --no-frozen-lockfile - - run: pnpm -r test:e2e + - run: pnpm -r test test-unit: runs-on: ubuntu-latest steps: diff --git a/src/index.ts b/src/index.ts index 3216043..ff150d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -104,6 +104,10 @@ const globalShimBanners = { ], } +const isModuleName = (name: string): name is ModuleName => { + return name in stdLibBrowser +} + /** * Returns a Vite plugin to polyfill Node's Core Modules for browser environments. Supports `node:` protocol imports. * @@ -194,6 +198,27 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin[] => { ...((isEnabled(optionsResolved.globals.process, 'dev')) ? [require.resolve('vite-plugin-node-polyfills/shims/process')] : []), ] + const resolvePlugin: Plugin = { + name: 'vite-plugin-node-polyfills:resolve', + enforce: 'pre', + resolveId: { + // @ts-expect-error -- hook filters are only supported in Vite 6.3.0+ + filter: { id: new RegExp(Object.keys(stdLibBrowser).map((name) => `^${name}\/?$`).join('|')) }, + async handler(source, importer, opts) { + if (!optionsResolved.protocolImports && isNodeProtocolImport(source)) { + return + } + source = source.endsWith('/') ? source.slice(0, -1) : source // strip trailing slash + if (!isModuleName(source) || isExcluded(source)) { + return + } + const aliased = toOverride(withoutNodeProtocol(source)) || stdLibBrowser[source] + const resolved = await this.resolve(aliased, importer, { skipSelf: true, ...opts }) + return resolved + }, + }, + } + const globalShimsBanner = [ ...((isEnabled(optionsResolved.globals.Buffer, 'dev')) ? globalShimBanners.buffer : []), ...((isEnabled(optionsResolved.globals.global, 'dev')) ? globalShimBanners.global : []), @@ -273,16 +298,11 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin[] => { ...isRolldownVite ? { rolldownOptions: { - resolve: { - // https://github.com/niksy/node-stdlib-browser/blob/3e7cd7f3d115ac5c4593b550e7d8c4a82a0d4ac4/README.md?plain=1#L150 - alias: { - ...polyfills, - }, - }, transform: { define: defines, }, plugins: [ + resolvePlugin, { name: 'vite-plugin-node-polyfills:optimizer', banner: isDev ? globalShimsBanner : undefined, @@ -322,16 +342,11 @@ export const nodePolyfills = (options: PolyfillOptions = {}): Plugin[] => { }, }, }, - resolve: { - // https://github.com/niksy/node-stdlib-browser/blob/3e7cd7f3d115ac5c4593b550e7d8c4a82a0d4ac4/README.md?plain=1#L150 - alias: { - ...polyfills, - }, - }, } }, } return [ + resolvePlugin, injectPlugin, plugin, ]