diff --git a/.changepacks/changepack_log_fixBunIsolatedWasmResolution20260830.json b/.changepacks/changepack_log_fixBunIsolatedWasmResolution20260830.json new file mode 100644 index 00000000..75e4d676 --- /dev/null +++ b/.changepacks/changepack_log_fixBunIsolatedWasmResolution20260830.json @@ -0,0 +1,7 @@ +{ + "changes": { + "packages/next-plugin/package.json": "Patch" + }, + "note": "Resolve WASM from the plugin's physical install location in Bun isolated installs", + "date": "2026-08-30T03:43:34.977Z" +} diff --git a/packages/next-plugin/src/__tests__/wasm.test.ts b/packages/next-plugin/src/__tests__/wasm.test.ts index e323b41c..da10f9b3 100644 --- a/packages/next-plugin/src/__tests__/wasm.test.ts +++ b/packages/next-plugin/src/__tests__/wasm.test.ts @@ -1,20 +1,43 @@ +import { + mkdirSync, + mkdtempSync, + rmSync, + symlinkSync, + writeFileSync, +} from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' + import * as wasm from '@devup-ui/wasm' import * as webpackPlugin from '@devup-ui/webpack-plugin' -import { afterEach, describe, expect, it } from 'bun:test' +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'bun:test' import { loadWasm, loadWebpackPlugin, + requireFromPlugin, requireWasm, setWasmForTesting, setWebpackPluginForTesting, } from '../wasm' +const originalCwd = process.cwd() +let tempRoots: string[] = [] + +beforeAll(() => { + tempRoots = [] +}) + afterEach(() => { + process.chdir(originalCwd) setWasmForTesting(undefined) setWebpackPluginForTesting(undefined) }) +afterAll(() => { + for (const root of tempRoots) rmSync(root, { recursive: true, force: true }) +}) + describe('WASM selection', () => { it('uses an injected namespace in tests', () => { setWasmForTesting(wasm) @@ -27,6 +50,44 @@ describe('WASM selection', () => { expect(typeof loadWasm(true).codeExtract).toBe('function') }) + it('resolves dependencies from a Bun-style isolated install', () => { + const root = mkdtempSync(join(tmpdir(), 'devup-ui-next-isolated-')) + tempRoots.push(root) + const isolatedNodeModules = join( + root, + 'node_modules/.bun/next-plugin/node_modules', + ) + const pluginDir = join(isolatedNodeModules, '@devup-ui/next-plugin') + const wasmDir = join(isolatedNodeModules, '@devup-ui/wasm') + const rootScope = join(root, 'node_modules/@devup-ui') + mkdirSync(pluginDir, { recursive: true }) + mkdirSync(wasmDir, { recursive: true }) + mkdirSync(rootScope, { recursive: true }) + writeFileSync( + join(pluginDir, 'package.json'), + JSON.stringify({ name: '@devup-ui/next-plugin' }), + ) + writeFileSync( + join(wasmDir, 'package.json'), + JSON.stringify({ name: '@devup-ui/wasm', main: 'index.cjs' }), + ) + writeFileSync( + join(wasmDir, 'index.cjs'), + 'module.exports = { isolated: true }', + ) + symlinkSync( + pluginDir, + join(rootScope, 'next-plugin'), + process.platform === 'win32' ? 'junction' : 'dir', + ) + + process.chdir(root) + + expect(requireFromPlugin<{ isolated: boolean }>('@devup-ui/wasm')).toEqual({ + isolated: true, + }) + }) + it.each(['MODULE_NOT_FOUND', 'ERR_PACKAGE_PATH_NOT_EXPORTED'])( 'falls back to the full package when the lite export fails with %s', (code) => { diff --git a/packages/next-plugin/src/wasm.ts b/packages/next-plugin/src/wasm.ts index 50bd97e1..e38bcccc 100644 --- a/packages/next-plugin/src/wasm.ts +++ b/packages/next-plugin/src/wasm.ts @@ -1,4 +1,4 @@ -import { existsSync } from 'node:fs' +import { existsSync, realpathSync } from 'node:fs' import { createRequire } from 'node:module' import { join } from 'node:path' @@ -11,7 +11,8 @@ let fullWasm: DevupWasm | undefined let liteWasm: DevupWasm | undefined let webpackPlugin: DevupWebpackPlugin | undefined -function requireFromPlugin(specifier: string): T { +/** @internal Resolve dependencies from the plugin's physical install location. */ +export function requireFromPlugin(specifier: string): T { const installedPackage = join( process.cwd(), 'node_modules/@devup-ui/next-plugin/package.json', @@ -25,7 +26,7 @@ function requireFromPlugin(specifier: string): T { : existsSync(workspacePackage) ? workspacePackage : join(process.cwd(), 'package.json') - return createRequire(requireBase)(specifier) as T + return createRequire(realpathSync(requireBase))(specifier) as T } type WasmLoader = (specifier: string) => DevupWasm