From 57b15b62895ef8d5e11807a5f6e31b7726d9fac0 Mon Sep 17 00:00:00 2001 From: dozyio Date: Sun, 21 Jun 2026 14:18:25 +0100 Subject: [PATCH] feat: bun support --- package.json | 2 + src/cmds/test.js | 2 +- src/test/bun.js | 95 +++++++++++++++++++++++++++++++++++++++++++++++ src/test/index.js | 14 +++++++ src/types.ts | 4 +- 5 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/test/bun.js diff --git a/package.json b/package.json index bc4d1a99a..9b7233c77 100644 --- a/package.json +++ b/package.json @@ -218,6 +218,7 @@ "doc-check": "node src/index.js doc-check", "spell-check": "node src/index.js spell-check", "test:node": "node src/index.js test -t node --cov", + "test:bun": "node src/index.js test -t bun", "test:deno": "node src/index.js test -t deno", "test:chrome": "node src/index.js test -t browser --cov", "test:chrome-webworker": "node src/index.js test -t webworker", @@ -242,6 +243,7 @@ "@types/mocha": "^10.0.0", "@types/node": "^24.12.2", "@typescript-eslint/parser": "^8.32.1", + "bun": "^1.3.14", "buffer": "^6.0.3", "bytes": "^3.1.0", "c8": "^11.0.0", diff --git a/src/cmds/test.js b/src/cmds/test.js index 0ebe9bf95..5228f669e 100644 --- a/src/cmds/test.js +++ b/src/cmds/test.js @@ -61,7 +61,7 @@ export default { alias: 't', describe: 'In which target environment to execute the tests', array: true, - choices: ['node', 'deno', 'browser', 'webworker', 'electron-main', 'electron-renderer', 'react-native-android', 'react-native-ios'], + choices: ['node', 'bun', 'deno', 'browser', 'webworker', 'electron-main', 'electron-renderer', 'react-native-android', 'react-native-ios'], default: userConfig.test.target }, watch: { diff --git a/src/test/bun.js b/src/test/bun.js new file mode 100644 index 000000000..ef011f7f1 --- /dev/null +++ b/src/test/bun.js @@ -0,0 +1,95 @@ +import { createRequire } from 'node:module' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { execa } from 'execa' +import fs from 'fs-extra' +import merge from '../utils/merge-options.js' +import { killIfProcessHangs } from './utils.js' + +const require = createRequire(import.meta.url) +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +const mochaPackageFile = require.resolve('mocha/package.json') +const mochaManifest = fs.readJSONSync(mochaPackageFile) +const mochaBinary = path.join(path.dirname(mochaPackageFile), mochaManifest.bin._mocha) + +const bunPackageFile = require.resolve('bun/package.json') +const bunManifest = fs.readJSONSync(bunPackageFile) +const bunBinary = path.join(path.dirname(bunPackageFile), bunManifest.bin.bun) + +/** + * @typedef {import("execa").Options} ExecaOptions + * @typedef {import('../types.js').TestOptions} TestOptions + * @typedef {import('../types.js').GlobalOptions} GlobalOptions + */ + +/** + * @param {TestOptions & GlobalOptions} argv + * @param {ExecaOptions} execaOptions + */ +export default async function testBun (argv, execaOptions) { + const progress = argv.progress ? ['--reporter=progress'] : [] + const files = argv.files.length > 0 + ? argv.files + : [ + 'test/bun.*js', + 'test/node.*js', + 'test/**/*.spec.*js', + 'test/bun.*ts', + 'test/node.*ts', + 'test/**/*.spec.*ts' + ] + + const args = [ + mochaBinary, + ...files, + ...progress, + '--ui', 'bdd', + '--require', 'source-map-support/register', + `--timeout=${argv.timeout}`, + '--color', 'true' + ] + + if (argv.grep) { + args.push(`--grep=${argv.grep}`) + } + + if (argv.watch) { + args.push('--watch') + } + + if (argv.bail) { + args.push('--bail') + } + + if (argv['--']) { + args.push(...argv['--']) + } + + // before hook + const before = await argv.fileConfig.test.before(argv) + const beforeEnv = before && before.env ? before.env : {} + + // run mocha + const proc = execa(bunBinary, args, + merge( + { + env: { + AEGIR_RUNNER: 'bun', + NODE_ENV: process.env.NODE_ENV || 'test', + ...beforeEnv + }, + preferLocal: true, + localDir: path.join(__dirname, '../..'), + stdio: argv.cov ? 'pipe' : 'inherit', + forceKillAfterDelay: 1_000 + }, + execaOptions + ) + ) + + await killIfProcessHangs(proc, argv) + + // after hook + await argv.fileConfig.test.after(argv, before) +} diff --git a/src/test/index.js b/src/test/index.js index bd09773a5..77607a107 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -1,6 +1,7 @@ import { execa } from 'execa' import pMap from 'p-map' import browser from './browser.js' +import bun from './bun.js' import deno from './deno.js' import electron from './electron.js' import node from './node.js' @@ -11,6 +12,7 @@ import rn from './react-native.js' */ const NO_BUILD = [ 'node', + 'bun', 'deno', 'electron-main', 'electron-renderer' @@ -80,6 +82,18 @@ const TASKS = [ */ enabled: (ctx) => ctx.target.includes('deno') }, + { + title: 'test bun', + /** + * @param {TestOptions & GlobalOptions} opts + * @param {ExecaOptions} execaOptions + */ + task: (opts, execaOptions) => bun({ ...opts, runner: 'bun' }, execaOptions), + /** + * @param {TestOptions & GlobalOptions} ctx + */ + enabled: (ctx) => ctx.target.includes('bun') + }, { title: 'test browser', /** diff --git a/src/types.ts b/src/types.ts index 4041116e5..50c93ebb3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -221,7 +221,7 @@ interface TestOptions { /** * In which target environment to execute the tests */ - target: Array<'node' | 'deno' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer' | 'react-native-android' | 'react-native-ios'> + target: Array<'node' | 'bun' | 'deno' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer' | 'react-native-android' | 'react-native-ios'> /** * Watch files for changes and rerun tests */ @@ -257,7 +257,7 @@ interface TestOptions { /** * Runner environment */ - runner: 'node' | 'deno' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer' | 'react-native-android' | 'react-native-ios' + runner: 'node' | 'bun' | 'deno' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer' | 'react-native-android' | 'react-native-ios' /** * Browser options */