diff --git a/packages/bun-polyfills/tools/bun_test_loader.mjs b/packages/bun-polyfills/tools/bun_test_loader.mjs index 2466157ce7..c5c6941983 100644 --- a/packages/bun-polyfills/tools/bun_test_loader.mjs +++ b/packages/bun-polyfills/tools/bun_test_loader.mjs @@ -3,17 +3,27 @@ /// import { fileURLToPath, pathToFileURL } from 'node:url'; import path from 'node:path'; -import util from 'node:util'; import fs from 'node:fs'; import $ from 'chalk'; import bunwasm from 'bun-wasm'; import { TransformResponseStatus } from 'bun-wasm/schema'; +const testRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'test'); +const tsconfigPath = path.resolve(testRoot, 'tsconfig.json'); +/** @type {Record} */ +let tsconfigPaths = {}; +if (fs.existsSync(tsconfigPath)) { + const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8')); + tsconfigPaths = tsconfig.compilerOptions.paths; +} else { + throw new Error('No tsconfig.json found at: ' + tsconfigPath); +} + await bunwasm.init(); const NO_STACK = () => void 0; const decoder = new TextDecoder('utf-8'); const libRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'src'); -const knownBunModules = ['sqlite', 'ffi', 'jsc', 'test']; +const knownBunModules = ['sqlite', 'ffi', 'jsc', 'test', 'wrap']; /** @type {string} */ let mainURL; @@ -24,13 +34,19 @@ export async function resolve(specifier, context, nextResolve) { if (specifier.startsWith('bun:')) { const module = specifier.slice(4); if (!knownBunModules.includes(module)) { - const err = new Error(`[bun-polyfills] Unknown or unimplemented bun module "${specifier}"`); + const err = new Error(`[bun-polyfills] Unknown or unimplemented bun module "${specifier}" imported from "${context.parentURL}"`); Error.captureStackTrace(err, NO_STACK); throw err; } - if (module === 'sqlite') throw new Error('bun:sqlite polyfill is not implemented yet'); + if (module === 'wrap') return { url: 'bun:wrap@' + context.parentURL, format: 'module', shortCircuit: true }; return { url: pathToFileURL(path.resolve(libRoot, 'modules', module + '.js')).href, format: 'module', shortCircuit: true }; } + // Not the really an accurate way to do this, but it works for the test suite usages + if (Object.keys(tsconfigPaths).includes(specifier)) { + const paths = tsconfigPaths[specifier]; + const resolved = paths.map(p => pathToFileURL(path.resolve(testRoot, p)).href); + specifier = resolved[0]; + } //console.debug('trying to resolve', specifier, 'from', context.parentURL); /** @type {Resolve.Return | Error} */ let next; @@ -68,9 +84,27 @@ export async function resolve(specifier, context, nextResolve) { else return next; } +const APPLY_IMPORT_META_POLYFILL = /*js*/` + ;(await import("${pathToFileURL(path.resolve(libRoot, 'global', 'importmeta.js')).href}")).default(import.meta); +`; /** @type {load} */ export async function load(url, context, nextLoad) { //console.debug('Loading', url, 'with context', context); + if (url.startsWith('bun:wrap@')) { + return { + shortCircuit: true, format: 'module', source: /*js*/` + import { createRequire } from 'node:module'; + const require = createRequire(import.meta.url.slice(9)); + export const __require = require; + export default new Proxy({ + __require: require, + }, { + get(target, prop) { + return target[prop]; + }, + });` + }; + } if (context.format === 'tsmodule' || context.format === 'tscommonjs') { const filepath = fileURLToPath(url); const src = fs.readFileSync(filepath, 'utf-8'); @@ -87,29 +121,41 @@ export async function load(url, context, nextLoad) { return { shortCircuit: true, format: /** @type {ModuleFormat} */(context.format.slice(2)), - source: decoder.decode(transform.files[0].data), + source: (context.format === 'tsmodule' + ? (url.includes('/bun-polyfills/') ? '' : APPLY_IMPORT_META_POLYFILL) + : '') + decoder.decode(transform.files[0].data), }; } if (context.format === 'json') context.importAssertions.type = 'json'; + const loaded = await nextLoad(url, context); if (url.startsWith('file://') && loaded.format === 'module') { const src = typeof loaded.source === 'string' ? loaded.source : decoder.decode(loaded.source); - return { shortCircuit: true, format: 'module', source: src }; + return { + shortCircuit: true, + format: 'module', + source: (url.includes('/bun-polyfills/') ? '' : APPLY_IMPORT_META_POLYFILL) + src + }; } else return loaded; } +/** @type {globalPreload} */ +export function globalPreload(ctx) { + return /*js*/`process.env.BUN_POLYFILLS_TEST_RUNNER = 1;`; +} + /** @param {import('bun-wasm/schema').Message[]} buildErrors */ function formatBuildErrors(buildErrors) { const formatted = buildErrors.map(err => { const loc = err.data.location; const str = `${$.redBright('error')}${$.gray(':')} ${$.bold(err.data.text)}\n` + - (loc - ? `${highlightErrorChar(loc.line_text, loc.column)}\n` + + (loc + ? `${highlightErrorChar(loc.line_text, loc.column)}\n` + $.redBright.bold('^'.padStart(loc.column)) + '\n' + `${$.bold(loc.file)}${$.gray(':')}${$.yellowBright(loc.line)}${$.gray(':')}${$.yellowBright(loc.column)} ${$.gray(loc.offset)}` - : '' - ); + : '' + ); const newerr = new Error(str); newerr.name = 'BuildError'; newerr.stack = str;