diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 961ed96073..9ffc358cfe 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -2333,6 +2333,7 @@ pub const ModuleLoader = struct { .@"node-fetch" => return jsSyntheticModule(.@"node-fetch", specifier), .@"@vercel/fetch" => return jsSyntheticModule(.vercel_fetch, specifier), .@"utf-8-validate" => return jsSyntheticModule(.@"utf-8-validate", specifier), + .@"abort-controller" => return jsSyntheticModule(.@"abort-controller", specifier), .undici => return jsSyntheticModule(.undici, specifier), .ws => return jsSyntheticModule(.ws, specifier), } @@ -2473,6 +2474,7 @@ const SavedSourceMap = JSC.SavedSourceMap; pub const HardcodedModule = enum { bun, + @"abort-controller", @"bun:ffi", @"bun:jsc", @"bun:main", @@ -2614,6 +2616,7 @@ pub const HardcodedModule = enum { .{ "ws", HardcodedModule.ws }, .{ "@vercel/fetch", HardcodedModule.@"@vercel/fetch" }, .{ "utf-8-validate", HardcodedModule.@"utf-8-validate" }, + .{ "abort-controller", HardcodedModule.@"abort-controller" }, }, ); @@ -2779,6 +2782,10 @@ pub const HardcodedModule = enum { .{ "inspector/promises", .{ .path = "inspector" } }, .{ "node:inspector/promises", .{ .path = "inspector" } }, + + // Polyfills we force to native + .{ "abort-controller", .{ .path = "abort-controller" } }, + .{ "abort-controller/polyfill", .{ .path = "abort-controller" } }, }; const node_alias_kvs = .{ diff --git a/src/bun.js/modules/AbortControllerModuleModule.h b/src/bun.js/modules/AbortControllerModuleModule.h new file mode 100644 index 0000000000..07c8ccea03 --- /dev/null +++ b/src/bun.js/modules/AbortControllerModuleModule.h @@ -0,0 +1,54 @@ +#pragma once + +#include "JSAbortController.h" +#include "JSAbortSignal.h" + +using namespace JSC; +using namespace WebCore; + +namespace Zig { + +inline void generateNativeModule_AbortControllerModule( + JSC::JSGlobalObject *lexicalGlobalObject, JSC::Identifier moduleKey, + Vector &exportNames, + JSC::MarkedArgumentBuffer &exportValues) { + + Zig::GlobalObject *globalObject = + reinterpret_cast(lexicalGlobalObject); + JSC::VM &vm = globalObject->vm(); + + auto *abortController = + WebCore::JSAbortController::getConstructor(vm, globalObject).getObject(); + JSValue abortSignal = + WebCore::JSAbortSignal::getConstructor(vm, globalObject); + + const auto controllerIdent = Identifier::fromString(vm, "AbortController"_s); + const auto signalIdent = Identifier::fromString(vm, "AbortSignal"_s); + const Identifier esModuleMarker = builtinNames(vm).__esModulePublicName(); + + exportNames.append(vm.propertyNames->defaultKeyword); + exportValues.append(abortController); + + exportNames.append(signalIdent); + exportValues.append(abortSignal); + + exportNames.append(controllerIdent); + exportValues.append(abortController); + + exportNames.append(esModuleMarker); + exportValues.append(jsBoolean(true)); + + // https://github.com/mysticatea/abort-controller/blob/a935d38e09eb95d6b633a8c42fcceec9969e7b05/dist/abort-controller.js#L125 + abortController->putDirect( + vm, signalIdent, abortSignal, + static_cast(PropertyAttribute::DontEnum)); + + abortController->putDirect( + vm, controllerIdent, abortController, + static_cast(PropertyAttribute::DontEnum)); + + abortController->putDirect( + vm, vm.propertyNames->defaultKeyword, abortController, + static_cast(PropertyAttribute::DontEnum)); +} +} // namespace Zig diff --git a/src/bun.js/modules/_NativeModule.h b/src/bun.js/modules/_NativeModule.h index f21ab46305..2e1545db28 100644 --- a/src/bun.js/modules/_NativeModule.h +++ b/src/bun.js/modules/_NativeModule.h @@ -34,6 +34,7 @@ macro("node:string_decoder"_s, NodeStringDecoder) \ macro("node:util/types"_s, NodeUtilTypes) \ macro("utf-8-validate"_s, UTF8Validate) \ + macro("abort-controller"_s, AbortControllerModule) \ #if ASSERT_ENABLED diff --git a/test/js/web/nationalized.test.ts b/test/js/web/nationalized.test.ts new file mode 100644 index 0000000000..e8d2f1462e --- /dev/null +++ b/test/js/web/nationalized.test.ts @@ -0,0 +1,31 @@ +import { test, expect, describe } from "bun:test"; + +// abort-controller +// 13 million weekly downloads +// https://github.com/mysticatea/abort-controller/blob/a935d38e09eb95d6b633a8c42fcceec9969e7b05/dist/abort-controller.js#L1 +describe("abort-controller", () => { + // + // We do not nationalie event-target-shim which this package depends on + // That is because it adds `defineEventTargetAttribute` which we would have to implemennt or else it would break packages that depend on it. + // + + test("CJS", () => { + const AbortControllerPolyfill = require("abort-controller"); + expect(AbortControllerPolyfill).toBe(AbortController); + expect(AbortControllerPolyfill.AbortSignal).toBe(AbortSignal); + expect(AbortControllerPolyfill.default.AbortController).toBe(AbortController); + expect(AbortControllerPolyfill.default.AbortSignal).toBe(AbortSignal); + }); + + test("ESM", async () => { + const AbortControllerPolyfill = await import("abort-controller"); + // @ts-ignore + expect(AbortControllerPolyfill.AbortController).toBe(AbortController); + // @ts-ignore + expect(AbortControllerPolyfill.AbortSignal).toBe(AbortSignal); + // @ts-ignore + expect(AbortControllerPolyfill.default.AbortController).toBe(AbortController); + // @ts-ignore + expect(AbortControllerPolyfill.default.AbortSignal).toBe(AbortSignal); + }); +});