aboutsummaryrefslogtreecommitdiff
path: root/src/js/builtins/Module.ts
blob: 3c9c2f1a00f7207d0b5ba4021d6fb0c385798570 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
$getter;
export function main() {
  return $requireMap.$get(Bun.main);
}

export function require(this: CommonJSModuleRecord, id: string) {
  return $overridableRequire.$call(this, id);
}

// overridableRequire can be overridden by setting `Module.prototype.require`
export function overridableRequire(this: CommonJSModuleRecord, 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(".node")) {
    return $internalRequire(id);
  }

  // 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, this);
  $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 = mod.$require(id, this?.id);

  // -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;
    }

    const 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.__esModule ? namespace : Object.create(namespace, { __esModule: { value: true } }));
    }
  }

  $evaluateCommonJSModule(mod);
  return mod.exports;
}

export function requireResolve(this: string | { path: string }, id: string) {
  return $resolveSync(id, typeof this === "string" ? this : this?.path, false);
}

export function requireNativeModule(id: string) {
  let esm = Loader.registry.$get(id);
  if (esm?.evaluated && (esm.state ?? 0) >= $ModuleReady) {
    const exports = Loader.getModuleNamespaceObject(esm.module);
    return exports.default;
  }
  return $requireESM(id).default;
}

/** require('node:module')._load */
export function moduleLoad(request: string, parentPath: CommonJSModuleRecord, isMain: string) {
  // TODO: `isMain` does four things in node
  // - sets `process.mainModule`
  // - sets `module.require.main`
  // - sets `module.id` to "."
  // - would pass true to the third argument of _resolveFilename if overridden
  return $overridableRequire.$call(parentPath, request);
}