Modules: CommonJS modules | Node.js v18.18.2 Documentation
require() 가 호출될 때, 로드될 정확한 파일명을 얻는 방식
require() 함수는 인자로 전달받은 모듈을 찾아 해당 모듈의 module.exports 를 반환한다. 각 모듈들은 Module Wrapper 때문에 원칙적으로 모두 외부에서 접근이 불가능하나(private), module.exports에 담겨 있는 변수 및 함수들은 require()를 통해 접근할 수 있다(public).
require() 함수는 전달받은 인자가 코어 모듈의 이름인지, 절대 경로인지, 상대 경로인지 등을 자동으로 인식해서 가장 적합한 모듈을 로드한다. require(X)에 대해, require() 함수는 다음 알고리즘을 사용해 모듈을 찾는다.
X 파일을 찾는다. 만약 X 파일이 존재한다면 해당 모듈을 로드한다.X 파일이 존재하지 않는다면 X.js 파일을 찾는다. 만약 X.js 파일이 존재한다면 해당 모듈을 로드한다.X.js 파일이 존재하지 않는다면 X.json 파일을 찾는다. 만약 X.json 파일이 존재한다면 해당 json 파일을 파싱한다.X.json 파일이 존재하지 않는다면 X.node 파일을 찾는다. 만약 X.node 파일이 존재한다면 해당 binary addon을 로드한다.X 파일을 찾는다. 만약 X 파일이 존재한다면 해당 모듈을 로드한다.X 파일이 존재하지 않는다면 X.js 파일을 찾는다. 만약 X.js 파일이 존재한다면 해당 모듈을 로드한다.X.js 파일이 존재하지 않는다면 X.json 파일을 찾는다. 만약 X.json 파일이 존재한다면 해당 json 파일을 파싱한다.X.json 파일이 존재하지 않는다면 X.node 파일을 찾는다. 만약 X.node 파일이 존재한다면 해당 binary addon을 로드한다.node_modules 디렉토리에서 X를 찾는다. 만약 X 모듈이 존재한다면 해당 모듈을 로드한다.node_modules 디렉토리에서 X를 찾는다. 이렇게 X를 찾을 때까지 계속해서 부모 디렉토리로의 node_modules 디렉토리를 탐색한다. 예를 들어 /home/user/projects/foo.js 파일에서 require("bar.js")를 호출하면, /home/user/projects/node_modules/bar.js, /home/user/node_modules/bar.js, /home/node_modules/bar.js, /node_modules/bar.js이 있는지 순차적으로 탐색한다.위 알고리즘에서 볼 수 있듯이 require() 함수는 파일을 찾을 때 자동으로 .js, .json, .node 확장자를 모두 검색하므로 require()를 사용할 때는 확장자는 생략해도 된다. 위의 예시 코드에서도 모두 확장자를 생략하고 있다.
참고로 아래 ES6 모듈 시스템에서 사용하는 .mjs 확장자 파일은 require() 함수로 로딩할 수 없다.
require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with '/'
a. set Y to be the file system root
3. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
c. THROW "not found"
4. If X begins with '#'
a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as its file extension format. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP
LOAD_INDEX(X)
1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. If "main" is a falsy value, GOTO 2.
c. let M = X + (json main field)
d. LOAD_AS_FILE(M)
e. LOAD_INDEX(M)
f. LOAD_INDEX(X) DEPRECATED
g. THROW "not found"
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_PACKAGE_EXPORTS(X, DIR)
b. LOAD_AS_FILE(DIR/X)
c. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIR + DIRS
d. let I = I - 1
5. return DIRS + GLOBAL_FOLDERS
LOAD_PACKAGE_IMPORTS(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "imports" is null or undefined, return.
4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
["node", "require"]) defined in the ESM resolver.
5. RESOLVE_ESM_MATCH(MATCH).
LOAD_PACKAGE_EXPORTS(X, DIR)
1. Try to interpret X as a combination of NAME and SUBPATH where the name
may have a @scope/ prefix and the subpath begins with a slash (`/`).
2. If X does not match this pattern or DIR/NAME/package.json is not a file,
return.
3. Parse DIR/NAME/package.json, and look for "exports" field.
4. If "exports" is null or undefined, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
`package.json` "exports", ["node", "require"]) defined in the ESM resolver.
6. RESOLVE_ESM_MATCH(MATCH)
LOAD_PACKAGE_SELF(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "exports" is null or undefined, return.
4. If the SCOPE/package.json "name" is not the first segment of X, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
"." + X.slice("name".length), `package.json` "exports", ["node", "require"])
defined in the ESM resolver.
6. RESOLVE_ESM_MATCH(MATCH)
RESOLVE_ESM_MATCH(MATCH)
1. let RESOLVED_PATH = fileURLToPath(MATCH)
2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension
format. STOP
3. THROW "not found"