aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/internal-module-registry-scanner.ts
blob: 7bc2f9bdddb190d8ffec54da77b402a2d9856ea4 (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
import fs from "fs";
import path from "path";
import { readdirRecursive, resolveSyncOrNull } from "./helpers";

export function createInternalModuleRegistry(basedir: string) {
  const moduleList = ["bun", "node", "thirdparty", "internal"]
    .flatMap(dir => readdirRecursive(path.join(basedir, dir)))
    .filter(file => file.endsWith(".js") || (file.endsWith(".ts") && !file.endsWith(".d.ts")))
    .map(file => file.slice(basedir.length + 1))
    .sort();

  // Create the Internal Module Registry
  const internalRegistry = new Map();
  for (let i = 0; i < moduleList.length; i++) {
    const prefix = moduleList[i].startsWith("node/")
      ? "node:"
      : moduleList[i].startsWith("bun:")
      ? "bun:"
      : moduleList[i].startsWith("internal/")
      ? "internal/"
      : undefined;
    if (prefix) {
      const id = prefix + moduleList[i].slice(prefix.length).replaceAll(".", "/").slice(0, -3);
      internalRegistry.set(id, i);
    }
  }

  // Native Module registry
  const nativeModuleH = fs.readFileSync(path.join(basedir, "../bun.js/modules/_NativeModule.h"), "utf8");
  const nativeModuleDefine = nativeModuleH.match(/BUN_FOREACH_NATIVE_MODULE\(macro\)\s*\\\n((.*\\\n)*\n)/);
  if (!nativeModuleDefine) {
    throw new Error(
      "Could not find BUN_FOREACH_NATIVE_MODULE in _NativeModule.h. Knowing native module IDs is a part of the codegen process.",
    );
  }
  let nextNativeModuleId = 0;
  const nativeModuleIds: Record<string, number> = {};
  const nativeModuleEnums: Record<string, string> = {};
  const nativeModuleEnumToId: Record<string, number> = {};
  for (const [_, idString, enumValue] of nativeModuleDefine[0].matchAll(/macro\((.*?),(.*?)\)/g)) {
    const processedIdString = JSON.parse(idString.trim().replace(/_s$/, ""));
    const processedEnumValue = enumValue.trim();
    const processedNumericId = nextNativeModuleId++;
    nativeModuleIds[processedIdString] = processedNumericId;
    nativeModuleEnums[processedIdString] = processedEnumValue;
    nativeModuleEnumToId[processedEnumValue] = processedNumericId;
  }

  function codegenRequireId(id: string) {
    return `(__intrinsic__getInternalField(__intrinsic__internalModuleRegistry, ${id}) || __intrinsic__createInternalModuleById(${id}))`;
  }

  function codegenRequireNativeModule(id: string) {
    return `(__intrinsic__requireNativeModule(${id.replace(/node:/, "")}))`;
  }

  const requireTransformer = (specifier: string, from: string) => {
    // this one is deprecated
    if (specifier === "$shared") specifier = "./internal/shared.ts";

    const directMatch = internalRegistry.get(specifier);
    if (directMatch) return codegenRequireId(`${directMatch}/*${specifier}*/`);

    if (specifier in nativeModuleIds) {
      return codegenRequireNativeModule(JSON.stringify(specifier));
    }

    const relativeMatch =
      resolveSyncOrNull(specifier, path.join(basedir, path.dirname(from))) ?? resolveSyncOrNull(specifier, basedir);

    if (relativeMatch) {
      const found = moduleList.indexOf(path.relative(basedir, relativeMatch));
      if (found === -1) {
        throw new Error(
          `Builtin Bundler: "${specifier}" cannot be imported here because it doesn't get a module ID. Only files in "src/js" besides "src/js/builtins" can be used here. Note that the 'node:' or 'bun:' prefix is required here. `,
        );
      }
      return codegenRequireId(`${found}/*${path.relative(basedir, relativeMatch)}*/`);
    }

    throw new Error(`Builtin Bundler: Could not resolve "${specifier}" in ${from}.`);
  };

  return {
    requireTransformer,
    nativeModuleIds,
    nativeModuleEnums,
    nativeModuleEnumToId,
    internalRegistry,
    moduleList,
  } as const;
}