mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 23:18:47 +00:00
Compare commits
8 Commits
dylan/test
...
jarred/mod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82b1d3fb65 | ||
|
|
6265ac2307 | ||
|
|
af131cfe77 | ||
|
|
15d268f8da | ||
|
|
2bd296bc90 | ||
|
|
39ce95c0d0 | ||
|
|
3a0d7fc3f0 | ||
|
|
b05adf1e35 |
2
.github/workflows/bun-linux-aarch64.yml
vendored
2
.github/workflows/bun-linux-aarch64.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
arch: aarch64
|
||||
build_arch: arm64
|
||||
runner: linux-arm64
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-linux-arm64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-linux-arm64-lto.tar.gz"
|
||||
webkit_basename: "bun-webkit-linux-arm64-lto"
|
||||
build_machine_arch: aarch64
|
||||
|
||||
|
||||
4
.github/workflows/bun-linux-build.yml
vendored
4
.github/workflows/bun-linux-build.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
arch: x86_64
|
||||
build_arch: amd64
|
||||
runner: big-ubuntu
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-linux-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-linux-amd64-lto.tar.gz"
|
||||
webkit_basename: "bun-webkit-linux-amd64-lto"
|
||||
build_machine_arch: x86_64
|
||||
- cpu: nehalem
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
arch: x86_64
|
||||
build_arch: amd64
|
||||
runner: big-ubuntu
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-linux-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-linux-amd64-lto.tar.gz"
|
||||
webkit_basename: "bun-webkit-linux-amd64-lto"
|
||||
build_machine_arch: x86_64
|
||||
|
||||
|
||||
16
.github/workflows/bun-mac-aarch64.yml
vendored
16
.github/workflows/bun-mac-aarch64.yml
vendored
@@ -119,7 +119,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64-baseline
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64-baseline
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: true
|
||||
# compile_obj: false
|
||||
# - cpu: haswell
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: true
|
||||
# compile_obj: false
|
||||
# - cpu: nehalem
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64-baseline
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64-baseline
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: false
|
||||
# compile_obj: true
|
||||
# - cpu: haswell
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: false
|
||||
# compile_obj: true
|
||||
- cpu: native
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
tag: bun-darwin-aarch64
|
||||
obj: bun-obj-darwin-aarch64
|
||||
artifact: bun-obj-darwin-aarch64
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
runner: macos-arm64
|
||||
dependencies: true
|
||||
compile_obj: true
|
||||
@@ -260,7 +260,7 @@ jobs:
|
||||
# package: bun-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64-baseline
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# - cpu: haswell
|
||||
# arch: x86_64
|
||||
# tag: bun-darwin-x64
|
||||
@@ -268,14 +268,14 @@ jobs:
|
||||
# package: bun-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
- cpu: native
|
||||
arch: aarch64
|
||||
tag: bun-darwin-aarch64
|
||||
obj: bun-obj-darwin-aarch64
|
||||
package: bun-darwin-aarch64
|
||||
artifact: bun-obj-darwin-aarch64
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
runner: macos-arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
16
.github/workflows/bun-mac-x64-baseline.yml
vendored
16
.github/workflows/bun-mac-x64-baseline.yml
vendored
@@ -119,7 +119,7 @@ jobs:
|
||||
obj: bun-obj-darwin-x64-baseline
|
||||
runner: macos-12
|
||||
artifact: bun-obj-darwin-x64-baseline
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
dependencies: true
|
||||
compile_obj: false
|
||||
# - cpu: haswell
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: true
|
||||
# compile_obj: false
|
||||
- cpu: nehalem
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
obj: bun-obj-darwin-x64-baseline
|
||||
runner: macos-12
|
||||
artifact: bun-obj-darwin-x64-baseline
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
dependencies: false
|
||||
compile_obj: true
|
||||
# - cpu: haswell
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: false
|
||||
# compile_obj: true
|
||||
# - cpu: native
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
# tag: bun-darwin-aarch64
|
||||
# obj: bun-obj-darwin-aarch64
|
||||
# artifact: bun-obj-darwin-aarch64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# runner: macos-arm64
|
||||
# dependencies: true
|
||||
# compile_obj: true
|
||||
@@ -261,7 +261,7 @@ jobs:
|
||||
package: bun-darwin-x64
|
||||
runner: macos-12
|
||||
artifact: bun-obj-darwin-x64-baseline
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# - cpu: haswell
|
||||
# arch: x86_64
|
||||
# tag: bun-darwin-x64
|
||||
@@ -269,14 +269,14 @@ jobs:
|
||||
# package: bun-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# - cpu: native
|
||||
# arch: aarch64
|
||||
# tag: bun-darwin-aarch64
|
||||
# obj: bun-obj-darwin-aarch64
|
||||
# package: bun-darwin-aarch64
|
||||
# artifact: bun-obj-darwin-aarch64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# runner: macos-arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
16
.github/workflows/bun-mac-x64.yml
vendored
16
.github/workflows/bun-mac-x64.yml
vendored
@@ -119,7 +119,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64-baseline
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64-baseline
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: true
|
||||
# compile_obj: false
|
||||
- cpu: haswell
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
obj: bun-obj-darwin-x64
|
||||
runner: macos-12
|
||||
artifact: bun-obj-darwin-x64
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
dependencies: true
|
||||
compile_obj: false
|
||||
# - cpu: nehalem
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
# obj: bun-obj-darwin-x64-baseline
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64-baseline
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# dependencies: false
|
||||
# compile_obj: true
|
||||
- cpu: haswell
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
obj: bun-obj-darwin-x64
|
||||
runner: macos-12
|
||||
artifact: bun-obj-darwin-x64
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
dependencies: false
|
||||
compile_obj: true
|
||||
# - cpu: native
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
# tag: bun-darwin-aarch64
|
||||
# obj: bun-obj-darwin-aarch64
|
||||
# artifact: bun-obj-darwin-aarch64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
# runner: macos-arm64
|
||||
# dependencies: true
|
||||
# compile_obj: true
|
||||
@@ -263,7 +263,7 @@ jobs:
|
||||
# package: bun-darwin-x64
|
||||
# runner: macos-12
|
||||
# artifact: bun-obj-darwin-x64-baseline
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
- cpu: haswell
|
||||
arch: x86_64
|
||||
tag: bun-darwin-x64
|
||||
@@ -271,14 +271,14 @@ jobs:
|
||||
package: bun-darwin-x64
|
||||
runner: macos-12
|
||||
artifact: bun-obj-darwin-x64
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-amd64-lto.tar.gz"
|
||||
# - cpu: native
|
||||
# arch: aarch64
|
||||
# tag: bun-darwin-aarch64
|
||||
# obj: bun-obj-darwin-aarch64
|
||||
# package: bun-darwin-aarch64
|
||||
# artifact: bun-obj-darwin-aarch64
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-2/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-oct3-3/bun-webkit-macos-arm64-lto.tar.gz"
|
||||
# runner: macos-arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -10,7 +10,7 @@ ARG ARCH=x86_64
|
||||
ARG BUILD_MACHINE_ARCH=x86_64
|
||||
ARG TRIPLET=${ARCH}-linux-gnu
|
||||
ARG BUILDARCH=amd64
|
||||
ARG WEBKIT_TAG=2023-oct3-2
|
||||
ARG WEBKIT_TAG=2023-oct3-3
|
||||
ARG ZIG_TAG=jul1
|
||||
ARG ZIG_VERSION="0.12.0-dev.899+027aabf49"
|
||||
ARG WEBKIT_BASENAME="bun-webkit-linux-$BUILDARCH"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@types/react": "^18.0.25",
|
||||
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
||||
"@typescript-eslint/parser": "^5.31.0",
|
||||
"bun-webkit": "0.0.1-1a49a1f94bf42ab4f8c6b11d7bbbb21e491d2d62"
|
||||
"bun-webkit": "0.0.1-b3eb3541118a997adc959e138155f5f4736892da"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
"prettier": "./.prettierrc.cjs"
|
||||
|
||||
32
packages/bun-types/bun-test.d.ts
vendored
32
packages/bun-types/bun-test.d.ts
vendored
@@ -27,6 +27,38 @@ declare module "bun:test" {
|
||||
|
||||
export const mock: {
|
||||
<T extends AnyFunction>(Function: T): Mock<T>;
|
||||
|
||||
/**
|
||||
* Replace the module `id` with the return value of `factory`.
|
||||
*
|
||||
* This is useful for mocking modules.
|
||||
*
|
||||
* @param id module ID to mock
|
||||
* @param factory a function returning an object that will be used as the exports of the mocked module
|
||||
*
|
||||
* @example
|
||||
* ## Example
|
||||
* ```ts
|
||||
* import { mock } from "bun:test";
|
||||
*
|
||||
* mock.module("fs/promises", () => {
|
||||
* return {
|
||||
* readFile: () => Promise.resolve("hello world"),
|
||||
* };
|
||||
* });
|
||||
*
|
||||
* import { readFile } from "fs/promises";
|
||||
*
|
||||
* console.log(await readFile("hello.txt", "utf8")); // hello world
|
||||
* ```
|
||||
*
|
||||
* ## More notes
|
||||
*
|
||||
* If the module is already loaded, exports are overwritten with the return
|
||||
* value of `factory`. If the export didn't exist before, it will not be
|
||||
* added to existing import statements. This is due to how ESM works.
|
||||
*/
|
||||
module(id: string, factory: () => any): void | Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
#include "JavaScriptCore/RegularExpression.h"
|
||||
#include "JavaScriptCore/JSMap.h"
|
||||
#include "JavaScriptCore/JSMapInlines.h"
|
||||
#include "JavaScriptCore/JSModuleRecord.h"
|
||||
#include "JavaScriptCore/JSModuleNamespaceObject.h"
|
||||
#include "JavaScriptCore/SourceOrigin.h"
|
||||
#include "JavaScriptCore/JSModuleLoader.h"
|
||||
#include "CommonJSModuleRecord.h"
|
||||
|
||||
namespace Zig {
|
||||
|
||||
@@ -403,6 +408,277 @@ JSFunction* BunPlugin::Group::find(JSC::JSGlobalObject* globalObject, String& pa
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BunPlugin::OnLoad::addModuleMock(JSC::VM& vm, const String& path, JSC::JSObject* mockObject)
|
||||
{
|
||||
Zig::GlobalObject* globalObject = Zig::jsCast<Zig::GlobalObject*>(mockObject->globalObject());
|
||||
|
||||
if (globalObject->onLoadPlugins.virtualModules == nullptr) {
|
||||
globalObject->onLoadPlugins.virtualModules = new BunPlugin::VirtualModuleMap;
|
||||
}
|
||||
auto* virtualModules = globalObject->onLoadPlugins.virtualModules;
|
||||
|
||||
virtualModules->set(path, JSC::Strong<JSC::JSObject> { vm, mockObject });
|
||||
}
|
||||
|
||||
class JSModuleMock final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
mutable WriteBarrier<JSObject> callbackFunctionOrCachedResult;
|
||||
bool hasCalledModuleMock = false;
|
||||
|
||||
static JSModuleMock* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* callback);
|
||||
static Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype);
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
JSObject* executeOnce(JSC::JSGlobalObject* lexicalGlobalObject);
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<JSModuleMock, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSModuleMock.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSModuleMock = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSModuleMock.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSModuleMock = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSObject* callback);
|
||||
|
||||
private:
|
||||
JSModuleMock(JSC::VM&, JSC::Structure*);
|
||||
};
|
||||
|
||||
const JSC::ClassInfo JSModuleMock::s_info = { "ModuleMock"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSModuleMock) };
|
||||
|
||||
JSModuleMock* JSModuleMock::create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* callback)
|
||||
{
|
||||
JSModuleMock* ptr = new (NotNull, JSC::allocateCell<JSModuleMock>(vm)) JSModuleMock(vm, structure);
|
||||
ptr->finishCreation(vm, callback);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void JSModuleMock::finishCreation(JSC::VM& vm, JSObject* callback)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
callbackFunctionOrCachedResult.set(vm, this, callback);
|
||||
}
|
||||
|
||||
JSModuleMock::JSModuleMock(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
Structure* JSModuleMock::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
JSObject* JSModuleMock::executeOnce(JSC::JSGlobalObject* lexicalGlobalObject)
|
||||
{
|
||||
auto& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (hasCalledModuleMock) {
|
||||
return callbackFunctionOrCachedResult.get();
|
||||
}
|
||||
|
||||
hasCalledModuleMock = true;
|
||||
|
||||
if (!callbackFunctionOrCachedResult) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "Cannot call mock without a callback"_s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSC::JSValue callbackValue = callbackFunctionOrCachedResult.get();
|
||||
if (!callbackValue.isCell() || !callbackValue.isCallable()) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) requires a function"_s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject* callback = callbackValue.getObject();
|
||||
JSC::JSValue result = JSC::call(lexicalGlobalObject, callback, JSC::getCallData(callback), JSC::jsUndefined(), ArgList());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (!result.isObject()) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) requires a function that returns an object"_s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* object = result.getObject();
|
||||
this->callbackFunctionOrCachedResult.set(vm, this, object);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
extern "C" EncodedJSValue JSMock__jsModuleMock(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callframe)
|
||||
{
|
||||
JSC::VM& vm = lexicalGlobalObject->vm();
|
||||
Zig::GlobalObject* globalObject = jsDynamicCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
if (UNLIKELY(!globalObject)) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "Cannot run mock from a different global context"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (callframe->argumentCount() < 1) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) requires a module and function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::JSString* specifierString = callframe->argument(0).toString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
WTF::String specifier = specifierString->value(globalObject);
|
||||
|
||||
if (specifier.isEmpty()) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) requires a module and function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (specifier.startsWith("./"_s) || specifier.startsWith("../"_s) || specifier == "."_s) {
|
||||
JSC::SourceOrigin sourceOrigin = callframe->callerSourceOrigin(vm);
|
||||
const URL& url = sourceOrigin.url();
|
||||
if (url.protocolIsFile()) {
|
||||
URL joinedURL = URL(url, specifier);
|
||||
specifier = joinedURL.fileSystemPath();
|
||||
specifierString = jsString(vm, specifier);
|
||||
} else {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) cannot mock relative paths in non-files"_s));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
JSC::JSValue callbackValue = callframe->argument(1);
|
||||
if (!callbackValue.isCell() || !callbackValue.isCallable()) {
|
||||
scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) requires a function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::JSObject* callback = callbackValue.getObject();
|
||||
|
||||
JSModuleMock* mock = JSModuleMock::create(vm, globalObject->mockModule.mockModuleStructure.getInitializedOnMainThread(globalObject), callback);
|
||||
|
||||
auto* esm = globalObject->esmRegistryMap();
|
||||
|
||||
auto getJSValue = [&]() -> JSValue {
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
JSValue result = mock->executeOnce(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, JSValue());
|
||||
|
||||
if (result && result.isObject()) {
|
||||
while (JSC::JSPromise* promise = jsDynamicCast<JSC::JSPromise*>(result)) {
|
||||
switch (promise->status(vm)) {
|
||||
case JSC::JSPromise::Status::Rejected: {
|
||||
result = promise->result(vm);
|
||||
scope.throwException(globalObject, result);
|
||||
return {};
|
||||
break;
|
||||
}
|
||||
case JSC::JSPromise::Status::Fulfilled: {
|
||||
result = promise->result(vm);
|
||||
break;
|
||||
}
|
||||
// TODO: blocking wait for promise
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
bool removeFromESM = false;
|
||||
bool removeFromCJS = false;
|
||||
|
||||
if (JSValue entryValue = esm->get(globalObject, specifierString)) {
|
||||
removeFromESM = true;
|
||||
|
||||
if (entryValue.isObject()) {
|
||||
JSObject* entry = entryValue.getObject();
|
||||
if (JSValue moduleValue = entry->getIfPropertyExists(globalObject, Identifier::fromString(vm, String("module"_s)))) {
|
||||
if (auto* mod = jsDynamicCast<JSC::AbstractModuleRecord*>(moduleValue)) {
|
||||
JSC::JSModuleNamespaceObject* moduleNamespaceObject = mod->getModuleNamespace(globalObject);
|
||||
JSValue exportsValue = getJSValue();
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
removeFromESM = false;
|
||||
|
||||
if (exportsValue.isObject()) {
|
||||
// TODO: use fast path for property iteration
|
||||
auto* object = exportsValue.getObject();
|
||||
JSC::PropertyNameArray names(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
|
||||
JSObject::getOwnPropertyNames(object, globalObject, names, DontEnumPropertiesMode::Exclude);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
for (auto& name : names) {
|
||||
// consistent with regular esm handling code
|
||||
auto catchScope = DECLARE_CATCH_SCOPE(vm);
|
||||
JSValue value = object->get(globalObject, name);
|
||||
if (scope.exception()) {
|
||||
scope.clearException();
|
||||
value = jsUndefined();
|
||||
}
|
||||
moduleNamespaceObject->overrideExportValue(globalObject, name, value);
|
||||
}
|
||||
|
||||
} else {
|
||||
// if it's not an object, I guess we just set the default export?
|
||||
moduleNamespaceObject->overrideExportValue(globalObject, vm.propertyNames->defaultKeyword, exportsValue);
|
||||
}
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// TODO: do we need to handle intermediate loading state here?
|
||||
// entry->putDirect(vm, Identifier::fromString(vm, String("evaluated"_s)), jsBoolean(true), 0);
|
||||
// entry->putDirect(vm, Identifier::fromString(vm, String("state"_s)), jsNumber(JSC::JSModuleLoader::Status::Ready), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto entryValue = globalObject->requireMap()->get(globalObject, specifierString)) {
|
||||
removeFromCJS = true;
|
||||
if (auto* moduleObject = jsDynamicCast<Bun::JSCommonJSModule*>(entryValue)) {
|
||||
JSValue exportsValue = getJSValue();
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
moduleObject->putDirect(vm, Bun::builtinNames(vm).exportsPublicName(), exportsValue, 0);
|
||||
moduleObject->hasEvaluated = true;
|
||||
removeFromCJS = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeFromESM) {
|
||||
esm->remove(globalObject, specifierString);
|
||||
}
|
||||
|
||||
if (removeFromCJS) {
|
||||
globalObject->requireMap()->remove(globalObject, specifierString);
|
||||
}
|
||||
|
||||
globalObject->onLoadPlugins.addModuleMock(vm, specifier, mock);
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSModuleMock::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
JSModuleMock* mock = jsCast<JSModuleMock*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(mock, info());
|
||||
Base::visitChildren(mock, visitor);
|
||||
|
||||
visitor.append(mock->callbackFunctionOrCachedResult);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSModuleMock);
|
||||
|
||||
EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path)
|
||||
{
|
||||
Group* groupPtr = this->group(namespaceString ? Bun::toWTFString(*namespaceString) : String());
|
||||
@@ -559,7 +835,13 @@ extern "C" JSC::EncodedJSValue Bun__runOnLoadPlugins(Zig::GlobalObject* globalOb
|
||||
}
|
||||
|
||||
namespace Bun {
|
||||
JSC::JSValue runVirtualModule(Zig::GlobalObject* globalObject, BunString* specifier)
|
||||
|
||||
Structure* createModuleMockStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return Zig::JSModuleMock::createStructure(vm, globalObject, prototype);
|
||||
}
|
||||
|
||||
JSC::JSValue runVirtualModule(Zig::GlobalObject* globalObject, BunString* specifier, bool& wasModuleMock)
|
||||
{
|
||||
auto fallback = [&]() -> JSC::JSValue {
|
||||
return JSValue::decode(Bun__runVirtualModule(globalObject, specifier));
|
||||
@@ -570,16 +852,27 @@ JSC::JSValue runVirtualModule(Zig::GlobalObject* globalObject, BunString* specif
|
||||
}
|
||||
auto& virtualModules = *globalObject->onLoadPlugins.virtualModules;
|
||||
WTF::String specifierString = Bun::toWTFString(*specifier);
|
||||
|
||||
if (auto virtualModuleFn = virtualModules.get(specifierString)) {
|
||||
auto& vm = globalObject->vm();
|
||||
JSC::JSObject* function = virtualModuleFn.get();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::MarkedArgumentBuffer arguments;
|
||||
JSC::CallData callData = JSC::getCallData(function);
|
||||
RELEASE_ASSERT(callData.type != JSC::CallData::Type::None);
|
||||
JSValue result;
|
||||
|
||||
if (Zig::JSModuleMock* moduleMock = jsDynamicCast<Zig::JSModuleMock*>(function)) {
|
||||
wasModuleMock = true;
|
||||
// module mock
|
||||
result = moduleMock->executeOnce(globalObject);
|
||||
} else {
|
||||
// regular function
|
||||
JSC::MarkedArgumentBuffer arguments;
|
||||
JSC::CallData callData = JSC::getCallData(function);
|
||||
RELEASE_ASSERT(callData.type != JSC::CallData::Type::None);
|
||||
|
||||
result = call(globalObject, function, callData, JSC::jsUndefined(), arguments);
|
||||
}
|
||||
|
||||
auto result = call(globalObject, function, callData, JSC::jsUndefined(), arguments);
|
||||
RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
|
||||
|
||||
if (auto* promise = JSC::jsDynamicCast<JSPromise*>(result)) {
|
||||
|
||||
@@ -72,6 +72,8 @@ public:
|
||||
VirtualModuleMap* virtualModules = nullptr;
|
||||
JSC::EncodedJSValue run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path);
|
||||
|
||||
void addModuleMock(JSC::VM& vm, const String& path, JSC::JSObject* mock);
|
||||
|
||||
~OnLoad()
|
||||
{
|
||||
if (virtualModules) {
|
||||
@@ -97,5 +99,6 @@ class GlobalObject;
|
||||
} // namespace Zig
|
||||
|
||||
namespace Bun {
|
||||
JSC::JSValue runVirtualModule(Zig::GlobalObject*, BunString* specifier);
|
||||
JSC::JSValue runVirtualModule(Zig::GlobalObject*, BunString* specifier, bool& wasModuleMock);
|
||||
JSC::Structure* createModuleMockStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype);
|
||||
}
|
||||
@@ -645,14 +645,15 @@ bool JSCommonJSModule::evaluate(
|
||||
RELEASE_AND_RETURN(throwScope, true);
|
||||
}
|
||||
|
||||
void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject,
|
||||
JSC::Identifier moduleKey,
|
||||
void populateESMExports(
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSValue result,
|
||||
Vector<JSC::Identifier, 4>& exportNames,
|
||||
JSC::MarkedArgumentBuffer& exportValues)
|
||||
JSC::MarkedArgumentBuffer& exportValues,
|
||||
bool ignoreESModuleAnnotation)
|
||||
{
|
||||
auto result = this->exportsObject();
|
||||
|
||||
auto& vm = globalObject->vm();
|
||||
Identifier esModuleMarker = builtinNames(vm).__esModulePublicName();
|
||||
|
||||
// Bun's intepretation of the "__esModule" annotation:
|
||||
//
|
||||
@@ -686,6 +687,7 @@ void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject,
|
||||
|
||||
if (result.isObject()) {
|
||||
auto* exports = result.getObject();
|
||||
bool hasESModuleMarker = !ignoreESModuleAnnotation && exports->hasProperty(globalObject, esModuleMarker);
|
||||
|
||||
auto* structure = exports->structure();
|
||||
uint32_t size = structure->inlineSize() + structure->outOfLineSize();
|
||||
@@ -694,8 +696,6 @@ void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject,
|
||||
|
||||
auto catchScope = DECLARE_CATCH_SCOPE(vm);
|
||||
|
||||
Identifier esModuleMarker = builtinNames(vm).__esModulePublicName();
|
||||
bool hasESModuleMarker = !this->ignoreESModuleAnnotation && exports->hasProperty(globalObject, esModuleMarker);
|
||||
if (catchScope.exception()) {
|
||||
catchScope.clearException();
|
||||
}
|
||||
@@ -805,6 +805,18 @@ void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject,
|
||||
}
|
||||
}
|
||||
|
||||
void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject,
|
||||
JSC::Identifier moduleKey,
|
||||
Vector<JSC::Identifier, 4>& exportNames,
|
||||
JSC::MarkedArgumentBuffer& exportValues)
|
||||
{
|
||||
auto result = this->exportsObject();
|
||||
|
||||
auto& vm = globalObject->vm();
|
||||
Identifier esModuleMarker = builtinNames(vm).__esModulePublicName();
|
||||
populateESMExports(globalObject, result, exportNames, exportValues, this->ignoreESModuleAnnotation);
|
||||
}
|
||||
|
||||
JSValue JSCommonJSModule::exportsObject()
|
||||
{
|
||||
return this->get(globalObject(), JSC::PropertyName(clientData(vm())->builtinNames().exportsPublicName()));
|
||||
|
||||
@@ -17,6 +17,13 @@ namespace Bun {
|
||||
JSC_DECLARE_HOST_FUNCTION(jsFunctionCreateCommonJSModule);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsFunctionLoadModule);
|
||||
|
||||
void populateESMExports(
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSC::JSValue result,
|
||||
WTF::Vector<JSC::Identifier, 4>& exportNames,
|
||||
JSC::MarkedArgumentBuffer& exportValues,
|
||||
bool ignoreESModuleAnnotation);
|
||||
|
||||
class JSCommonJSModule final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
#include <JavaScriptCore/WeakMapImplInlines.h>
|
||||
#include <JavaScriptCore/FunctionPrototype.h>
|
||||
#include <JavaScriptCore/DateInstance.h>
|
||||
|
||||
#include <JavaScriptCore/JSModuleEnvironment.h>
|
||||
#include <JavaScriptCore/JSModuleNamespaceObject.h>
|
||||
#include "BunPlugin.h"
|
||||
namespace Bun {
|
||||
|
||||
/**
|
||||
@@ -606,7 +608,13 @@ extern "C" EncodedJSValue JSMock__jsSpyOn(JSC::JSGlobalObject* lexicalGlobalObje
|
||||
if (!hasValue || slot.isValue()) {
|
||||
JSValue value = jsUndefined();
|
||||
if (hasValue) {
|
||||
value = slot.getValue(globalObject, propertyKey);
|
||||
if (UNLIKELY(slot.isTaintedByOpaqueObject())) {
|
||||
// if it's a Proxy or JSModuleNamespaceObject
|
||||
value = object->get(globalObject, propertyKey);
|
||||
} else {
|
||||
value = slot.getValue(globalObject, propertyKey);
|
||||
}
|
||||
|
||||
if (jsDynamicCast<JSMockFunction*>(value)) {
|
||||
return JSValue::encode(value);
|
||||
}
|
||||
@@ -624,7 +632,12 @@ extern "C" EncodedJSValue JSMock__jsSpyOn(JSC::JSGlobalObject* lexicalGlobalObje
|
||||
|
||||
mock->copyNameAndLength(vm, globalObject, value);
|
||||
|
||||
object->putDirect(vm, propertyKey, mock, attributes);
|
||||
if (JSModuleNamespaceObject* moduleNamespaceObject = jsDynamicCast<JSModuleNamespaceObject*>(object)) {
|
||||
moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock);
|
||||
} else {
|
||||
object->putDirect(vm, propertyKey, mock, attributes);
|
||||
}
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
pushImpl(mock, globalObject, JSMockImplementation::Kind::Call, value);
|
||||
@@ -633,7 +646,13 @@ extern "C" EncodedJSValue JSMock__jsSpyOn(JSC::JSGlobalObject* lexicalGlobalObje
|
||||
attributes = slot.attributes();
|
||||
|
||||
attributes |= PropertyAttribute::Accessor;
|
||||
object->putDirect(vm, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes);
|
||||
|
||||
if (JSModuleNamespaceObject* moduleNamespaceObject = jsDynamicCast<JSModuleNamespaceObject*>(object)) {
|
||||
moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock);
|
||||
} else {
|
||||
object->putDirect(vm, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes);
|
||||
}
|
||||
|
||||
// mock->setName(propertyKey.publicName());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
@@ -696,6 +715,13 @@ JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject)
|
||||
Structure* implementation = ActiveSpySet::createStructure(init.vm, init.owner, jsNull());
|
||||
init.set(implementation);
|
||||
});
|
||||
|
||||
mock.mockModuleStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
|
||||
Structure* implementation = createModuleMockStructure(init.vm, init.owner, jsNull());
|
||||
init.set(implementation);
|
||||
});
|
||||
|
||||
mock.mockImplementationStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
|
||||
Structure* implementation = JSMockImplementation::createStructure(init.vm, init.owner, jsNull());
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
LazyProperty<JSC::JSGlobalObject, Structure> mockResultStructure;
|
||||
LazyProperty<JSC::JSGlobalObject, Structure> mockImplementationStructure;
|
||||
LazyProperty<JSC::JSGlobalObject, Structure> mockObjectStructure;
|
||||
LazyProperty<JSC::JSGlobalObject, Structure> mockModuleStructure;
|
||||
LazyProperty<JSC::JSGlobalObject, Structure> activeSpySetStructure;
|
||||
LazyProperty<JSC::JSGlobalObject, JSFunction> withImplementationCleanupFunction;
|
||||
LazyProperty<JSC::JSGlobalObject, JSC::Structure> mockWithImplementationCleanupDataStructure;
|
||||
|
||||
@@ -43,6 +43,8 @@ using namespace JSC;
|
||||
using namespace Zig;
|
||||
using namespace WebCore;
|
||||
|
||||
static OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, bool wasModuleMock = false);
|
||||
|
||||
extern "C" BunLoaderType Bun__getDefaultLoader(JSC::JSGlobalObject*, BunString* specifier);
|
||||
|
||||
static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
|
||||
@@ -178,7 +180,7 @@ PendingVirtualModuleResult* PendingVirtualModuleResult::create(JSC::JSGlobalObje
|
||||
return virtualModule;
|
||||
}
|
||||
|
||||
OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, BunString* specifier)
|
||||
OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, BunString* specifier, bool wasModuleMock)
|
||||
{
|
||||
OnLoadResult result = {};
|
||||
result.type = OnLoadResultTypeError;
|
||||
@@ -193,9 +195,16 @@ OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::
|
||||
return result;
|
||||
}
|
||||
|
||||
if (wasModuleMock) {
|
||||
result.type = OnLoadResultTypeObject;
|
||||
result.value.object = objectValue;
|
||||
return result;
|
||||
}
|
||||
|
||||
JSC::JSObject* object = objectValue.getObject();
|
||||
if (UNLIKELY(!object)) {
|
||||
scope.throwException(globalObject, JSC::createError(globalObject, "Expected onLoad callback to return an object"_s));
|
||||
scope.throwException(globalObject, JSC::createError(globalObject, "Expected module mock to return an object"_s));
|
||||
|
||||
result.value.error = scope.exception();
|
||||
result.type = OnLoadResultTypeError;
|
||||
return result;
|
||||
@@ -259,16 +268,17 @@ OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::
|
||||
return result;
|
||||
}
|
||||
|
||||
static OnLoadResult handleOnLoadResult(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, BunString* specifier)
|
||||
static OnLoadResult handleOnLoadResult(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, BunString* specifier, bool wasModuleMock = false)
|
||||
{
|
||||
if (JSC::JSPromise* promise = JSC::jsDynamicCast<JSC::JSPromise*>(objectValue)) {
|
||||
OnLoadResult result = {};
|
||||
result.type = OnLoadResultTypePromise;
|
||||
result.value.promise = objectValue;
|
||||
result.wasMock = wasModuleMock;
|
||||
return result;
|
||||
}
|
||||
|
||||
return handleOnLoadResultNotPromise(globalObject, objectValue, specifier);
|
||||
return handleOnLoadResultNotPromise(globalObject, objectValue, specifier, wasModuleMock);
|
||||
}
|
||||
|
||||
template<bool allowPromise>
|
||||
@@ -277,9 +287,10 @@ static JSValue handleVirtualModuleResult(
|
||||
JSValue virtualModuleResult,
|
||||
ErrorableResolvedSource* res,
|
||||
BunString* specifier,
|
||||
BunString* referrer)
|
||||
BunString* referrer,
|
||||
bool wasModuleMock = false)
|
||||
{
|
||||
auto onLoadResult = handleOnLoadResult(globalObject, virtualModuleResult, specifier);
|
||||
auto onLoadResult = handleOnLoadResult(globalObject, virtualModuleResult, specifier, wasModuleMock);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
@@ -409,6 +420,8 @@ extern "C" void Bun__onFulfillAsyncModule(
|
||||
promise->resolve(promise->globalObject(), JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
|
||||
}
|
||||
|
||||
extern "C" bool isBunTest;
|
||||
|
||||
JSValue fetchCommonJSModule(
|
||||
Zig::GlobalObject* globalObject,
|
||||
JSCommonJSModule* target,
|
||||
@@ -424,6 +437,40 @@ JSValue fetchCommonJSModule(
|
||||
|
||||
auto& builtinNames = WebCore::clientData(vm)->builtinNames();
|
||||
|
||||
bool wasModuleMock = false;
|
||||
|
||||
// When "bun test" is enabled, allow users to override builtin modules
|
||||
// This is important for being able to trivially mock things like the filesystem.
|
||||
if (isBunTest) {
|
||||
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
|
||||
JSPromise* promise = jsCast<JSPromise*>(handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock));
|
||||
switch (promise->status(vm)) {
|
||||
case JSPromise::Status::Rejected: {
|
||||
uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt();
|
||||
promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag));
|
||||
JSC::throwException(globalObject, scope, promise->result(vm));
|
||||
RELEASE_AND_RETURN(scope, JSValue {});
|
||||
}
|
||||
case JSPromise::Status::Pending: {
|
||||
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, Bun::toWTFString(*specifier), "\" is unsupported. use \"await import()\" instead."_s));
|
||||
RELEASE_AND_RETURN(scope, JSValue {});
|
||||
}
|
||||
case JSPromise::Status::Fulfilled: {
|
||||
if (!res->success) {
|
||||
throwException(scope, res->result.err, globalObject);
|
||||
RELEASE_AND_RETURN(scope, {});
|
||||
}
|
||||
if (!wasModuleMock) {
|
||||
auto* jsSourceCode = jsCast<JSSourceCode*>(promise->result(vm));
|
||||
globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
RELEASE_AND_RETURN(scope, jsNumber(-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Bun__fetchBuiltinModule(bunVM, globalObject, specifier, referrer, res)) {
|
||||
if (!res->success) {
|
||||
throwException(scope, res->result.err, globalObject);
|
||||
@@ -465,29 +512,34 @@ JSValue fetchCommonJSModule(
|
||||
}
|
||||
}
|
||||
|
||||
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier)) {
|
||||
JSPromise* promise = jsCast<JSPromise*>(handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer));
|
||||
switch (promise->status(vm)) {
|
||||
case JSPromise::Status::Rejected: {
|
||||
uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt();
|
||||
promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag));
|
||||
JSC::throwException(globalObject, scope, promise->result(vm));
|
||||
RELEASE_AND_RETURN(scope, JSValue {});
|
||||
}
|
||||
case JSPromise::Status::Pending: {
|
||||
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, Bun::toWTFString(*specifier), "\" is unsupported. use \"await import()\" instead."_s));
|
||||
RELEASE_AND_RETURN(scope, JSValue {});
|
||||
}
|
||||
case JSPromise::Status::Fulfilled: {
|
||||
if (!res->success) {
|
||||
throwException(scope, res->result.err, globalObject);
|
||||
RELEASE_AND_RETURN(scope, {});
|
||||
// When "bun test" is NOT enabled, disable users from overriding builtin modules
|
||||
if (!isBunTest) {
|
||||
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
|
||||
JSPromise* promise = jsCast<JSPromise*>(handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock));
|
||||
switch (promise->status(vm)) {
|
||||
case JSPromise::Status::Rejected: {
|
||||
uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt();
|
||||
promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag));
|
||||
JSC::throwException(globalObject, scope, promise->result(vm));
|
||||
RELEASE_AND_RETURN(scope, JSValue {});
|
||||
}
|
||||
case JSPromise::Status::Pending: {
|
||||
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, Bun::toWTFString(*specifier), "\" is unsupported. use \"await import()\" instead."_s));
|
||||
RELEASE_AND_RETURN(scope, JSValue {});
|
||||
}
|
||||
case JSPromise::Status::Fulfilled: {
|
||||
if (!res->success) {
|
||||
throwException(scope, res->result.err, globalObject);
|
||||
RELEASE_AND_RETURN(scope, {});
|
||||
}
|
||||
if (!wasModuleMock) {
|
||||
auto* jsSourceCode = jsCast<JSSourceCode*>(promise->result(vm));
|
||||
globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
RELEASE_AND_RETURN(scope, jsNumber(-1));
|
||||
}
|
||||
}
|
||||
auto* jsSourceCode = jsCast<JSSourceCode*>(promise->result(vm));
|
||||
globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
RELEASE_AND_RETURN(scope, jsNumber(-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +603,8 @@ JSValue fetchCommonJSModule(
|
||||
RELEASE_AND_RETURN(scope, jsNumber(-1));
|
||||
}
|
||||
|
||||
extern "C" bool isBunTest;
|
||||
|
||||
template<bool allowPromise>
|
||||
static JSValue fetchESMSourceCode(
|
||||
Zig::GlobalObject* globalObject,
|
||||
@@ -601,6 +655,16 @@ static JSValue fetchESMSourceCode(
|
||||
}
|
||||
};
|
||||
|
||||
bool wasModuleMock = false;
|
||||
|
||||
// When "bun test" is enabled, allow users to override builtin modules
|
||||
// This is important for being able to trivially mock things like the filesystem.
|
||||
if (isBunTest) {
|
||||
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
|
||||
return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock);
|
||||
}
|
||||
}
|
||||
|
||||
if (Bun__fetchBuiltinModule(bunVM, globalObject, specifier, referrer, res)) {
|
||||
if (!res->success) {
|
||||
throwException(scope, res->result.err, globalObject);
|
||||
@@ -640,8 +704,11 @@ static JSValue fetchESMSourceCode(
|
||||
}
|
||||
}
|
||||
|
||||
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier)) {
|
||||
return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer);
|
||||
// When "bun test" is NOT enabled, disable users from overriding builtin modules
|
||||
if (!isBunTest) {
|
||||
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
|
||||
return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (allowPromise) {
|
||||
@@ -724,7 +791,10 @@ extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobal
|
||||
BunString specifier = Bun::toString(globalObject, specifierString);
|
||||
BunString referrer = Bun::toString(globalObject, referrerString);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
JSC::JSValue result = handleVirtualModuleResult<false>(reinterpret_cast<Zig::GlobalObject*>(globalObject), objectResult, &res, &specifier, &referrer);
|
||||
|
||||
bool wasModuleMock = pendingModule->wasModuleMock;
|
||||
|
||||
JSC::JSValue result = handleVirtualModuleResult<false>(reinterpret_cast<Zig::GlobalObject*>(globalObject), objectResult, &res, &specifier, &referrer, wasModuleMock);
|
||||
if (res.success) {
|
||||
if (scope.exception()) {
|
||||
auto retValue = JSValue::encode(promise->rejectWithCaughtException(globalObject, scope));
|
||||
|
||||
@@ -42,6 +42,7 @@ union OnLoadResultValue {
|
||||
struct OnLoadResult {
|
||||
OnLoadResultValue value;
|
||||
OnLoadResultType type;
|
||||
bool wasMock;
|
||||
};
|
||||
|
||||
class PendingVirtualModuleResult : public JSC::JSInternalFieldObjectImpl<3> {
|
||||
@@ -81,9 +82,10 @@ public:
|
||||
|
||||
PendingVirtualModuleResult(JSC::VM&, JSC::Structure*);
|
||||
void finishCreation(JSC::VM&, const WTF::String& specifier, const WTF::String& referrer);
|
||||
|
||||
bool wasModuleMock = false;
|
||||
};
|
||||
|
||||
OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue);
|
||||
JSValue fetchESMSourceCodeSync(
|
||||
Zig::GlobalObject* globalObject,
|
||||
ErrorableResolvedSource* res,
|
||||
|
||||
@@ -2206,7 +2206,8 @@ static inline EncodedJSValue functionPerformanceNowBody(JSGlobalObject* globalOb
|
||||
return JSValue::encode(jsDoubleNumber(result));
|
||||
}
|
||||
|
||||
static inline EncodedJSValue functionPerformanceGetEntriesByNameBody(JSGlobalObject* globalObject) {
|
||||
static inline EncodedJSValue functionPerformanceGetEntriesByNameBody(JSGlobalObject* globalObject)
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
auto* global = reinterpret_cast<GlobalObject*>(globalObject);
|
||||
auto* array = JSC::constructEmptyArray(globalObject, nullptr);
|
||||
@@ -2296,7 +2297,6 @@ JSC_DEFINE_HOST_FUNCTION(functionPerformanceNow, (JSGlobalObject * globalObject,
|
||||
return functionPerformanceNowBody(globalObject);
|
||||
}
|
||||
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(functionPerformanceGetEntriesByName, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
return functionPerformanceGetEntriesByNameBody(globalObject);
|
||||
@@ -3149,6 +3149,24 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
init.set(map);
|
||||
});
|
||||
|
||||
m_esmRegistryMap.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSMap>::Initializer& init) {
|
||||
auto* global = init.owner;
|
||||
auto& vm = init.vm;
|
||||
JSMap* registry = nullptr;
|
||||
if (auto loaderValue = global->getIfPropertyExists(global, JSC::Identifier::fromString(vm, "Loader"_s))) {
|
||||
if (auto registryValue = loaderValue.getObject()->getIfPropertyExists(global, JSC::Identifier::fromString(vm, "registry"_s))) {
|
||||
registry = jsCast<JSC::JSMap*>(registryValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!registry) {
|
||||
registry = JSC::JSMap::create(init.vm, init.owner->mapStructure());
|
||||
}
|
||||
|
||||
init.set(registry);
|
||||
});
|
||||
|
||||
m_encodeIntoObjectStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
|
||||
auto& vm = init.vm;
|
||||
@@ -3886,6 +3904,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
thisObject->m_utilInspectStylizeNoColorFunction.visit(visitor);
|
||||
thisObject->m_lazyReadableStreamPrototypeMap.visit(visitor);
|
||||
thisObject->m_requireMap.visit(visitor);
|
||||
thisObject->m_esmRegistryMap.visit(visitor);
|
||||
thisObject->m_encodeIntoObjectStructure.visit(visitor);
|
||||
thisObject->m_JSArrayBufferControllerPrototype.visit(visitor);
|
||||
thisObject->m_JSFileSinkControllerPrototype.visit(visitor);
|
||||
@@ -3928,6 +3947,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
thisObject->mockModule.mockResultStructure.visit(visitor);
|
||||
thisObject->mockModule.mockImplementationStructure.visit(visitor);
|
||||
thisObject->mockModule.mockObjectStructure.visit(visitor);
|
||||
thisObject->mockModule.mockModuleStructure.visit(visitor);
|
||||
thisObject->mockModule.activeSpySetStructure.visit(visitor);
|
||||
thisObject->mockModule.mockWithImplementationCleanupDataStructure.visit(visitor);
|
||||
thisObject->mockModule.withImplementationCleanupFunction.visit(visitor);
|
||||
|
||||
@@ -213,6 +213,7 @@ public:
|
||||
|
||||
JSC::JSMap* readableStreamNativeMap() { return m_lazyReadableStreamPrototypeMap.getInitializedOnMainThread(this); }
|
||||
JSC::JSMap* requireMap() { return m_requireMap.getInitializedOnMainThread(this); }
|
||||
JSC::JSMap* esmRegistryMap() { return m_esmRegistryMap.getInitializedOnMainThread(this); }
|
||||
JSC::Structure* encodeIntoObjectStructure() { return m_encodeIntoObjectStructure.getInitializedOnMainThread(this); }
|
||||
|
||||
JSC::Structure* callSiteStructure() const { return m_callSiteStructure.getInitializedOnMainThread(this); }
|
||||
@@ -481,6 +482,7 @@ public:
|
||||
LazyProperty<JSGlobalObject, JSFunction> m_emitReadableNextTickFunction;
|
||||
LazyProperty<JSGlobalObject, JSMap> m_lazyReadableStreamPrototypeMap;
|
||||
LazyProperty<JSGlobalObject, JSMap> m_requireMap;
|
||||
LazyProperty<JSGlobalObject, JSMap> m_esmRegistryMap;
|
||||
LazyProperty<JSGlobalObject, Structure> m_encodeIntoObjectStructure;
|
||||
LazyProperty<JSGlobalObject, JSObject> m_JSArrayBufferControllerPrototype;
|
||||
LazyProperty<JSGlobalObject, JSObject> m_JSFileSinkControllerPrototype;
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMScript;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForCommonJSModuleRecord;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSMockImplementation;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSModuleMock;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSMockFunction;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForAsyncContextFrame;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMockWithImplementationCleanupData;
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMScript;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForCommonJSModuleRecord;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSMockImplementation;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSModuleMock;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSMockFunction;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForAsyncContextFrame;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForMockWithImplementationCleanupData;
|
||||
|
||||
@@ -3258,3 +3258,5 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub export var isBunTest: bool = false;
|
||||
|
||||
@@ -1655,6 +1655,10 @@ pub const Expect = struct {
|
||||
const result_: ?JSValue = brk: {
|
||||
var vm = globalObject.bunVM();
|
||||
var return_value: JSValue = .zero;
|
||||
|
||||
// Drain existing unhandled rejections
|
||||
vm.global.handleRejectedPromises();
|
||||
|
||||
var scope = vm.unhandledRejectionScope();
|
||||
var prev_unhandled_pending_rejection_to_capture = vm.unhandled_pending_rejection_to_capture;
|
||||
vm.unhandled_pending_rejection_to_capture = &return_value;
|
||||
@@ -1662,12 +1666,14 @@ pub const Expect = struct {
|
||||
const return_value_from_fucntion: JSValue = value.call(globalObject, &.{});
|
||||
vm.unhandled_pending_rejection_to_capture = prev_unhandled_pending_rejection_to_capture;
|
||||
|
||||
vm.global.handleRejectedPromises();
|
||||
|
||||
if (return_value == .zero) {
|
||||
return_value = return_value_from_fucntion;
|
||||
}
|
||||
|
||||
if (return_value.asAnyPromise()) |promise| {
|
||||
globalObject.bunVM().waitForPromise(promise);
|
||||
vm.waitForPromise(promise);
|
||||
scope.apply(vm);
|
||||
const promise_result = promise.result(globalObject.vm());
|
||||
|
||||
@@ -1676,15 +1682,24 @@ pub const Expect = struct {
|
||||
break :brk null;
|
||||
},
|
||||
.Rejected => {
|
||||
promise.setHandled(globalObject.vm());
|
||||
|
||||
// since we know for sure it rejected, we should always return the error
|
||||
break :brk promise_result.toError() orelse promise_result;
|
||||
},
|
||||
.Pending => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
if (return_value != return_value_from_fucntion) {
|
||||
if (return_value_from_fucntion.asAnyPromise()) |existing| {
|
||||
existing.setHandled(globalObject.vm());
|
||||
}
|
||||
}
|
||||
|
||||
scope.apply(vm);
|
||||
|
||||
break :brk return_value.toError();
|
||||
break :brk return_value.toError() orelse return_value_from_fucntion.toError();
|
||||
};
|
||||
|
||||
const did_throw = result_ != null;
|
||||
|
||||
@@ -459,7 +459,9 @@ pub const Jest = struct {
|
||||
const mockFn = JSC.NewFunction(globalObject, ZigString.static("fn"), 1, JSMock__jsMockFn, false);
|
||||
const spyOn = JSC.NewFunction(globalObject, ZigString.static("spyOn"), 2, JSMock__jsSpyOn, false);
|
||||
const restoreAllMocks = JSC.NewFunction(globalObject, ZigString.static("restoreAllMocks"), 2, JSMock__jsRestoreAllMocks, false);
|
||||
const mockModuleFn = JSC.NewFunction(globalObject, ZigString.static("module"), 2, JSMock__jsModuleMock, false);
|
||||
module.put(globalObject, ZigString.static("mock"), mockFn);
|
||||
mockFn.put(globalObject, ZigString.static("module"), mockModuleFn);
|
||||
|
||||
const jest = JSValue.createEmptyObject(globalObject, 7);
|
||||
jest.put(globalObject, ZigString.static("fn"), mockFn);
|
||||
@@ -488,6 +490,7 @@ pub const Jest = struct {
|
||||
const vi = JSValue.createEmptyObject(globalObject, 3);
|
||||
vi.put(globalObject, ZigString.static("fn"), mockFn);
|
||||
vi.put(globalObject, ZigString.static("spyOn"), spyOn);
|
||||
vi.put(globalObject, ZigString.static("module"), mockModuleFn);
|
||||
vi.put(globalObject, ZigString.static("restoreAllMocks"), restoreAllMocks);
|
||||
module.put(globalObject, ZigString.static("vi"), vi);
|
||||
|
||||
@@ -497,6 +500,7 @@ pub const Jest = struct {
|
||||
extern fn Bun__Jest__testPreloadObject(*JSC.JSGlobalObject) JSC.JSValue;
|
||||
extern fn Bun__Jest__testModuleObject(*JSC.JSGlobalObject) JSC.JSValue;
|
||||
extern fn JSMock__jsMockFn(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;
|
||||
extern fn JSMock__jsModuleMock(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;
|
||||
extern fn JSMock__jsNow(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;
|
||||
extern fn JSMock__jsSetSystemTime(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;
|
||||
extern fn JSMock__jsRestoreAllMocks(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;
|
||||
@@ -687,7 +691,7 @@ pub const TestScope = struct {
|
||||
this.func_arg[this.func_arg.len - 1] = callback_func;
|
||||
}
|
||||
|
||||
initial_value = this.func.call(vm.global, @as([]const JSC.JSValue, this.func_arg));
|
||||
initial_value = callJSFunctionForTestRunner(vm, vm.global, this.func, this.func_arg);
|
||||
|
||||
if (initial_value.isAnyError()) {
|
||||
if (!Jest.runner.?.did_pending_test_fail) {
|
||||
@@ -937,7 +941,7 @@ pub const DescribeScope = struct {
|
||||
|
||||
const vm = VirtualMachine.get();
|
||||
var result: JSC.JSValue = switch (cb.getLength(globalObject)) {
|
||||
0 => cb.call(globalObject, &.{}),
|
||||
0 => callJSFunctionForTestRunner(vm, globalObject, cb, &.{}),
|
||||
else => brk: {
|
||||
this.done = false;
|
||||
const done_func = JSC.NewFunctionWithData(
|
||||
@@ -948,7 +952,7 @@ pub const DescribeScope = struct {
|
||||
false,
|
||||
this,
|
||||
);
|
||||
var result = cb.call(globalObject, &.{done_func});
|
||||
var result = callJSFunctionForTestRunner(vm, globalObject, cb, &.{done_func});
|
||||
vm.waitFor(&this.done);
|
||||
break :brk result;
|
||||
},
|
||||
@@ -1000,7 +1004,8 @@ pub const DescribeScope = struct {
|
||||
|
||||
const vm = VirtualMachine.get();
|
||||
// note: we do not support "done" callback in global hooks in the first release.
|
||||
var result: JSC.JSValue = cb.call(globalThis, &.{});
|
||||
var result: JSC.JSValue = callJSFunctionForTestRunner(vm, globalThis, cb, &.{});
|
||||
|
||||
if (result.asAnyPromise()) |promise| {
|
||||
if (promise.status(globalThis.vm()) == .Pending) {
|
||||
result.protect();
|
||||
@@ -1094,8 +1099,7 @@ pub const DescribeScope = struct {
|
||||
|
||||
{
|
||||
JSC.markBinding(@src());
|
||||
globalObject.clearTerminationException();
|
||||
var result = callback.call(globalObject, args);
|
||||
var result = callJSFunctionForTestRunner(VirtualMachine.get(), globalObject, callback, args);
|
||||
|
||||
if (result.asAnyPromise()) |prom| {
|
||||
globalObject.bunVM().waitForPromise(prom);
|
||||
@@ -2047,3 +2051,12 @@ inline fn createEach(
|
||||
|
||||
return JSC.NewFunctionWithData(globalThis, name, 3, eachBind, true, each_data);
|
||||
}
|
||||
|
||||
fn callJSFunctionForTestRunner(vm: *JSC.VirtualMachine, globalObject: *JSC.JSGlobalObject, function: JSC.JSValue, args: []const JSC.JSValue) JSC.JSValue {
|
||||
globalObject.clearTerminationException();
|
||||
const result = function.call(globalObject, args);
|
||||
result.ensureStillAlive();
|
||||
globalObject.vm().releaseWeakRefs();
|
||||
vm.drainMicrotasks();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -580,6 +580,7 @@ pub const TestCommand = struct {
|
||||
var snapshot_file_buf = std.ArrayList(u8).init(ctx.allocator);
|
||||
var snapshot_values = Snapshots.ValuesHashMap.init(ctx.allocator);
|
||||
var snapshot_counts = bun.StringHashMap(usize).init(ctx.allocator);
|
||||
JSC.isBunTest = true;
|
||||
|
||||
var reporter = try ctx.allocator.create(CommandLineReporter);
|
||||
reporter.* = CommandLineReporter{
|
||||
|
||||
@@ -368,8 +368,8 @@ describe("bundler", () => {
|
||||
console.log('observer', getValue());
|
||||
}
|
||||
setObserver(observer);
|
||||
import('./out/a.js');
|
||||
import('./out/b.js');
|
||||
await import('./out/a.js');
|
||||
await import('./out/b.js');
|
||||
`,
|
||||
},
|
||||
run: [
|
||||
|
||||
@@ -565,34 +565,34 @@ describe("expect()", () => {
|
||||
}).not.toThrow(err);
|
||||
|
||||
const weirdThings = [
|
||||
/watttt/g,
|
||||
BigInt(123),
|
||||
-42,
|
||||
NaN,
|
||||
Infinity,
|
||||
-Infinity,
|
||||
undefined,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
1,
|
||||
"",
|
||||
"hello",
|
||||
{},
|
||||
[],
|
||||
new Date(),
|
||||
new Error(),
|
||||
new RegExp("foo"),
|
||||
new Map(),
|
||||
new Set(),
|
||||
Promise.resolve(),
|
||||
Promise.reject(Symbol("123")).finally(() => {}),
|
||||
Symbol("123"),
|
||||
() => /watttt/g,
|
||||
() => BigInt(123),
|
||||
() => -42,
|
||||
() => NaN,
|
||||
() => Infinity,
|
||||
() => -Infinity,
|
||||
() => undefined,
|
||||
() => null,
|
||||
() => true,
|
||||
() => false,
|
||||
() => 0,
|
||||
() => 1,
|
||||
() => "",
|
||||
() => "hello",
|
||||
() => {},
|
||||
() => [],
|
||||
() => new Date(),
|
||||
() => new Error(),
|
||||
() => new RegExp("foo"),
|
||||
() => new Map(),
|
||||
() => new Set(),
|
||||
() => Promise.resolve(),
|
||||
() => Promise.reject(Symbol("123")),
|
||||
() => Symbol("123"),
|
||||
];
|
||||
for (const weirdThing of weirdThings) {
|
||||
expect(() => {
|
||||
throw weirdThing;
|
||||
throw weirdThing();
|
||||
}).toThrow();
|
||||
}
|
||||
|
||||
|
||||
9
test/js/bun/test/mock/mock-module-fixture.ts
Normal file
9
test/js/bun/test/mock/mock-module-fixture.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function fn() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
export function iCallFn() {
|
||||
return fn();
|
||||
}
|
||||
|
||||
export const variable = 7;
|
||||
82
test/js/bun/test/mock/mock-module.test.ts
Normal file
82
test/js/bun/test/mock/mock-module.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// TODO:
|
||||
// - Write tests for errors
|
||||
// - Write tests for Promise
|
||||
// - Write tests for Promise rejection
|
||||
// - Write tests for pending promise when a module already exists
|
||||
// - Write test for export * from
|
||||
// - Write test for export {foo} from "./foo"
|
||||
// - Write test for import {foo} from "./foo"; export {foo}
|
||||
|
||||
import { mock, test, expect, spyOn, Mock } from "bun:test";
|
||||
import { fn, iCallFn, variable } from "./mock-module-fixture";
|
||||
import * as spyFixture from "./spymodule-fixture";
|
||||
|
||||
test("spyOn", () => {
|
||||
spyOn(spyFixture, "iSpy");
|
||||
expect(spyFixture.iSpy).not.toHaveBeenCalled();
|
||||
spyFixture.iSpy(123);
|
||||
expect(spyFixture.iSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("mocking a local file", async () => {
|
||||
expect(fn()).toEqual(42);
|
||||
expect(variable).toEqual(7);
|
||||
|
||||
mock.module("./mock-module-fixture.ts", () => {
|
||||
return {
|
||||
fn: () => 1,
|
||||
variable: 8,
|
||||
};
|
||||
});
|
||||
expect(fn()).toEqual(1);
|
||||
expect(variable).toEqual(8);
|
||||
mock.module("./mock-module-fixture.ts", () => {
|
||||
return {
|
||||
fn: () => 2,
|
||||
variable: 9,
|
||||
};
|
||||
});
|
||||
expect(fn()).toEqual(2);
|
||||
expect(variable).toEqual(9);
|
||||
mock.module("./mock-module-fixture.ts", () => {
|
||||
return {
|
||||
fn: () => 3,
|
||||
variable: 10,
|
||||
};
|
||||
});
|
||||
expect(fn()).toEqual(3);
|
||||
expect(variable).toEqual(10);
|
||||
expect(require("./mock-module-fixture").fn()).toBe(3);
|
||||
expect(require("./mock-module-fixture").variable).toBe(10);
|
||||
expect(iCallFn()).toBe(3);
|
||||
});
|
||||
|
||||
test("mocking a package", async () => {
|
||||
mock.module("ha-ha-ha", () => {
|
||||
return {
|
||||
wow: () => 42,
|
||||
};
|
||||
});
|
||||
const hahaha = await import("ha-ha-ha");
|
||||
expect(hahaha.wow()).toBe(42);
|
||||
expect(require("ha-ha-ha").wow()).toBe(42);
|
||||
mock.module("ha-ha-ha", () => {
|
||||
return {
|
||||
wow: () => 43,
|
||||
};
|
||||
});
|
||||
|
||||
expect(hahaha.wow()).toBe(43);
|
||||
expect(require("ha-ha-ha").wow()).toBe(43);
|
||||
});
|
||||
|
||||
test("mocking a builtin", async () => {
|
||||
mock.module("fs/promises", () => {
|
||||
return {
|
||||
readFile: () => Promise.resolve("hello world"),
|
||||
};
|
||||
});
|
||||
|
||||
const { readFile } = await import("node:fs/promises");
|
||||
expect(await readFile("hello.txt", "utf8")).toBe("hello world");
|
||||
});
|
||||
3
test/js/bun/test/mock/spymodule-fixture.ts
Normal file
3
test/js/bun/test/mock/spymodule-fixture.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function iSpy(_) {
|
||||
return 42;
|
||||
}
|
||||
Reference in New Issue
Block a user