mirror of
https://github.com/oven-sh/bun
synced 2026-02-18 23:01:58 +00:00
Compare commits
2 Commits
claude/fix
...
feature/v8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bea1ad8a3f | ||
|
|
3a955bc824 |
4
packages/bun-types/globals.d.ts
vendored
4
packages/bun-types/globals.d.ts
vendored
@@ -1060,7 +1060,7 @@ interface ArrayBuffer {
|
||||
/**
|
||||
* Resize an ArrayBuffer in-place.
|
||||
*/
|
||||
resize(byteLength: number): ArrayBuffer;
|
||||
resize(newByteLength?: number): void;
|
||||
|
||||
/**
|
||||
* Returns a section of an ArrayBuffer.
|
||||
@@ -1405,7 +1405,7 @@ interface PromiseConstructor {
|
||||
*/
|
||||
withResolvers<T>(): {
|
||||
promise: Promise<T>;
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
resolve: (value: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
};
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
#include "ZigSourceProvider.h"
|
||||
#include <JavaScriptCore/FunctionPrototype.h>
|
||||
#include "JSCommonJSModule.h"
|
||||
#include <JavaScriptCore/JSModuleLoader.h>
|
||||
#include <JavaScriptCore/JSModuleNamespaceObject.h>
|
||||
#include <JavaScriptCore/JSSourceCode.h>
|
||||
#include <JavaScriptCore/LazyPropertyInlines.h>
|
||||
@@ -684,19 +683,6 @@ JSC_DEFINE_CUSTOM_SETTER(setterUnderscoreCompile,
|
||||
return true;
|
||||
}
|
||||
|
||||
static BunLoaderType loaderFromFilename(const String& filename)
|
||||
{
|
||||
if (filename.endsWith(".ts"_s))
|
||||
return BunLoaderTypeTS;
|
||||
if (filename.endsWith(".tsx"_s))
|
||||
return BunLoaderTypeTSX;
|
||||
if (filename.endsWith(".jsx"_s))
|
||||
return BunLoaderTypeJSX;
|
||||
// _compile always receives JavaScript/TypeScript source code.
|
||||
// Default to JS for unknown extensions (e.g. .custom, .node, etc.)
|
||||
return BunLoaderTypeJS;
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(functionJSCommonJSModule_compile, (JSGlobalObject * globalObject, CallFrame* callframe))
|
||||
{
|
||||
auto* moduleObject = jsDynamicCast<JSCommonJSModule*>(callframe->thisValue());
|
||||
@@ -714,95 +700,46 @@ JSC_DEFINE_HOST_FUNCTION(functionJSCommonJSModule_compile, (JSGlobalObject * glo
|
||||
String filenameString = filenameValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
String wrappedString;
|
||||
auto* zigGlobalObject = jsCast<Zig::GlobalObject*>(globalObject);
|
||||
if (zigGlobalObject->hasOverriddenModuleWrapper) [[unlikely]] {
|
||||
wrappedString = makeString(
|
||||
zigGlobalObject->m_moduleWrapperStart,
|
||||
sourceString,
|
||||
zigGlobalObject->m_moduleWrapperEnd);
|
||||
} else {
|
||||
wrappedString = makeString(
|
||||
"(function(exports,require,module,__filename,__dirname){"_s,
|
||||
sourceString,
|
||||
"\n})"_s);
|
||||
}
|
||||
|
||||
// Determine loader from filename extension.
|
||||
BunLoaderType loader = loaderFromFilename(filenameString);
|
||||
moduleObject->sourceCode = makeSource(
|
||||
WTF::move(wrappedString),
|
||||
SourceOrigin(URL::fileURLWithFileSystemPath(filenameString)),
|
||||
JSC::SourceTaintedOrigin::Untainted,
|
||||
filenameString,
|
||||
WTF::TextPosition(),
|
||||
JSC::SourceProviderSourceType::Program);
|
||||
|
||||
// Transpile the source through Bun's transpiler. This handles:
|
||||
// - TypeScript syntax (.ts, .tsx)
|
||||
// - JSX syntax (.jsx, .tsx)
|
||||
// - ESM import/export → CJS conversion (when code uses CJS features)
|
||||
BunString specifier = Bun::toStringRef(filenameString);
|
||||
BunString referrer = BunStringEmpty;
|
||||
auto sourceUTF8 = sourceString.utf8();
|
||||
ZigString sourceZig = { reinterpret_cast<const unsigned char*>(sourceUTF8.data()), sourceUTF8.length() };
|
||||
ErrorableResolvedSource transpileResult = {};
|
||||
|
||||
Bun__transpileVirtualModule(globalObject, &specifier, &referrer, &sourceZig, loader, &transpileResult);
|
||||
EncodedJSValue encodedFilename = JSValue::encode(filenameValue);
|
||||
#if OS(WINDOWS)
|
||||
JSValue dirnameValue = JSValue::decode(Bun__Path__dirname(globalObject, true, &encodedFilename, 1));
|
||||
#else
|
||||
JSValue dirnameValue = JSValue::decode(Bun__Path__dirname(globalObject, false, &encodedFilename, 1));
|
||||
#endif
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
if (!transpileResult.success) {
|
||||
if (!throwScope.exception()) {
|
||||
throwException(globalObject, throwScope, createError(globalObject, "Failed to transpile source in Module._compile"_s));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
String dirnameString = dirnameValue.toWTFString(globalObject);
|
||||
|
||||
ResolvedSource& source = transpileResult.result.value;
|
||||
|
||||
if (source.isCommonJSModule) {
|
||||
// The transpiler detected CJS usage and already wrapped the code in
|
||||
// (function(exports, require, module, __filename, __dirname) { ... }).
|
||||
String wrappedString = source.source_code.toWTFString(BunString::ZeroCopy);
|
||||
|
||||
if (zigGlobalObject->hasOverriddenModuleWrapper) [[unlikely]] {
|
||||
// Strip the default wrapper and re-wrap with the custom one.
|
||||
auto trimStart = wrappedString.find('\n');
|
||||
if (trimStart != WTF::notFound) {
|
||||
wrappedString = makeString(
|
||||
zigGlobalObject->m_moduleWrapperStart,
|
||||
wrappedString.substring(trimStart, wrappedString.length() - trimStart - 4),
|
||||
zigGlobalObject->m_moduleWrapperEnd);
|
||||
}
|
||||
}
|
||||
|
||||
moduleObject->sourceCode = makeSource(
|
||||
WTF::move(wrappedString),
|
||||
SourceOrigin(URL::fileURLWithFileSystemPath(filenameString)),
|
||||
JSC::SourceTaintedOrigin::Untainted,
|
||||
filenameString,
|
||||
WTF::TextPosition(),
|
||||
JSC::SourceProviderSourceType::Program);
|
||||
|
||||
EncodedJSValue encodedFilename = JSValue::encode(filenameValue);
|
||||
#if OS(WINDOWS)
|
||||
JSValue dirnameValue = JSValue::decode(Bun__Path__dirname(globalObject, true, &encodedFilename, 1));
|
||||
#else
|
||||
JSValue dirnameValue = JSValue::decode(Bun__Path__dirname(globalObject, false, &encodedFilename, 1));
|
||||
#endif
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
String dirnameString = dirnameValue.toWTFString(globalObject);
|
||||
|
||||
evaluateCommonJSModuleOnce(
|
||||
vm,
|
||||
zigGlobalObject,
|
||||
moduleObject,
|
||||
jsString(vm, dirnameString),
|
||||
jsString(vm, filenameString));
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
} else {
|
||||
// The source contains ESM syntax (import/export). The transpiler kept it as ESM
|
||||
// (with TS/JSX stripped). Provide the transpiled source to JSC's module loader
|
||||
// and evaluate it as ESM, then populate the CJS module's exports.
|
||||
auto provider = Zig::SourceProvider::create(zigGlobalObject, source, JSC::SourceProviderSourceType::Module, false);
|
||||
zigGlobalObject->moduleLoader()->provideFetch(globalObject, filenameValue, JSC::SourceCode(WTF::move(provider)));
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
// Use requireESMFromHijackedExtension to synchronously evaluate the ESM module
|
||||
// and populate the CJS module's exports from the ESM namespace.
|
||||
JSC::JSFunction* requireESM = zigGlobalObject->requireESMFromHijackedExtension();
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
args.append(filenameValue);
|
||||
JSC::CallData callData = JSC::getCallData(requireESM);
|
||||
NakedPtr<JSC::Exception> returnedException = nullptr;
|
||||
JSC::profiledCall(globalObject, JSC::ProfilingReason::API, requireESM, callData, moduleObject, args, returnedException);
|
||||
if (returnedException) [[unlikely]] {
|
||||
throwException(globalObject, throwScope, returnedException->value());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
WTF::NakedPtr<JSC::Exception> exception;
|
||||
evaluateCommonJSModuleOnce(
|
||||
vm,
|
||||
jsCast<Zig::GlobalObject*>(globalObject),
|
||||
moduleObject,
|
||||
jsString(vm, dirnameString),
|
||||
jsString(vm, filenameString));
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
@@ -86,7 +86,75 @@ function getHeapStatistics() {
|
||||
};
|
||||
}
|
||||
function getHeapSpaceStatistics() {
|
||||
notimpl("getHeapSpaceStatistics");
|
||||
const stats = jsc.heapStats();
|
||||
const memory = jsc.memoryUsage();
|
||||
const total = totalmem();
|
||||
|
||||
// JSC doesn't have the same heap space breakdown as V8, but we provide
|
||||
// compatible structure with reasonable values based on JSC's memory model.
|
||||
// This allows applications that depend on this API to work.
|
||||
const heapSize = stats.heapSize;
|
||||
const heapCapacity = stats.heapCapacity;
|
||||
const extraMemory = stats.extraMemorySize;
|
||||
|
||||
return [
|
||||
{
|
||||
space_name: "read_only_space",
|
||||
space_size: 0,
|
||||
space_used_size: 0,
|
||||
space_available_size: 0,
|
||||
physical_space_size: 0,
|
||||
},
|
||||
{
|
||||
space_name: "new_space",
|
||||
space_size: Math.floor(heapCapacity * 0.2),
|
||||
space_used_size: Math.floor(heapSize * 0.2),
|
||||
space_available_size: Math.floor((heapCapacity - heapSize) * 0.2),
|
||||
physical_space_size: Math.floor(heapCapacity * 0.2),
|
||||
},
|
||||
{
|
||||
space_name: "old_space",
|
||||
space_size: Math.floor(heapCapacity * 0.6),
|
||||
space_used_size: Math.floor(heapSize * 0.6),
|
||||
space_available_size: Math.floor((heapCapacity - heapSize) * 0.6),
|
||||
physical_space_size: Math.floor(heapCapacity * 0.6),
|
||||
},
|
||||
{
|
||||
space_name: "code_space",
|
||||
space_size: Math.floor(heapCapacity * 0.1),
|
||||
space_used_size: Math.floor(heapSize * 0.1),
|
||||
space_available_size: Math.floor((heapCapacity - heapSize) * 0.1),
|
||||
physical_space_size: Math.floor(heapCapacity * 0.1),
|
||||
},
|
||||
{
|
||||
space_name: "shared_space",
|
||||
space_size: 0,
|
||||
space_used_size: 0,
|
||||
space_available_size: 0,
|
||||
physical_space_size: 0,
|
||||
},
|
||||
{
|
||||
space_name: "large_object_space",
|
||||
space_size: extraMemory,
|
||||
space_used_size: extraMemory,
|
||||
space_available_size: 0,
|
||||
physical_space_size: extraMemory,
|
||||
},
|
||||
{
|
||||
space_name: "code_large_object_space",
|
||||
space_size: 0,
|
||||
space_used_size: 0,
|
||||
space_available_size: 0,
|
||||
physical_space_size: 0,
|
||||
},
|
||||
{
|
||||
space_name: "new_large_object_space",
|
||||
space_size: 0,
|
||||
space_used_size: 0,
|
||||
space_available_size: Math.floor(total * 0.01),
|
||||
physical_space_size: 0,
|
||||
},
|
||||
];
|
||||
}
|
||||
function getHeapCodeStatistics() {
|
||||
notimpl("getHeapCodeStatistics");
|
||||
|
||||
@@ -512,6 +512,124 @@ describe("@types/bun integration test", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("core-js type compatibility", () => {
|
||||
// Tests that bun-types are compatible with core-js type patterns.
|
||||
// core-js defines ponyfill constructors that extend global built-in
|
||||
// interfaces, and these will fail with TS2430 if bun-types deviates
|
||||
// from the spec signatures.
|
||||
typeTest("bun-types signatures are compatible with core-js extends pattern", {
|
||||
files: {
|
||||
"core-js-extends-check.ts": `
|
||||
// The resolve parameter must be non-optional (required) per the spec.
|
||||
// core-js's CoreJSPromiseConstructor extends PromiseConstructor with
|
||||
// this signature — if bun-types makes resolve optional, this fails with TS2430.
|
||||
interface StrictPromiseWithResolvers<T> {
|
||||
promise: Promise<T>;
|
||||
resolve: (value: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
}
|
||||
interface StrictPromiseConstructor extends PromiseConstructor {
|
||||
withResolvers<T>(): StrictPromiseWithResolvers<T>;
|
||||
}
|
||||
|
||||
// ArrayBuffer.resize must return void per the spec.
|
||||
// If bun-types returns ArrayBuffer instead, this fails with TS2430.
|
||||
interface StrictArrayBuffer extends ArrayBuffer {
|
||||
resize(newByteLength?: number): void;
|
||||
}
|
||||
`,
|
||||
},
|
||||
emptyInterfaces: expectedEmptyInterfacesWhenNoDOM,
|
||||
diagnostics: diagnostics => {
|
||||
const relevantDiagnostics = diagnostics.filter(d => d.line?.startsWith("core-js-extends-check.ts"));
|
||||
expect(relevantDiagnostics).toEqual([]);
|
||||
},
|
||||
});
|
||||
|
||||
// Intentionally fetches type definitions from the upstream core-js v4-types
|
||||
// branch at test time rather than vendoring them, so we always test against
|
||||
// the latest core-js types and catch new incompatibilities early.
|
||||
// https://github.com/zloirock/core-js/tree/v4-types/packages/core-js-types
|
||||
const CORE_JS_TYPES_TREE_API = "https://api.github.com/repos/zloirock/core-js/git/trees/v4-types?recursive=1";
|
||||
const CORE_JS_TYPES_RAW_BASE = "https://raw.githubusercontent.com/zloirock/core-js/v4-types";
|
||||
const CORE_JS_TYPES_PREFIX = "packages/core-js-types/src/base/";
|
||||
|
||||
async function fetchWithRetry(url: string, retries = 3): Promise<Response> {
|
||||
for (let i = 0; i < retries; i++) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (response.ok) return response;
|
||||
if (i === retries - 1) throw new Error(`Failed to fetch ${url}: ${response.status}`);
|
||||
} catch (error) {
|
||||
if (i === retries - 1) throw error;
|
||||
}
|
||||
await Bun.sleep(1000 * (i + 1));
|
||||
}
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
test("no conflicts with upstream core-js-types", async () => {
|
||||
// Discover all non-pure .d.ts files from the core-js-types package
|
||||
const treeResponse = await fetchWithRetry(CORE_JS_TYPES_TREE_API);
|
||||
const tree: { tree: { path: string; type: string }[] } = await treeResponse.json();
|
||||
|
||||
const typesFiles = tree.tree
|
||||
.filter(
|
||||
entry =>
|
||||
entry.type === "blob" &&
|
||||
entry.path.startsWith(CORE_JS_TYPES_PREFIX) &&
|
||||
entry.path.endsWith(".d.ts") &&
|
||||
!entry.path.includes("/pure/"),
|
||||
)
|
||||
.map(entry => entry.path.slice(CORE_JS_TYPES_PREFIX.length));
|
||||
|
||||
if (typesFiles.length === 0) throw new Error("No core-js type files found — API may have changed");
|
||||
|
||||
// Fetch all files in parallel
|
||||
const files: Record<string, string> = {};
|
||||
await Promise.all(
|
||||
typesFiles.map(async file => {
|
||||
const response = await fetchWithRetry(`${CORE_JS_TYPES_RAW_BASE}/${CORE_JS_TYPES_PREFIX}${file}`);
|
||||
files[`core-js-types/${file}`] = await response.text();
|
||||
}),
|
||||
);
|
||||
|
||||
files["core-js-compat.ts"] =
|
||||
typesFiles.map(file => `/// <reference path="core-js-types/${file}" />`).join("\n") +
|
||||
`
|
||||
// Verify usage works with both bun-types and core-js-types loaded
|
||||
const buf = new ArrayBuffer(1024, { maxByteLength: 2048 });
|
||||
buf.resize(2048);
|
||||
|
||||
const { promise, resolve, reject } = Promise.withResolvers<string>();
|
||||
resolve("hello");
|
||||
`;
|
||||
|
||||
const fixtureDir = await createIsolatedFixture();
|
||||
const { diagnostics, emptyInterfaces } = await diagnose(fixtureDir, { files });
|
||||
|
||||
// core-js declares some DOM interfaces (Element, Node, etc.) for
|
||||
// iterable-dom-collections — these are empty without lib.dom.d.ts.
|
||||
// Just verify we're a superset of the base expected empty interfaces.
|
||||
for (const name of expectedEmptyInterfacesWhenNoDOM) {
|
||||
expect(emptyInterfaces).toContain(name);
|
||||
}
|
||||
|
||||
// Filter out core-js internal issues (missing cross-references, circular types)
|
||||
// that aren't caused by bun-types incompatibility.
|
||||
const ignoredCodes = new Set([
|
||||
2688, // "Cannot find type definition file" — core-js cross-references between its own files
|
||||
2502, // "referenced directly or indirectly in its own type annotation" — circular refs in core-js
|
||||
]);
|
||||
const relevantDiagnostics = diagnostics.filter(
|
||||
d =>
|
||||
!ignoredCodes.has(d.code) &&
|
||||
(d.line === null || d.line.startsWith("core-js-compat.ts") || d.line.startsWith("core-js-types/")),
|
||||
);
|
||||
expect(relevantDiagnostics).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("lib configuration", () => {
|
||||
typeTest("checks with no lib at all", {
|
||||
options: {
|
||||
|
||||
@@ -13,3 +13,10 @@ const buf = new SharedArrayBuffer(1024);
|
||||
buf.grow(2048);
|
||||
|
||||
expectType(buffer[Symbol.toStringTag]).extends<string>();
|
||||
|
||||
// ArrayBuffer.resize() should return void per the ECMAScript spec
|
||||
expectType(buffer.resize(2048)).is<void>();
|
||||
|
||||
// Promise.withResolvers resolve parameter should be non-optional
|
||||
const { resolve } = Promise.withResolvers<string>();
|
||||
expectType(resolve).is<(value: string | PromiseLike<string>) => void>();
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
test("Module._compile handles ESM import/export syntax", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
const Module = require('module');
|
||||
|
||||
const source = \`
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const config = {
|
||||
value: 42,
|
||||
};
|
||||
|
||||
export default config;
|
||||
\`;
|
||||
|
||||
const m = new Module('/tmp/test-26874.ts');
|
||||
m.filename = '/tmp/test-26874.ts';
|
||||
m.paths = Module._nodeModulePaths('/tmp/');
|
||||
m._compile(source, '/tmp/test-26874.ts');
|
||||
console.log(JSON.stringify(m.exports.default));
|
||||
`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout.trim()).toBe('{"value":42}');
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("Module._compile handles TypeScript syntax", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
const Module = require('module');
|
||||
|
||||
const source = \`
|
||||
interface Config {
|
||||
value: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const config: Config = {
|
||||
value: 123,
|
||||
name: "test",
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
\`;
|
||||
|
||||
const m = new Module('/tmp/test-26874-ts.ts');
|
||||
m.filename = '/tmp/test-26874-ts.ts';
|
||||
m.paths = Module._nodeModulePaths('/tmp/');
|
||||
m._compile(source, '/tmp/test-26874-ts.ts');
|
||||
console.log(JSON.stringify(m.exports));
|
||||
`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout.trim()).toBe('{"value":123,"name":"test"}');
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("Module._compile still works with plain CJS source", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
const Module = require('module');
|
||||
|
||||
const source = \`
|
||||
const x = 10;
|
||||
const y = 20;
|
||||
module.exports = { sum: x + y };
|
||||
\`;
|
||||
|
||||
const m = new Module('/tmp/test-26874-cjs.js');
|
||||
m.filename = '/tmp/test-26874-cjs.js';
|
||||
m.paths = Module._nodeModulePaths('/tmp/');
|
||||
m._compile(source, '/tmp/test-26874-cjs.js');
|
||||
console.log(JSON.stringify(m.exports));
|
||||
`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout.trim()).toBe('{"sum":30}');
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("Module._compile handles ESM with .js filename", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
const Module = require('module');
|
||||
|
||||
const source = \`
|
||||
export const hello = "world";
|
||||
export default { greeting: "hi" };
|
||||
\`;
|
||||
|
||||
const m = new Module('/tmp/test-26874-esm.js');
|
||||
m.filename = '/tmp/test-26874-esm.js';
|
||||
m.paths = Module._nodeModulePaths('/tmp/');
|
||||
m._compile(source, '/tmp/test-26874-esm.js');
|
||||
console.log(JSON.stringify(m.exports.default));
|
||||
`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout.trim()).toBe('{"greeting":"hi"}');
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("Module._compile handles ESM TypeScript with imports and exports", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
const Module = require('module');
|
||||
|
||||
const source = \`
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
interface Config {
|
||||
turbopack: { root: string };
|
||||
}
|
||||
|
||||
const config: Config = {
|
||||
turbopack: {
|
||||
root: "/test",
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
\`;
|
||||
|
||||
const m = new Module('/tmp/test-26874-esm-ts.ts');
|
||||
m.filename = '/tmp/test-26874-esm-ts.ts';
|
||||
m.paths = Module._nodeModulePaths('/tmp/');
|
||||
m._compile(source, '/tmp/test-26874-esm-ts.ts');
|
||||
console.log(JSON.stringify(m.exports.default));
|
||||
`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout.trim()).toBe('{"turbopack":{"root":"/test"}}');
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
56
test/v8/heap-space-statistics.test.ts
Normal file
56
test/v8/heap-space-statistics.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import v8 from "node:v8";
|
||||
|
||||
describe("v8.getHeapSpaceStatistics", () => {
|
||||
it("returns an array of heap space objects", () => {
|
||||
const stats = v8.getHeapSpaceStatistics();
|
||||
|
||||
expect(Array.isArray(stats)).toBe(true);
|
||||
expect(stats.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("each entry has the required properties", () => {
|
||||
const stats = v8.getHeapSpaceStatistics();
|
||||
|
||||
for (const space of stats) {
|
||||
expect(typeof space.space_name).toBe("string");
|
||||
expect(typeof space.space_size).toBe("number");
|
||||
expect(typeof space.space_used_size).toBe("number");
|
||||
expect(typeof space.space_available_size).toBe("number");
|
||||
expect(typeof space.physical_space_size).toBe("number");
|
||||
}
|
||||
});
|
||||
|
||||
it("includes expected heap space names", () => {
|
||||
const stats = v8.getHeapSpaceStatistics();
|
||||
const spaceNames = stats.map(s => s.space_name);
|
||||
|
||||
// Check for common V8 heap space names
|
||||
expect(spaceNames).toContain("new_space");
|
||||
expect(spaceNames).toContain("old_space");
|
||||
expect(spaceNames).toContain("code_space");
|
||||
expect(spaceNames).toContain("large_object_space");
|
||||
});
|
||||
|
||||
it("returns non-negative numeric values", () => {
|
||||
const stats = v8.getHeapSpaceStatistics();
|
||||
|
||||
for (const space of stats) {
|
||||
expect(space.space_size).toBeGreaterThanOrEqual(0);
|
||||
expect(space.space_used_size).toBeGreaterThanOrEqual(0);
|
||||
expect(space.space_available_size).toBeGreaterThanOrEqual(0);
|
||||
expect(space.physical_space_size).toBeGreaterThanOrEqual(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("space_used_size does not exceed space_size for non-empty spaces", () => {
|
||||
const stats = v8.getHeapSpaceStatistics();
|
||||
|
||||
for (const space of stats) {
|
||||
// Only check spaces that have a non-zero size
|
||||
if (space.space_size > 0) {
|
||||
expect(space.space_used_size).toBeLessThanOrEqual(space.space_size);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user