mirror of
https://github.com/oven-sh/bun
synced 2026-02-19 07:12:24 +00:00
Compare commits
2 Commits
claude/fix
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
053637d55f | ||
|
|
b746919e38 |
@@ -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());
|
||||
}
|
||||
|
||||
@@ -10,8 +10,56 @@ using namespace JSC;
|
||||
extern "C" SYSV_ABI void* JSDOMFile__construct(JSC::JSGlobalObject*, JSC::CallFrame* callframe);
|
||||
extern "C" SYSV_ABI bool JSDOMFile__hasInstance(EncodedJSValue, JSC::JSGlobalObject*, EncodedJSValue);
|
||||
|
||||
// TODO: make this inehrit from JSBlob instead of InternalFunction
|
||||
// That will let us remove this hack for [Symbol.hasInstance] and fix the prototype chain.
|
||||
// File.prototype inherits from Blob.prototype per the spec.
|
||||
// This gives File instances all Blob methods while having a distinct prototype
|
||||
// with constructor === File and [Symbol.toStringTag] === "File".
|
||||
class JSDOMFilePrototype final : public JSC::JSNonFinalObject {
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
public:
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSDOMFilePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSDOMFilePrototype* prototype = new (NotNull, JSC::allocateCell<JSDOMFilePrototype>(vm)) JSDOMFilePrototype(vm, structure);
|
||||
prototype->finishCreation(vm, globalObject);
|
||||
return prototype;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDOMFilePrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
protected:
|
||||
JSDOMFilePrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
// Set [Symbol.toStringTag] = "File" so Object.prototype.toString.call(file) === "[object File]"
|
||||
this->putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol,
|
||||
jsNontrivialString(vm, "File"_s),
|
||||
JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly);
|
||||
}
|
||||
};
|
||||
|
||||
const JSC::ClassInfo JSDOMFilePrototype::s_info = { "File"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFilePrototype) };
|
||||
|
||||
class JSDOMFile : public JSC::InternalFunction {
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
@@ -40,15 +88,20 @@ public:
|
||||
Base::finishCreation(vm, 2, "File"_s);
|
||||
}
|
||||
|
||||
static JSDOMFile* create(JSC::VM& vm, JSGlobalObject* globalObject)
|
||||
static JSDOMFile* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::JSObject* filePrototype)
|
||||
{
|
||||
auto* zigGlobal = defaultGlobalObject(globalObject);
|
||||
auto structure = createStructure(vm, globalObject, zigGlobal->functionPrototype());
|
||||
auto* object = new (NotNull, JSC::allocateCell<JSDOMFile>(vm)) JSDOMFile(vm, structure);
|
||||
object->finishCreation(vm);
|
||||
|
||||
// This is not quite right. But we'll fix it if someone files an issue about it.
|
||||
object->putDirect(vm, vm.propertyNames->prototype, zigGlobal->JSBlobPrototype(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | 0);
|
||||
// Set File.prototype to the distinct FilePrototype object (which inherits from Blob.prototype).
|
||||
object->putDirect(vm, vm.propertyNames->prototype, filePrototype,
|
||||
JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
|
||||
|
||||
// Set FilePrototype.constructor = File
|
||||
filePrototype->putDirect(vm, vm.propertyNames->constructor, object,
|
||||
static_cast<unsigned>(JSC::PropertyAttribute::DontEnum));
|
||||
|
||||
return object;
|
||||
}
|
||||
@@ -69,7 +122,7 @@ public:
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
JSObject* newTarget = asObject(callFrame->newTarget());
|
||||
auto* constructor = globalObject->JSDOMFileConstructor();
|
||||
Structure* structure = globalObject->JSBlobStructure();
|
||||
Structure* structure = globalObject->JSFileStructure();
|
||||
if (constructor != newTarget) {
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
@@ -77,7 +130,7 @@ public:
|
||||
// ShadowRealm functions belong to a different global object.
|
||||
getFunctionRealm(lexicalGlobalObject, newTarget));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
structure = InternalFunction::createSubclassStructure(lexicalGlobalObject, newTarget, functionGlobalObject->JSBlobStructure());
|
||||
structure = InternalFunction::createSubclassStructure(lexicalGlobalObject, newTarget, functionGlobalObject->JSFileStructure());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
@@ -103,9 +156,30 @@ const JSC::ClassInfo JSDOMFile::s_info = { "File"_s, &Base::s_info, nullptr, nul
|
||||
|
||||
namespace Bun {
|
||||
|
||||
JSC::Structure* createJSFileStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
auto* zigGlobal = defaultGlobalObject(globalObject);
|
||||
JSC::JSObject* blobPrototype = zigGlobal->JSBlobPrototype();
|
||||
|
||||
// Create FilePrototype with [[Prototype]] = Blob.prototype
|
||||
auto* protoStructure = JSDOMFilePrototype::createStructure(vm, globalObject, blobPrototype);
|
||||
auto* filePrototype = JSDOMFilePrototype::create(vm, globalObject, protoStructure);
|
||||
|
||||
// Create the structure for File instances: [[Prototype]] = FilePrototype
|
||||
return JSC::Structure::create(vm, globalObject, filePrototype,
|
||||
JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), WebCore::JSBlob::StructureFlags),
|
||||
WebCore::JSBlob::info(), NonArray);
|
||||
}
|
||||
|
||||
JSC::JSObject* createJSDOMFileConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
return JSDOMFile::create(vm, globalObject);
|
||||
auto* zigGlobal = defaultGlobalObject(globalObject);
|
||||
|
||||
// Get the File instance structure - its prototype is the FilePrototype we need
|
||||
auto* fileStructure = zigGlobal->JSFileStructure();
|
||||
auto* filePrototype = fileStructure->storedPrototypeObject();
|
||||
|
||||
return JSDOMFile::create(vm, globalObject, filePrototype);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
|
||||
namespace Bun {
|
||||
JSC::JSObject* createJSDOMFileConstructor(JSC::VM&, JSC::JSGlobalObject*);
|
||||
JSC::Structure* createJSFileStructure(JSC::VM&, JSC::JSGlobalObject*);
|
||||
}
|
||||
|
||||
@@ -1805,6 +1805,11 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
init.set(CustomGetterSetter::create(init.vm, errorInstanceLazyStackCustomGetter, errorInstanceLazyStackCustomSetter));
|
||||
});
|
||||
|
||||
m_JSFileStructure.initLater(
|
||||
[](const Initializer<Structure>& init) {
|
||||
init.set(Bun::createJSFileStructure(init.vm, init.owner));
|
||||
});
|
||||
|
||||
m_JSDOMFileConstructor.initLater(
|
||||
[](const Initializer<JSObject>& init) {
|
||||
JSObject* fileConstructor = Bun::createJSDOMFileConstructor(init.vm, init.owner);
|
||||
|
||||
@@ -610,6 +610,7 @@ public:
|
||||
V(private, LazyPropertyOfGlobalObject<Structure>, m_importMetaBakeObjectStructure) \
|
||||
V(private, LazyPropertyOfGlobalObject<Structure>, m_asyncBoundFunctionStructure) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSC::JSObject>, m_JSDOMFileConstructor) \
|
||||
V(public, LazyPropertyOfGlobalObject<Structure>, m_JSFileStructure) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSC::JSObject>, m_JSMIMEParamsConstructor) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSC::JSObject>, m_JSMIMETypeConstructor) \
|
||||
\
|
||||
@@ -712,6 +713,7 @@ public:
|
||||
|
||||
JSObject* cryptoObject() const { return m_cryptoObject.getInitializedOnMainThread(this); }
|
||||
JSObject* JSDOMFileConstructor() const { return m_JSDOMFileConstructor.getInitializedOnMainThread(this); }
|
||||
JSC::Structure* JSFileStructure() const { return m_JSFileStructure.getInitializedOnMainThread(this); }
|
||||
|
||||
JSMap* nodeWorkerEnvironmentData() { return m_nodeWorkerEnvironmentData.get(); }
|
||||
void setNodeWorkerEnvironmentData(JSMap* data);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
67
test/regression/issue/26899.test.ts
Normal file
67
test/regression/issue/26899.test.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/26899
|
||||
// File.prototype should be distinct from Blob.prototype
|
||||
|
||||
test("File.prototype !== Blob.prototype", () => {
|
||||
expect(File.prototype).not.toBe(Blob.prototype);
|
||||
});
|
||||
|
||||
test("File.prototype inherits from Blob.prototype", () => {
|
||||
expect(Object.getPrototypeOf(File.prototype)).toBe(Blob.prototype);
|
||||
});
|
||||
|
||||
test("new File(...).constructor.name === 'File'", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(file.constructor.name).toBe("File");
|
||||
});
|
||||
|
||||
test("new File(...).constructor === File", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(file.constructor).toBe(File);
|
||||
});
|
||||
|
||||
test("new File(...).constructor !== Blob", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(file.constructor).not.toBe(Blob);
|
||||
});
|
||||
|
||||
test("Object.prototype.toString.call(file) === '[object File]'", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(Object.prototype.toString.call(file)).toBe("[object File]");
|
||||
});
|
||||
|
||||
test("file instanceof File", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(file instanceof File).toBe(true);
|
||||
});
|
||||
|
||||
test("file instanceof Blob", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(file instanceof Blob).toBe(true);
|
||||
});
|
||||
|
||||
test("blob is not instanceof File", () => {
|
||||
const blob = new Blob(["hello"]);
|
||||
expect(blob instanceof File).toBe(false);
|
||||
});
|
||||
|
||||
test("File instances have Blob methods", () => {
|
||||
const file = new File(["hello"], "hello.txt");
|
||||
expect(typeof file.text).toBe("function");
|
||||
expect(typeof file.arrayBuffer).toBe("function");
|
||||
expect(typeof file.slice).toBe("function");
|
||||
expect(typeof file.stream).toBe("function");
|
||||
});
|
||||
|
||||
test("File name and lastModified work", () => {
|
||||
const file = new File(["hello"], "hello.txt", { lastModified: 12345 });
|
||||
expect(file.name).toBe("hello.txt");
|
||||
expect(file.lastModified).toBe(12345);
|
||||
});
|
||||
|
||||
test("File.prototype has correct Symbol.toStringTag", () => {
|
||||
const desc = Object.getOwnPropertyDescriptor(File.prototype, Symbol.toStringTag);
|
||||
expect(desc).toBeDefined();
|
||||
expect(desc!.value).toBe("File");
|
||||
});
|
||||
Reference in New Issue
Block a user