4
5

More than 3 years have passed since last update.

Node.jsのモジュール解決プロセス(和訳)

Last updated at Posted at 2020-04-15

Node.jsのrequireがどのようにモジュールを探すか、そのプロセスを説明した下記公式ドキュメントの和訳です。

内容に誤りがあればお教えください。

Node.jsのモジュール解決プロセス

require(X) をパス Y にあるモジュールで実行したとき、

  1. もし、 X がコアモジュールなら、
    • a. コアモジュールを返す
    • b. 終了
  2. もし、 X が "/" で始まるなら、
    • a. Y のパスをファイルシステムルートにセットしなおす
  3. もし、 X が "./"、"/"、"../"のどれかで始まるなら、
  4. LOAD_SELF_REFERENCE(X, dirname(Y))
  5. LOAD_NODE_MODULES(X, dirname(Y))
  6. 例外"not found"を投げる

LOAD_AS_FILE(X)

  1. もし、 X がファイルなら、 X をそのファイル拡張子の形式としてロードする。 終了
  2. もし、 X.js がファイルなら、 X.js をJavaScriptテキストとしてロードする。 終了
  3. もし、 X.json がファイルなら、 X.json をパースしてJavaScriptオブジェクトにする。 終了
  4. もし、 X.node がファイルなら、 X.node をバイナリアドオンとしてロードする。 終了

LOAD_INDEX(X)

  1. もし、 X/index.js がファイルなら、 X/index.js をJavaScriptテキストとしてロードする。 終了
  2. もし、 X/index.json がファイルなら、 X/index.json をパースしてJavaScriptオブジェクトにする。 終了
  3. もし、 X/index.node がファイルなら、 X/index.node をバイナリアドオンとしてロードする。 終了

LOAD_AS_DIRECTORY(X)

  1. もし、 X/package.json がファイルなら、
    • a. X/package.json をパースして、mainフィールドを探す。
    • b. もし、mainがfalsyな値なら、2に進む
    • c. let M = X + main
    • d. LOAD_AS_FILE(M)
    • e. LOAD_INDEX(M)
    • f. LOAD_INDEX(X) 非推奨
    • g. 例外"not found"を投げる
  2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)

  1. let DIRS = NODE_MODULES_PATHS(START)
  2. for each DIR in DIRS:

NODE_MODULES_PATHS(START)

1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = [GLOBAL_FOLDERS]
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS

これは、NODE_MODULES_PATHS('/root/a/b/c')を与えたときに、下記のリストを返す処理です。

[
  '$HOME/.node_modules',
  '$HOME/.node_libraries',
  '$PREFIX/lib/node',
  '/root/a/b/c/node_modules',
  '/root/a/b/node_modules',
  '/root/a/node_modules',
  '/root/node_modules',
  '/node_modules',
]

LOAD_SELF_REFERENCE(X, START)

  1. STARTに最も近いパッケージのスコープを探す。
  2. もし、スコープが見つからなければ、return。
  3. もし、package.jsonに"exports"がなければ、return。
  4. もし、package.jsonの name が X で始まらないなら、"not found"例外を投げる。
  5. それ以外の場合は、このパッケージに相対的な X の残りの部分を、package.jsonのnameでLOAD_NODE_MODULESを介してロードされたかのようにロードする。

LOAD_PACKAGE_EXPORTS(DIR, X)

このプロセスは右記の規格に対応するものです→jkrems/proposal-pkg-exports: Proposal for Bare Module Specifier Resolution in node.js

  1. X を名前とサブパスの組み合わせとしての解釈を試みる。その名前は @scope にスラッシュ(/)で始まるサブパスが続く形式を想定する。
  2. もし、 X このパターンにマッチしない、もしくは、DIR/名前/package.jsonがファイルでないなら、return。
  3. DIR/name/package.json をパースして、 "exports" フィールドを探す。
  4. もし、 "exports" が null か undefined なら、return。
  5. もし、 "exports" が object での場合、"." 始まりのキーがありつつ "." 始まりでないキーもあるなら、"invalid config"例外を投げる。
  6. もし、 "exports" が string または "." 始まりのキーが1つもない object なら、それの値を "." として扱う。
  7. もし、 サブパスが "." で "exports" が "." エントリーを持たないなら、return
  8. "exports" の中からサブパスで始まる最も長いキーを探す。
  9. もし、キーが見つからないなら、 "not found" 例外を投げる。
  10. let RESOLVED = fileURLToPath(PACKAGE_EXPORTS_TARGET_RESOLVE(pathToFileURL(DIR/name), exports[key], subpath.slice(key.length), ["node", "require"])) (これはESMリゾルバーで定義されたもの)
  11. もしキーが "/" で終わるなら:
  12. それ以外の場合は、
    • a. もし RESOLVED がファイルなら、そのファイル拡張子フォーマットでロードする。 終了
  13. "not found"例外を投げる
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5