Builder
When ESBench starts, it first builds suites for benchmark with builders. A builder is an object contain 2 properties: name
specifies the default name, and build()
method generates the output.
export interface Builder {
/**
* Suggest a name for the builder, it will be used if no name specified from config.
*/
name: string;
/**
* Transform the files needed for the benchmark.
*
* @param outDir The directory in which all generated chunks should be placed.
* @param files Import specifiers of suites relative to cwd.
*/
build(outDir: string, files: string[]): Awaitable<void>;
}
The responsibilities of the builder are:
- handle imports so that the target platform can load modules (e.g. bundle modules from
node_modules
for browsers). - converting code that is not supported by the target platform (e.g. TS -> JS)
Inputs
A builder instance will only be called once. Each item of the toolchain in the configuration matches a number of files that will be added to the files list of all builders for that item at build time and passed to encode
function.
For example, with this config, the files
argument of myBuilder
will include:
- Files matches glob patterns
./foo/*.[jt]s
and./bar/*.js
. - Files matches the pattern
./baz/*.js
but not match./**/utils-*.js
.
const myBuilder = {
name: "mybuilder",
encode(outDir, files) { /* ... */ },
};
export default defineConfig({
toolchains: [{
include: ["./foo/*.[jt]s", "./bar/*.js"],
builders: [myBuilder],
}, {
include: ["./baz/*.js"],
exclude: ["./**/utils-*.js"],
builders: [myBuilder],
}],
});
ESBench enforces the convention that the output of a build must contain a file named <outDir>/index.js
, which will be used by executors, and it should have a default export of the function:
/**
* The entry file that build output needs to export a function that match the signature.
*
* This function needs to call `runAndSend` and provide the import module function.
*/
type EntryExport = (postMessage: Channel, files: string[], pattern?: string) => Promise<void>;
Implementation
A example of integrate ESBench with Rolldown.
- Create an entry module that contains imports of all suites, so that Rolldown can resolve them.
- Call
rolldown
to start the build process. - Write files to
outDir
, the entry filename must beindex.js
.
import { writeFileSync } from "node:fs";
import { Builder, defineConfig, WebRemoteExecutor } from "esbench/host";
import { Plugin, rolldown } from 'rolldown';
class RolldownBuilder implements Builder {
name = "rolldown"
async build(outDir: string, files: string[]) {
// A better approach is use a plugin to create it from string.
const imports = files.map(s => `"${s}":()=>import("${s}")`).join()
writeFileSync("temp-entry.js", `\
import { runAndSend } from "esbench";
const suites = {${imports}\n};
const doImport = file => suites[file]();
export default function (post, file, pattern) {
return runAndSend(post, doImport, file, pattern);
}`
);
// Build and write output to `outDir`.
const build = await rolldown({
input: {
index: "temp-entry.js",
},
resolve: {
conditionNames: ['import'],
},
});
await build.write({
dir: outDir,
chunkFileNames: "[name].js", // The entry file must be "index.js"
})
}
}