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

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

* [Modules | Node.js v13.13.0 Documentation](https://nodejs.org/api/modules.html#modules_all_together)

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

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

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

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

### LOAD_AS_FILE(X)

[LOAD_AS_FILE]: #load_as_filex

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

### LOAD_INDEX(X)

[LOAD_INDEX]: #load_indexx

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)

[LOAD_AS_DIRECTORY]: #load_as_directoryx

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)

[LOAD_NODE_MODULES]: #load_node_modulesx-start

1. let DIRS = [NODE_MODULES_PATHS]\(START)
2. for each DIR in DIRS:
   - a. [LOAD_PACKAGE_EXPORTS]\(DIR, X)
   - b. [LOAD_AS_FILE]\(DIR/X)
   - c. [LOAD_AS_DIRECTORY]\(DIR/X)

### NODE_MODULES_PATHS(START)

[NODE_MODULES_PATHS]: #node_modules_pathsstart

```js
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')`を与えたときに、下記のリストを返す処理です。

```js
[
  '$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)

[LOAD_SELF_REFERENCE]: #load_self_referencex-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)

[LOAD_PACKAGE_EXPORTS]: #load_package_exportsdir-x

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


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. もしキーが "/" で終わるなら:
    - a. [LOAD_AS_FILE]\(RESOLVED)
    - b. [LOAD_AS_DIRECTORY]\(RESOLVED)
12. それ以外の場合は、
    - a. もし RESOLVED がファイルなら、そのファイル拡張子フォーマットでロードする。 __終了__
13. "not found"例外を投げる