aboutsummaryrefslogtreecommitdiff
path: root/src/js/builtins/Module.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/builtins/Module.ts')
-rw-r--r--src/js/builtins/Module.ts100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/js/builtins/Module.ts b/src/js/builtins/Module.ts
new file mode 100644
index 000000000..0b5fcafe8
--- /dev/null
+++ b/src/js/builtins/Module.ts
@@ -0,0 +1,100 @@
+interface Module {
+ id: string;
+ path: string;
+
+ $require(id: string, mod: any): any;
+ children: Module[];
+}
+
+$getter;
+export function main() {
+ return $requireMap.$get(Bun.main);
+}
+
+export function require(this: Module, id: string) {
+ const existing = $requireMap.$get(id) || $requireMap.$get((id = $resolveSync(id, this.path, false)));
+ if (existing) {
+ // Scenario where this is necessary:
+ //
+ // In an ES Module, we have:
+ //
+ // import "react-dom/server"
+ // import "react"
+ //
+ // Synchronously, the "react" import is created first, and then the
+ // "react-dom/server" import is created. Then, at ES Module link time, they
+ // are evaluated. The "react-dom/server" import is evaluated first, and
+ // require("react") was previously created as an ESM module, so we wait
+ // for the ESM module to load
+ //
+ // ...and then when this code is reached, unless
+ // we evaluate it "early", we'll get an empty object instead of the module
+ // exports.
+ //
+ $evaluateCommonJSModule(existing);
+ return existing.exports;
+ }
+
+ if (id.endsWith(".json") || id.endsWith(".toml") || id.endsWith(".node")) {
+ return $internalRequire(id);
+ }
+
+ let esm = Loader.registry.$get(id);
+ if (esm?.evaluated && (esm.state ?? 0) >= $ModuleReady) {
+ const mod = esm.module;
+ const namespace = Loader.getModuleNamespaceObject(mod);
+ const exports =
+ namespace?.[$commonJSSymbol] === 0 || namespace?.default?.[$commonJSSymbol] === 0
+ ? namespace.default
+ : namespace.__esModule
+ ? namespace
+ : Object.create(namespace, { __esModule: { value: true } });
+ $requireMap.$set(id, $createCommonJSModule(id, exports, true));
+ return exports;
+ }
+
+ // To handle import/export cycles, we need to create a module object and put
+ // it into the map before we import it.
+ const mod = $createCommonJSModule(id, {}, false);
+ $requireMap.$set(id, mod);
+
+ // This is where we load the module. We will see if Module._load and
+ // Module._compile are actually important for compatibility.
+ //
+ // Note: we do not need to wrap this in a try/catch, if it throws the C++ code will
+ // clear the module from the map.
+ //
+ var out = this.$require(id, mod);
+
+ // -1 means we need to lookup the module from the ESM registry.
+ if (out === -1) {
+ try {
+ out = $requireESM(id);
+ } catch (exception) {
+ // Since the ESM code is mostly JS, we need to handle exceptions here.
+ $requireMap.$delete(id);
+ throw exception;
+ }
+
+ esm = Loader.registry.$get(id);
+
+ // If we can pull out a ModuleNamespaceObject, let's do it.
+ if (esm?.evaluated && (esm.state ?? 0) >= $ModuleReady) {
+ const namespace = Loader.getModuleNamespaceObject(esm!.module);
+ return (mod.exports =
+ // if they choose a module
+ namespace?.[$commonJSSymbol] === 0 || namespace?.default?.[$commonJSSymbol] === 0
+ ? namespace.default
+ : namespace.__esModule
+ ? namespace
+ : Object.create(namespace, { __esModule: { value: true } }));
+ }
+ }
+
+ $evaluateCommonJSModule(mod);
+ return mod.exports;
+}
+
+export function requireResolve(this: Module, id: string) {
+ return $resolveSync(id, this.path, false);
+}