모듈: node:module
API
추가됨: v0.3.7
Module
객체
Module
인스턴스와 상호 작용할 때 일반적인 유틸리티 메서드를 제공합니다. CommonJS 모듈에서 자주 볼 수 있는 module
변수입니다. import 'node:module'
또는 require('node:module')
을 통해 액세스합니다.
module.builtinModules
[히스토리]
버전 | 변경 사항 |
---|---|
v23.5.0 | 이 목록에는 이제 접두사 전용 모듈도 포함됩니다. |
v9.3.0, v8.10.0, v6.13.0 | 추가됨: v9.3.0, v8.10.0, v6.13.0 |
Node.js에서 제공하는 모든 모듈의 이름 목록입니다. 모듈이 타사에서 유지 관리되는지 여부를 확인하는 데 사용할 수 있습니다.
이 컨텍스트에서 module
은 모듈 래퍼에서 제공하는 객체와 동일하지 않습니다. 이에 액세스하려면 Module
모듈을 require합니다.
// module.mjs
// ECMAScript 모듈에서
import { builtinModules as builtin } from 'node:module'
// module.cjs
// CommonJS 모듈에서
const builtin = require('node:module').builtinModules
module.createRequire(filename)
추가됨: v12.2.0
filename
<string> | <URL> require 함수를 구성하는 데 사용할 파일 이름입니다. 파일 URL 객체, 파일 URL 문자열 또는 절대 경로 문자열이어야 합니다.- 반환값: <require> Require 함수
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
// sibling-module.js는 CommonJS 모듈입니다.
const siblingModule = require('./sibling-module')
module.findPackageJSON(specifier[, base])
추가됨: v23.2.0
specifier
<string> | <URL> 가져올package.json
이 있는 모듈에 대한 지정자입니다. 베어 지정자를 전달할 때는 패키지 루트의package.json
이 반환됩니다. 상대 지정자 또는 절대 지정자를 전달할 때는 가장 가까운 상위package.json
이 반환됩니다.base
<string> | <URL> 포함 모듈의 절대 위치(file:
URL 문자열 또는 FS 경로)입니다. CJS의 경우__filename
(__dirname
이 아님!)을 사용하고, ESM의 경우import.meta.url
을 사용합니다.specifier
가 절대 지정자인 경우 전달할 필요가 없습니다.- 반환값: <string> | <undefined>
package.json
이 발견되면 경로를 반환합니다.startLocation
이 패키지인 경우 패키지의 루트package.json
; 상대 또는 확인되지 않은 경우startLocation
과 가장 가까운package.json
을 반환합니다.
/path/to/project
├ packages/
├ bar/
├ bar.js
└ package.json // name = '@foo/bar'
└ qux/
├ node_modules/
└ some-package/
└ package.json // name = 'some-package'
├ qux.js
└ package.json // name = '@foo/qux'
├ main.js
└ package.json // name = '@foo'
// /path/to/project/packages/bar/bar.js
import { findPackageJSON } from 'node:module'
findPackageJSON('..', import.meta.url)
// '/path/to/project/package.json'
// 절대 지정자를 대신 전달할 때도 동일한 결과가 나옵니다.
findPackageJSON(new URL('../', import.meta.url))
findPackageJSON(import.meta.resolve('../'))
findPackageJSON('some-package', import.meta.url)
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// 절대 지정자를 전달할 때, 확인된 모듈이 중첩된 `package.json`이 있는 하위 폴더 안에 있는 경우 다른 결과를 얻을 수 있습니다.
findPackageJSON(import.meta.resolve('some-package'))
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'
findPackageJSON('@foo/qux', import.meta.url)
// '/path/to/project/packages/qux/package.json'
// /path/to/project/packages/bar/bar.js
const { findPackageJSON } = require('node:module')
const { pathToFileURL } = require('node:url')
const path = require('node:path')
findPackageJSON('..', __filename)
// '/path/to/project/package.json'
// 절대 지정자를 대신 전달할 때도 동일한 결과가 나옵니다.
findPackageJSON(pathToFileURL(path.join(__dirname, '..')))
findPackageJSON('some-package', __filename)
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// 절대 지정자를 전달할 때, 확인된 모듈이 중첩된 `package.json`이 있는 하위 폴더 안에 있는 경우 다른 결과를 얻을 수 있습니다.
findPackageJSON(pathToFileURL(require.resolve('some-package')))
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'
findPackageJSON('@foo/qux', __filename)
// '/path/to/project/packages/qux/package.json'
module.isBuiltin(moduleName)
추가됨: v18.6.0, v16.17.0
import { isBuiltin } from 'node:module'
isBuiltin('node:fs') // true
isBuiltin('fs') // true
isBuiltin('wss') // false
module.register(specifier[, parentURL][, options])
[히스토리]
버전 | 변경 사항 |
---|---|
v20.8.0, v18.19.0 | WHATWG URL 인스턴스 지원 추가 |
v20.6.0, v18.19.0 | 추가됨: v20.6.0, v18.19.0 |
specifier
<string> | <URL> 등록할 사용자 정의 훅;import()
에 전달될 문자열과 동일해야 하며, 상대 경로인 경우parentURL
을 기준으로 확인됩니다.parentURL
<string> | <URL>import.meta.url
과 같은 기준 URL을 기준으로specifier
를 확인하려면 여기에 해당 URL을 전달할 수 있습니다. 기본값:'data:'
options
<Object>parentURL
<string> | <URL>import.meta.url
과 같은 기준 URL을 기준으로specifier
를 확인하려면 여기에 해당 URL을 전달할 수 있습니다. 두 번째 인수로parentURL
이 제공된 경우 이 속성은 무시됩니다. 기본값:'data:'
data
<any>initialize
훅에 전달할 임의의 복제 가능한 JavaScript 값.transferList
<Object[]>initialize
훅에 전달할 전송 가능한 객체.
Node.js 모듈 확인 및 로딩 동작을 사용자 지정하는 훅을 내보내는 모듈을 등록합니다. 사용자 지정 훅을 참조하십시오.
module.registerHooks(options)
추가됨: v23.5.0
[Stable: 1 - Experimental]
Stable: 1 Stability: 1.1 - 활성 개발 중
options
<Object>load
<Function> | <undefined> load hook 참조. 기본값:undefined
.resolve
<Function> | <undefined> resolve hook 참조. 기본값:undefined
.
Node.js 모듈 해석 및 로딩 동작을 사용자 지정하는 hook을 등록합니다. 사용자 지정 hook 참조.
module.stripTypeScriptTypes(code[, options])
추가됨: v23.2.0
[Stable: 1 - Experimental]
Stable: 1 Stability: 1.1 - 활성 개발 중
code
<string> 형식 주석을 제거할 코드.options
<Object>반환값: <string> 형식 주석이 제거된 코드.
module.stripTypeScriptTypes()
는 TypeScript 코드에서 형식 주석을 제거합니다.vm.runInContext()
또는vm.compileFunction()
을 사용하여 실행하기 전에 TypeScript 코드에서 형식 주석을 제거하는 데 사용할 수 있습니다. 기본적으로 코드에Enums
와 같이 변환이 필요한 TypeScript 기능이 포함되어 있으면 오류가 발생합니다. 자세한 내용은 type-stripping을 참조하십시오.mode
가'transform'
이면 TypeScript 기능을 JavaScript로 변환합니다. 자세한 내용은 TypeScript 기능 변환을 참조하십시오.mode
가'strip'
이면 위치가 보존되므로 소스 맵이 생성되지 않습니다.sourceMap
이 제공되면mode
가'strip'
일 때 오류가 발생합니다.
경고: TypeScript 파서의 변경으로 인해 이 함수의 출력은 Node.js 버전 간에 안정적이지 않을 수 있습니다.
import { stripTypeScriptTypes } from 'node:module'
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code)
console.log(strippedCode)
// 출력: const a = 1;
const { stripTypeScriptTypes } = require('node:module')
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code)
console.log(strippedCode)
// 출력: const a = 1;
sourceUrl
이 제공되면 출력의 끝에 주석으로 추가됩니다.
import { stripTypeScriptTypes } from 'node:module'
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' })
console.log(strippedCode)
// 출력: const a = 1\n\n//# sourceURL=source.ts;
const { stripTypeScriptTypes } = require('node:module')
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' })
console.log(strippedCode)
// 출력: const a = 1\n\n//# sourceURL=source.ts;
mode
가 'transform'
이면 코드가 JavaScript로 변환됩니다.
import { stripTypeScriptTypes } from 'node:module'
const code = `
namespace MathUtil {
export const add = (a: number, b: number) => a + b;
}`
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true })
console.log(strippedCode)
// 출력:
// var MathUtil;
// (function(MathUtil) {
// MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...
const { stripTypeScriptTypes } = require('node:module')
const code = `
namespace MathUtil {
export const add = (a: number, b: number) => a + b;
}`
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true })
console.log(strippedCode)
// 출력:
// var MathUtil;
// (function(MathUtil) {
// MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...
module.syncBuiltinESMExports()
추가됨: v12.12.0
module.syncBuiltinESMExports()
메서드는 기본 제공 ES 모듈의 모든 라이브 바인딩을 CommonJS 내보내기의 속성과 일치하도록 업데이트합니다. ES 모듈에서 내보낸 이름을 추가하거나 제거하지 않습니다.
const fs = require('node:fs')
const assert = require('node:assert')
const { syncBuiltinESMExports } = require('node:module')
fs.readFile = newAPI
delete fs.readFileSync
function newAPI() {
// ...
}
fs.newAPI = newAPI
syncBuiltinESMExports()
import('node:fs').then(esmFS => {
// 기존 readFile 속성을 새 값과 동기화합니다.
assert.strictEqual(esmFS.readFile, newAPI)
// readFileSync가 필요한 fs에서 삭제되었습니다.
assert.strictEqual('readFileSync' in fs, false)
// syncBuiltinESMExports()는 esmFS에서 readFileSync를 제거하지 않습니다.
assert.strictEqual('readFileSync' in esmFS, true)
// syncBuiltinESMExports()는 이름을 추가하지 않습니다.
assert.strictEqual(esmFS.newAPI, undefined)
})
모듈 컴파일 캐시
[히스토리]
버전 | 변경 사항 |
---|---|
v22.8.0 | 런타임 액세스를 위한 초기 JavaScript API 추가 |
v22.1.0 | 추가됨: v22.1.0 |
모듈 컴파일 캐시는 module.enableCompileCache()
메서드 또는 NODE_COMPILE_CACHE=dir
환경 변수를 사용하여 활성화할 수 있습니다. 활성화되면 Node.js가 CommonJS 또는 ECMAScript 모듈을 컴파일할 때마다 지정된 디렉토리에 영구적으로 저장된 온디스크 V8 코드 캐시를 사용하여 컴파일 속도를 높입니다. 이로 인해 모듈 그래프의 첫 번째 로드 속도가 느려질 수 있지만, 모듈의 내용이 변경되지 않으면 동일한 모듈 그래프를 이후에 로드할 때 속도가 크게 향상될 수 있습니다.
디스크에서 생성된 컴파일 캐시를 정리하려면 캐시 디렉토리를 제거하기만 하면 됩니다. 캐시 디렉토리는 동일한 디렉토리가 컴파일 캐시 저장소로 사용될 때 다음에 다시 생성됩니다. 디스크가 오래된 캐시로 가득 차는 것을 방지하려면 os.tmpdir()
아래의 디렉토리를 사용하는 것이 좋습니다. 디렉토리를 지정하지 않고 module.enableCompileCache()
를 호출하여 컴파일 캐시를 활성화하면 Node.js는 NODE_COMPILE_CACHE=dir
환경 변수가 설정된 경우 이를 사용하거나 그렇지 않으면 path.join(os.tmpdir(), 'node-compile-cache')
를 기본값으로 사용합니다. 실행 중인 Node.js 인스턴스에서 사용하는 컴파일 캐시 디렉토리를 찾으려면 module.getCompileCacheDir()
를 사용합니다.
현재 V8 JavaScript 코드 적용 범위와 함께 컴파일 캐시를 사용하는 경우 V8에서 수집하는 적용 범위는 코드 캐시에서 역직렬화된 함수에서 정확도가 떨어질 수 있습니다. 정확한 적용 범위를 생성하려면 테스트를 실행할 때 이 기능을 끄는 것이 좋습니다.
활성화된 모듈 컴파일 캐시는 NODE_DISABLE_COMPILE_CACHE=1
환경 변수를 사용하여 비활성화할 수 있습니다. 이는 컴파일 캐시가 예기치 않거나 원치 않는 동작(예: 정확도가 떨어지는 테스트 적용 범위)을 유발하는 경우 유용할 수 있습니다.
하나의 Node.js 버전에서 생성된 컴파일 캐시는 다른 Node.js 버전에서 재사용할 수 없습니다. 다른 Node.js 버전에서 생성된 캐시는 동일한 기본 디렉토리를 사용하여 캐시를 유지하는 경우 별도로 저장되므로 공존할 수 있습니다.
현재 컴파일 캐시가 활성화되고 모듈이 새로 로드되면 코드 캐시가 컴파일된 코드에서 즉시 생성되지만 Node.js 인스턴스가 종료되려고 할 때만 디스크에 기록됩니다. 이것은 변경될 수 있습니다. 애플리케이션이 다른 Node.js 인스턴스를 생성하고 상위 인스턴스가 종료되기 오래 전에 캐시를 공유하도록 하려면 누적된 코드 캐시가 디스크에 플러시되도록 하기 위해 module.flushCompileCache()
메서드를 사용할 수 있습니다.
module.constants.compileCacheStatus
추가됨: v22.8.0
다음 상수는 module.enableCompileCache()
함수가 반환하는 객체의 status
필드에 반환되어 모듈 컴파일 캐시 활성화 시도 결과를 나타냅니다.
상수 | 설명 |
---|---|
ENABLED | Node.js가 컴파일 캐시를 성공적으로 활성화했습니다. 컴파일 캐시를 저장하는 데 사용된 디렉토리는 반환된 객체의 directory 필드에 반환됩니다. |
ALREADY_ENABLED | 컴파일 캐시가 이미 활성화되어 있습니다. 이전에 module.enableCompileCache() 함수를 호출하거나 NODE_COMPILE_CACHE=dir 환경 변수를 통해 활성화되었을 수 있습니다. 컴파일 캐시를 저장하는 데 사용된 디렉토리는 반환된 객체의 directory 필드에 반환됩니다. |
FAILED | Node.js가 컴파일 캐시를 활성화하지 못했습니다. 지정된 디렉토리를 사용할 권한이 없거나 다양한 종류의 파일 시스템 오류가 원인일 수 있습니다. 오류에 대한 자세한 내용은 반환된 객체의 message 필드에 반환됩니다. |
DISABLED | NODE_DISABLE_COMPILE_CACHE=1 환경 변수가 설정되어 있기 때문에 Node.js가 컴파일 캐시를 활성화할 수 없습니다. |
module.enableCompileCache([cacheDir])
추가됨: v22.8.0
cacheDir
<string> | <undefined> 컴파일 캐시를 저장/검색할 디렉토리를 지정하는 선택적 경로입니다.- 반환값: <Object>
status
<integer>module.constants.compileCacheStatus
중 하나입니다.message
<string> | <undefined> Node.js가 컴파일 캐시를 활성화할 수 없는 경우 오류 메시지를 포함합니다.status
가module.constants.compileCacheStatus.FAILED
인 경우에만 설정됩니다.directory
<string> | <undefined> 컴파일 캐시가 활성화된 경우 컴파일 캐시가 저장된 디렉토리를 포함합니다.status
가module.constants.compileCacheStatus.ENABLED
또는module.constants.compileCacheStatus.ALREADY_ENABLED
인 경우에만 설정됩니다.
현재 Node.js 인스턴스에서 모듈 컴파일 캐시를 활성화합니다.
cacheDir
이 지정되지 않은 경우 Node.js는 NODE_COMPILE_CACHE=dir
환경 변수에 지정된 디렉토리를 사용하거나(설정된 경우) 그렇지 않으면 path.join(os.tmpdir(), 'node-compile-cache')
를 사용합니다. 일반적인 사용 사례에서는 cacheDir
을 지정하지 않고 module.enableCompileCache()
를 호출하는 것이 좋습니다. 그러면 필요할 때 NODE_COMPILE_CACHE
환경 변수로 디렉토리를 재정의할 수 있습니다.
컴파일 캐시는 애플리케이션의 기능에 필요하지 않은 조용한 최적화이므로 이 메서드는 컴파일 캐시를 활성화할 수 없는 경우 예외를 throw하지 않도록 설계되었습니다. 대신, 디버깅에 도움이 되는 오류 메시지를 message
필드에 포함하는 객체를 반환합니다. 컴파일 캐시가 성공적으로 활성화되면 반환된 객체의 directory
필드에는 컴파일 캐시가 저장된 디렉토리의 경로가 포함됩니다. 반환된 객체의 status
필드는 모듈 컴파일 캐시 활성화 시도 결과를 나타내는 module.constants.compileCacheStatus
값 중 하나입니다.
이 메서드는 현재 Node.js 인스턴스에만 영향을 미칩니다. 하위 작업자 스레드에서 활성화하려면 하위 작업자 스레드에서도 이 메서드를 호출하거나 process.env.NODE_COMPILE_CACHE
값을 컴파일 캐시 디렉토리로 설정하여 하위 작업자에게 동작을 상속받도록 합니다. 디렉토리는 이 메서드에서 반환된 directory
필드 또는 module.getCompileCacheDir()
를 사용하여 얻을 수 있습니다.
module.flushCompileCache()
추가됨: v23.0.0
현재 Node.js 인스턴스에 이미 로드된 모듈에서 누적된 모듈 컴파일 캐시를 디스크에 플러시합니다. 모든 플러시 파일 시스템 작업이 끝난 후 반환되며, 성공 여부와 관계없습니다. 오류가 발생하더라도 컴파일 캐시 누락이 애플리케이션의 실제 작동에 영향을 주지 않으므로 자동으로 실패합니다.
module.getCompileCacheDir()
추가됨: v22.8.0
- 반환값: <string> | <undefined> 활성화된 경우 모듈 컴파일 캐시 디렉터리의 경로, 그렇지 않으면
undefined
.
사용자 정의 훅
[히스토리]
버전 | 변경 사항 |
---|---|
v23.5.0 | 동기 및 스레드 내 훅 지원 추가 |
v20.6.0, v18.19.0 | globalPreload 를 대체할 initialize 훅 추가 |
v18.6.0, v16.17.0 | 로더 체이닝 지원 추가 |
v16.12.0 | getFormat , getSource , transformSource , globalPreload 제거; load 훅 및 getGlobalPreload 훅 추가 |
v8.8.0 | 추가됨: v8.8.0 |
[안정성: 1 - 실험적]
안정성: 1.2 - 출시 후보(비동기 버전) 안정성: 1.1 - 개발 중(동기 버전)
현재 지원되는 두 가지 유형의 모듈 사용자 정의 훅이 있습니다.
활성화
모듈 확인 및 로딩은 다음을 통해 사용자 지정할 수 있습니다.
훅은 --import
또는 --require
플래그를 사용하여 애플리케이션 코드가 실행되기 전에 등록할 수 있습니다.
node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js
// register-hooks.js
// 이 파일은 최상위 await를 포함하지 않는 경우에만 require()-될 수 있습니다.
// 전용 스레드에서 비동기 훅을 등록하려면 module.register()를 사용합니다.
import { register } from 'node:module'
register('./hooks.mjs', import.meta.url)
// register-hooks.js
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
// 전용 스레드에서 비동기 훅을 등록하려면 module.register()를 사용합니다.
register('./hooks.mjs', pathToFileURL(__filename))
// 메인 스레드에서 동기 훅을 등록하려면 module.registerHooks()를 사용합니다.
import { registerHooks } from 'node:module'
registerHooks({
resolve(specifier, context, nextResolve) {
/* 구현 */
},
load(url, context, nextLoad) {
/* 구현 */
},
})
// 메인 스레드에서 동기 훅을 등록하려면 module.registerHooks()를 사용합니다.
const { registerHooks } = require('node:module')
registerHooks({
resolve(specifier, context, nextResolve) {
/* 구현 */
},
load(url, context, nextLoad) {
/* 구현 */
},
})
--import
또는 --require
에 전달된 파일은 종속성의 내보내기일 수도 있습니다.
node --import some-package/register ./my-app.js
node --require some-package/register ./my-app.js
여기서 some-package
는 다음 register-hooks.js
예제와 같이 register()
를 호출하는 파일에 매핑되는 /register
내보내기를 정의하는 "exports"
필드를 가지고 있습니다.
--import
또는 --require
를 사용하면 애플리케이션 파일(애플리케이션의 진입점 포함)이 임포트되기 전에 훅이 등록되고 기본적으로 모든 작업자 스레드에도 등록됩니다.
또는 진입점에서 register()
및 registerHooks()
를 호출할 수 있지만, 훅이 등록된 후 실행되어야 하는 ESM 코드에는 동적 import()
를 사용해야 합니다.
import { register } from 'node:module'
register('http-to-https', import.meta.url)
// 이것은 동적 `import()`이므로 `http-to-https` 훅은
// `./my-app.js`와 그것이 임포트하거나 require하는 다른 모든 파일을 처리하기 위해 실행됩니다.
await import('./my-app.js')
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
register('http-to-https', pathToFileURL(__filename))
// 이것은 동적 `import()`이므로 `http-to-https` 훅은
// `./my-app.js`와 그것이 임포트하거나 require하는 다른 모든 파일을 처리하기 위해 실행됩니다.
import('./my-app.js')
사용자 지정 훅은 등록 후에 로드된 모든 모듈과 import
및 내장 require
를 통해 참조하는 모듈에 대해 실행됩니다. module.createRequire()
를 사용하여 사용자가 만든 require
함수는 동기 훅으로만 사용자 지정할 수 있습니다.
이 예제에서는 http-to-https
훅을 등록하지만, 이후에 임포트된 모듈(이 경우 my-app.js
와 CommonJS 종속성에서 내장 require
를 통해 참조하는 모든 항목)에 대해서만 사용할 수 있습니다.
import('./my-app.js')
가 대신 정적 import './my-app.js'
였다면, 앱은 http-to-https
훅이 등록되기 전에 이미 로드되었을 것입니다. 이는 ES 모듈 사양 때문인데, 정적 임포트는 먼저 트리의 리프에서 평가된 다음 트렁크로 돌아갑니다. my-app.js
내부에 정적 임포트가 있을 수 있으며, 이는 my-app.js
가 동적으로 임포트될 때까지 평가되지 않습니다.
동기 훅을 사용하는 경우 import
, require
및 createRequire()
를 사용하여 생성된 사용자 require
가 모두 지원됩니다.
import { registerHooks, createRequire } from 'node:module'
registerHooks({
/* 동기 훅 구현 */
})
const require = createRequire(import.meta.url)
// 동기 훅은 import, require() 및 createRequire()를 통해 생성된 사용자 require() 함수에 영향을 미칩니다.
await import('./my-app.js')
require('./my-app-2.js')
const { register, registerHooks } = require('node:module')
const { pathToFileURL } = require('node:url')
registerHooks({
/* 동기 훅 구현 */
})
const userRequire = createRequire(__filename)
// 동기 훅은 import, require() 및 createRequire()를 통해 생성된 사용자 require() 함수에 영향을 미칩니다.
import('./my-app.js')
require('./my-app-2.js')
userRequire('./my-app-3.js')
마지막으로, 앱을 실행하기 전에 훅을 등록하는 것만 원하고 그 목적으로 별도의 파일을 만들고 싶지 않다면 data:
URL을 --import
에 전달할 수 있습니다.
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("http-to-https", pathToFileURL("./"));' ./my-app.js
체이닝
register
를 여러 번 호출할 수 있습니다.
// entrypoint.mjs
import { register } from 'node:module'
register('./foo.mjs', import.meta.url)
register('./bar.mjs', import.meta.url)
await import('./my-app.mjs')
// entrypoint.cjs
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
const parentURL = pathToFileURL(__filename)
register('./foo.mjs', parentURL)
register('./bar.mjs', parentURL)
import('./my-app.mjs')
이 예시에서 등록된 훅은 체인을 형성합니다. 이 체인은 후입선출(LIFO) 방식으로 실행됩니다. foo.mjs
와 bar.mjs
모두 resolve
훅을 정의하는 경우, 다음과 같이 호출됩니다(오른쪽에서 왼쪽 순서에 유의): Node의 기본값 ← ./foo.mjs
← ./bar.mjs
( ./bar.mjs
부터 시작하여 ./foo.mjs
, 그 다음 Node.js 기본값). 다른 모든 훅에도 동일하게 적용됩니다.
등록된 훅은 register
자체에도 영향을 미칩니다. 이 예시에서 bar.mjs
는 foo.mjs
에 의해 등록된 훅을 통해 해석되고 로드됩니다(foo
의 훅이 이미 체인에 추가되었기 때문입니다). 이를 통해 이전에 등록된 훅이 JavaScript로 변환되는 한, JavaScript가 아닌 언어로 훅을 작성하는 등의 작업이 가능해집니다.
register
메서드는 훅을 정의하는 모듈 내부에서는 호출할 수 없습니다.
registerHooks
의 체이닝도 비슷하게 작동합니다. 동기 훅과 비동기 훅이 혼합된 경우, 동기 훅은 항상 비동기 훅이 실행되기 전에 실행됩니다. 즉, 마지막으로 실행되는 동기 훅의 다음 훅에는 비동기 훅의 호출이 포함됩니다.
// entrypoint.mjs
import { registerHooks } from 'node:module'
const hook1 = {
/* implementation of hooks */
}
const hook2 = {
/* implementation of hooks */
}
// hook2 run before hook1.
registerHooks(hook1)
registerHooks(hook2)
// entrypoint.cjs
const { registerHooks } = require('node:module')
const hook1 = {
/* implementation of hooks */
}
const hook2 = {
/* implementation of hooks */
}
// hook2 run before hook1.
registerHooks(hook1)
registerHooks(hook2)
모듈 사용자 정의 후크와의 통신
비동기 후크는 애플리케이션 코드를 실행하는 메인 스레드와 별개의 전용 스레드에서 실행됩니다. 즉, 전역 변수를 변경해도 다른 스레드에 영향을 미치지 않으며, 스레드 간 통신에는 메시지 채널을 사용해야 합니다.
register
메서드를 사용하여 initialize
후크에 데이터를 전달할 수 있습니다. 후크에 전달되는 데이터에는 포트와 같은 전송 가능한 객체가 포함될 수 있습니다.
import { register } from 'node:module'
import { MessageChannel } from 'node:worker_threads'
// 이 예제는 메시지 채널을 사용하여 `port2`를 후크에 보내는 방법을 보여줍니다.
const { port1, port2 } = new MessageChannel()
port1.on('message', msg => {
console.log(msg)
})
port1.unref()
register('./my-hooks.mjs', {
parentURL: import.meta.url,
data: { number: 1, port: port2 },
transferList: [port2],
})
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
const { MessageChannel } = require('node:worker_threads')
// 이 예제는 메시지 채널을 사용하여 `port2`를 후크에 보내는 방법을 보여줍니다.
const { port1, port2 } = new MessageChannel()
port1.on('message', msg => {
console.log(msg)
})
port1.unref()
register('./my-hooks.mjs', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
})
동기 모듈 후크는 애플리케이션 코드가 실행되는 것과 같은 스레드에서 실행됩니다. 메인 스레드가 액세스하는 컨텍스트의 전역 변수를 직접 변경할 수 있습니다.
후크
module.register()
에서 허용하는 비동기 후크
register
메서드를 사용하여 후크 집합을 내보내는 모듈을 등록할 수 있습니다. 후크는 모듈 해석 및 로딩 프로세스를 사용자 지정하기 위해 Node.js에서 호출하는 함수입니다. 내보낸 함수는 특정 이름과 시그니처를 가져야 하며, 명명된 내보내기로 내보내야 합니다.
export async function initialize({ number, port }) {
// `register`에서 데이터를 받습니다.
}
export async function resolve(specifier, context, nextResolve) {
// `import` 또는 `require` 지정자를 가져와 URL로 해석합니다.
}
export async function load(url, context, nextLoad) {
// 해석된 URL을 가져와 평가할 소스 코드를 반환합니다.
}
비동기 후크는 애플리케이션 코드가 실행되는 메인 스레드와 분리된 별도의 스레드에서 실행됩니다. 즉, 다른 영역입니다. 후크 스레드는 메인 스레드에서 언제든지 종료될 수 있으므로 비동기 작업(console.log
와 같음)이 완료될 때까지 기다리지 마십시오. 기본적으로 자식 워커로 상속됩니다.
module.registerHooks()
에서 허용하는 동기식 후크
추가됨: v23.5.0
module.registerHooks()
메서드는 동기식 후크 함수를 허용합니다. 후크 구현자는 module.registerHooks()
호출 직전에 초기화 코드를 직접 실행할 수 있으므로 initialize()
는 지원되지 않으며 필요하지 않습니다.
function resolve(specifier, context, nextResolve) {
// `import` 또는 `require` 지정자를 가져와 URL로 확인합니다.
}
function load(url, context, nextLoad) {
// 확인된 URL을 가져와 평가할 소스 코드를 반환합니다.
}
동기식 후크는 모듈이 로드되는 것과 같은 스레드와 같은 영역에서 실행됩니다. 비동기식 후크와 달리 기본적으로 자식 워커 스레드에 상속되지 않지만, --import
또는 --require
로 미리 로드된 파일을 사용하여 후크가 등록된 경우 자식 워커 스레드는 process.execArgv
상속을 통해 미리 로드된 스크립트를 상속받을 수 있습니다. 자세한 내용은 Worker
문서를 참조하십시오.
동기식 후크에서 사용자는 모듈 코드에서 console.log()
가 완료되는 것과 같은 방식으로 console.log()
가 완료될 것으로 예상할 수 있습니다.
후크의 관례
후크는 체인의 일부이며, 해당 체인이 하나의 사용자 지정(사용자 제공) 후크와 항상 존재하는 기본 후크로만 구성되는 경우에도 마찬가지입니다. 후크 함수는 중첩됩니다. 각 함수는 항상 일반 객체를 반환해야 하며, 체인은 각 함수가 후속 로더의 후크(LIFO 순서)에 대한 참조인 next<hookName>()
을 호출하는 결과로 발생합니다.
필요한 속성이 없는 값을 반환하는 후크는 예외를 발생시킵니다. next<hookName>()
을 호출하지 않고 그리고 shortCircuit: true
를 반환하지 않고 반환하는 후크도 예외를 발생시킵니다. 이러한 오류는 의도하지 않은 체인 중단을 방지하는 데 도움이 됩니다. 체인이 사용자의 후크에서 의도적으로 끝남을 알리려면 후크에서 shortCircuit: true
를 반환하십시오.
initialize()
추가됨: v20.6.0, v18.19.0
data
<any>register(loader, import.meta.url, { data })
의 데이터.
initialize
훅은 register
에서만 허용됩니다. 동기 훅에 대한 초기화는 registerHooks()
호출 전에 직접 실행될 수 있으므로 registerHooks()
는 이를 지원하거나 필요로 하지 않습니다.
initialize
훅은 훅 모듈이 초기화될 때 훅 스레드에서 실행되는 사용자 정의 함수를 정의하는 방법을 제공합니다. 초기화는 register
를 통해 훅 모듈이 등록될 때 발생합니다.
이 훅은 포트 및 기타 전송 가능한 객체를 포함하여 register
호출에서 데이터를 수신할 수 있습니다. initialize
의 반환 값은 <Promise>일 수 있으며, 이 경우 주 애플리케이션 스레드 실행이 재개되기 전에 대기됩니다.
모듈 사용자 지정 코드:
// path-to-my-hooks.js
export async function initialize({ number, port }) {
port.postMessage(`increment: ${number + 1}`)
}
호출자 코드:
import assert from 'node:assert'
import { register } from 'node:module'
import { MessageChannel } from 'node:worker_threads'
// 이 예제는 메시지 채널을 사용하여 `port2`를 `initialize` 훅으로 보냄으로써 메인(애플리케이션) 스레드와 훅 스레드에서 실행되는 훅 간에 통신하는 방법을 보여줍니다.
const { port1, port2 } = new MessageChannel()
port1.on('message', msg => {
assert.strictEqual(msg, 'increment: 2')
})
port1.unref()
register('./path-to-my-hooks.js', {
parentURL: import.meta.url,
data: { number: 1, port: port2 },
transferList: [port2],
})
const assert = require('node:assert')
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
const { MessageChannel } = require('node:worker_threads')
// 이 예제는 메시지 채널을 사용하여 `port2`를 `initialize` 훅으로 보냄으로써 메인(애플리케이션) 스레드와 훅 스레드에서 실행되는 훅 간에 통신하는 방법을 보여줍니다.
const { port1, port2 } = new MessageChannel()
port1.on('message', msg => {
assert.strictEqual(msg, 'increment: 2')
})
port1.unref()
register('./path-to-my-hooks.js', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
})
resolve(specifier, context, nextResolve)
[히스토리]
버전 | 변경 사항 |
---|---|
v23.5.0 | 동기 및 스레드 내 후크 지원 추가 |
v21.0.0, v20.10.0, v18.19.0 | context.importAssertions 속성이 context.importAttributes 로 대체되었습니다. 이전 이름을 사용해도 여전히 지원되지만 실험적인 경고가 발생합니다. |
v18.6.0, v16.17.0 | resolve 후크 체이닝 지원 추가. 각 후크는 nextResolve() 를 호출하거나 반환 값에 shortCircuit 속성을 true 로 설정해야 합니다. |
v17.1.0, v16.14.0 | import assertions 지원 추가 |
specifier
<문자열>context
<객체>nextResolve
<함수> 체인의 다음resolve
후크 또는 마지막 사용자 제공resolve
후크 이후의 Node.js 기본resolve
후크반환값: <객체> | <Promise> 비동기 버전은 다음 속성을 포함하는 객체 또는 해당 객체로 확인되는
Promise
를 사용합니다. 동기 버전은 동기적으로 반환되는 객체만 허용합니다.
resolve
후크 체인은 Node.js에 지정된 import
문이나 표현식 또는 require
호출을 찾고 캐싱하는 방법을 알려주는 역할을 합니다. load
후크에 대한 힌트로 형식(예: 'module'
)을 선택적으로 반환할 수 있습니다. 형식이 지정되면 load
후크는 최종 format
값을 제공하는 역할을 하며( resolve
에서 제공하는 힌트를 무시할 수 있음) resolve
가 format
을 제공하는 경우 Node.js 기본 load
후크에 값을 전달하기 위해서라도 사용자 지정 load
후크가 필요합니다.
import 유형 속성은 로드된 모듈을 내부 모듈 캐시에 저장하기 위한 캐시 키의 일부입니다. resolve
후크는 모듈을 소스 코드에 있는 것과 다른 속성으로 캐시해야 하는 경우 importAttributes
객체를 반환하는 역할을 합니다.
context
의 conditions
속성은 이 확인 요청에 대한 패키지 내보내기 조건과 일치하는 데 사용되는 조건의 배열입니다. 다른 곳에서 조건부 매핑을 조회하거나 기본 확인 논리를 호출할 때 목록을 수정하는 데 사용할 수 있습니다.
현재 패키지 내보내기 조건은 항상 후크에 전달되는 context.conditions
배열에 있습니다. defaultResolve
를 호출할 때 기본 Node.js 모듈 지정자 확인 동작을 보장하려면, defaultResolve
에 전달되는 context.conditions
배열에는 원래 resolve
후크에 전달된 context.conditions
배열의 모든 요소가 포함되어야 합니다.
// module.register()에서 허용하는 비동기 버전입니다.
export async function resolve(specifier, context, nextResolve) {
const { parentURL = null } = context
if (Math.random() > 0.5) {
// 특정 조건
// 일부 또는 모든 지정자에 대해 확인을 위한 사용자 지정 로직을 수행합니다.
// 항상 {url: <문자열>} 형식의 객체를 반환합니다.
return {
shortCircuit: true,
url: parentURL ? new URL(specifier, parentURL).href : new URL(specifier).href,
}
}
if (Math.random() < 0.5) {
// 다른 조건
// `defaultResolve`를 호출할 때 인수를 수정할 수 있습니다. 이 경우 조건부 내보내기를 일치시키기 위한 다른 값을 추가합니다.
return nextResolve(specifier, {
...context,
conditions: [...context.conditions, 'another-condition'],
})
}
// 체인의 다음 후크로 위임합니다. 이 후크는 마지막 사용자 지정 로더인 경우 Node.js 기본 확인입니다.
return nextResolve(specifier)
}
// module.registerHooks()에서 허용하는 동기 버전입니다.
function resolve(specifier, context, nextResolve) {
// 위의 비동기 resolve()와 유사합니다. 비동기 로직이 없기 때문입니다.
}
load(url, context, nextLoad)
[히스토리]
버전 | 변경 사항 |
---|---|
v23.5.0 | 동기 및 스레드 내 버전 지원 추가 |
v20.6.0 | commonjs 형식의 source 지원 추가 |
v18.6.0, v16.17.0 | 로드 훅 체이닝 지원 추가. 각 훅은 nextLoad() 를 호출하거나 반환 값에 shortCircuit 속성을 true 로 설정해야 함 |
url
<문자열>resolve
체인에서 반환된 URLcontext
<객체>nextLoad
<함수> 체인의 후속load
훅 또는 마지막 사용자 제공load
훅 이후의 Node.js 기본load
훅반환값: <객체> | <Promise> 비동기 버전은 다음 속성을 포함하는 객체 또는 해당 객체로 확인되는
Promise
를 사용합니다. 동기 버전은 동기적으로 반환된 객체만 허용합니다.format
<문자열>shortCircuit
<정의되지 않음> | <부울> 이 훅이load
훅 체인을 종료하려는 신호입니다. 기본값:false
source
<문자열> | <ArrayBuffer> | <TypedArray> Node.js에서 평가할 소스
load
훅은 URL을 해석, 검색 및 구문 분석하는 방법을 정의하는 사용자 지정 방법을 제공합니다. 또한 가져오기 속성의 유효성을 검사합니다.
format
의 최종 값은 다음 중 하나여야 합니다.
format | 설명 | load 에서 반환된 source 에 허용되는 유형 |
---|---|---|
'builtin' | Node.js 내장 모듈 로드 | 해당 없음 |
'commonjs' | Node.js CommonJS 모듈 로드 | { 문자열 , ArrayBuffer , TypedArray , null , undefined } |
'json' | JSON 파일 로드 | { 문자열 , ArrayBuffer , TypedArray } |
'module' | ES 모듈 로드 | { 문자열 , ArrayBuffer , TypedArray } |
'wasm' | WebAssembly 모듈 로드 | { ArrayBuffer , TypedArray } |
'builtin'
유형의 경우 source
값은 무시됩니다. 현재 Node.js 내장(코어) 모듈의 값을 바꿀 수 없기 때문입니다.
비동기 load
훅의 주의 사항
비동기 load
훅을 사용할 때, 'commonjs'
에 대한 source
를 생략하는 것과 제공하는 것은 매우 다른 효과를 갖습니다.
source
를 제공하면, 이 모듈의 모든require
호출은 등록된resolve
및load
훅을 사용하여 ESM 로더에 의해 처리되고; 이 모듈의 모든require.resolve
호출은 등록된resolve
훅을 사용하여 ESM 로더에 의해 처리됩니다; CommonJS API의 하위 집합만 사용 가능하며(예:require.extensions
,require.cache
,require.resolve.paths
없음) CommonJS 모듈 로더에 대한 몽키 패칭은 적용되지 않습니다.source
가undefined
또는null
이면 CommonJS 모듈 로더에 의해 처리되고require
/require.resolve
호출은 등록된 훅을 통과하지 않습니다.nullish
source
에 대한 이 동작은 임시적인 것입니다. 미래에는nullish
source
가 지원되지 않습니다.
이러한 주의 사항은 동기 load
훅에는 적용되지 않습니다. 동기 load
훅의 경우 사용자 정의된 CommonJS 모듈에 사용 가능한 CommonJS API의 전체 집합이 제공되며, require
/require.resolve
는 항상 등록된 훅을 통과합니다.
load
체인의 마지막 훅에 대한 next
값인 Node.js 내부 비동기 load
구현은 이전과의 호환성을 위해 format
이 'commonjs'
일 때 source
에 대해 null
을 반환합니다. 기본 동작이 아닌 동작을 선택하는 예제 훅은 다음과 같습니다.
import { readFile } from 'node:fs/promises'
// module.register()에서 허용하는 비동기 버전. module.registerSync()에서 허용하는 동기 버전에는 이 수정이 필요하지 않습니다.
export async function load(url, context, nextLoad) {
const result = await nextLoad(url, context)
if (result.format === 'commonjs') {
result.source ??= await readFile(new URL(result.responseURL ?? url))
}
return result
}
이는 동기 load
훅에도 적용되지 않습니다. 이 경우 반환되는 source
는 모듈 형식에 관계없이 다음 훅에서 로드된 소스 코드를 포함합니다.
- 특정
ArrayBuffer
객체는SharedArrayBuffer
입니다. - 특정
TypedArray
객체는Uint8Array
입니다.
텍스트 기반 형식(즉, 'json'
, 'module'
)의 소스 값이 문자열이 아니면 util.TextDecoder
를 사용하여 문자열로 변환됩니다.
load
훅은 확인된 URL의 소스 코드를 검색하는 사용자 정의 메서드를 정의하는 방법을 제공합니다. 이를 통해 로더는 디스크에서 파일을 읽는 것을 피할 수 있습니다. 또한 인식되지 않는 형식을 지원되는 형식으로 매핑하는 데 사용할 수도 있습니다(예: yaml
을 module
로).
// module.register()에서 허용하는 비동기 버전.
export async function load(url, context, nextLoad) {
const { format } = context
if (Math.random() > 0.5) {
// 어떤 조건
/*
일부 또는 모든 URL에 대해 소스를 검색하는 사용자 정의 로직을 수행합니다.
항상 다음 형식의 객체를 반환합니다. {
format: <string>,
source: <string|buffer>,
}.
*/
return {
format,
shortCircuit: true,
source: '...',
}
}
// 체인의 다음 훅으로 위임합니다.
return nextLoad(url)
}
// module.registerHooks()에서 허용하는 동기 버전.
function load(url, context, nextLoad) {
// 위의 비동기 load()와 유사합니다. 비동기 로직이 없기 때문입니다.
}
더욱 고급 시나리오에서는 이를 사용하여 지원되지 않는 소스를 지원되는 소스로 변환할 수도 있습니다(예제 참조).
예시
다양한 모듈 사용자 정의 후크는 Node.js 코드 로딩 및 평가 동작을 광범위하게 사용자 정의하는 데 함께 사용할 수 있습니다.
HTTPS에서 가져오기
다음 후크는 이러한 지정자에 대한 기본적인 지원을 활성화하는 후크를 등록합니다. Node.js 코어 기능에 대한 상당한 개선으로 보일 수 있지만, 이러한 후크를 실제로 사용하는 데는 상당한 단점이 있습니다. 디스크에서 파일을 로드하는 것보다 성능이 훨씬 느리고, 캐싱이 없으며, 보안이 없습니다.
// https-hooks.mjs
import { get } from 'node:https'
export function load(url, context, nextLoad) {
// 네트워크를 통해 JavaScript를 로드하려면 가져와서 반환해야 합니다.
if (url.startsWith('https://')) {
return new Promise((resolve, reject) => {
get(url, res => {
let data = ''
res.setEncoding('utf8')
res.on('data', chunk => (data += chunk))
res.on('end', () =>
resolve({
// 이 예시는 모든 네트워크 제공 JavaScript가 ES 모듈 코드라고 가정합니다.
format: 'module',
shortCircuit: true,
source: data,
})
)
}).on('error', err => reject(err))
})
}
// Node.js가 다른 모든 URL을 처리하도록 합니다.
return nextLoad(url)
}
// main.mjs
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js'
console.log(VERSION)
위의 후크 모듈을 사용하여 node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs
를 실행하면 main.mjs
의 URL에 있는 모듈에 따라 CoffeeScript의 현재 버전이 출력됩니다.
트랜스파일링
Node.js가 이해하지 못하는 형식의 소스는 load
후크를 사용하여 JavaScript로 변환할 수 있습니다.
이는 Node.js를 실행하기 전에 소스 파일을 트랜스파일링하는 것보다 성능이 떨어집니다. 트랜스파일러 후크는 개발 및 테스트 목적으로만 사용해야 합니다.
비동기 버전
// coffeescript-hooks.mjs
import { readFile } from 'node:fs/promises'
import { dirname, extname, resolve as resolvePath } from 'node:path'
import { cwd } from 'node:process'
import { fileURLToPath, pathToFileURL } from 'node:url'
import coffeescript from 'coffeescript'
const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/
export async function load(url, context, nextLoad) {
if (extensionsRegex.test(url)) {
// CoffeeScript 파일은 CommonJS 또는 ES 모듈일 수 있으므로, 모든 CoffeeScript 파일이 동일한 위치의 .js 파일과 동일하게 Node.js에서 처리되도록 합니다. 임의의 .js 파일을 Node.js가 어떻게 해석하는지 확인하려면 파일 시스템에서 가장 가까운 상위 package.json 파일을 검색하고 해당 "type" 필드를 읽습니다.
const format = await getPackageType(url)
const { source: rawSource } = await nextLoad(url, { ...context, format })
// 이 훅은 모든 가져온 CoffeeScript 파일에 대해 CoffeeScript 소스 코드를 JavaScript 소스 코드로 변환합니다.
const transformedSource = coffeescript.compile(rawSource.toString(), url)
return {
format,
shortCircuit: true,
source: transformedSource,
}
}
// Node.js가 다른 모든 URL을 처리하도록 합니다.
return nextLoad(url)
}
async function getPackageType(url) {
// `url`은 load() 훅에서 확인된 url이 전달될 때 첫 번째 반복 동안 파일 경로일 뿐입니다.
// load()의 실제 파일 경로에는 사양에 따라 파일 확장자가 포함됩니다.
// `url`에 파일 확장자가 있는지 여부에 대한 이 간단한 참/거짓 확인은 대부분의 프로젝트에서 작동하지만 일부 예외적인 경우(확장자 없는 파일 또는 트레일링 공백으로 끝나는 url 등)를 다루지 않습니다.
const isFilePath = !!extname(url)
// 파일 경로인 경우 해당 경로가 있는 디렉토리를 가져옵니다.
const dir = isFilePath ? dirname(fileURLToPath(url)) : url
// 동일한 디렉토리에 있는 package.json의 파일 경로를 작성합니다.
// 이 파일은 존재할 수도 있고 존재하지 않을 수도 있습니다.
const packagePath = resolvePath(dir, 'package.json')
// 존재하지 않을 수도 있는 package.json을 읽어봅니다.
const type = await readFile(packagePath, { encoding: 'utf8' })
.then(filestring => JSON.parse(filestring).type)
.catch(err => {
if (err?.code !== 'ENOENT') console.error(err)
})
// package.json이 존재하고 값이 있는 `type` 필드를 포함하는 경우, 완료입니다.
if (type) return type
// 그렇지 않은 경우(루트가 아닌 경우) 위쪽의 다음 디렉토리를 계속 확인합니다.
// 루트에 있는 경우 중지하고 false를 반환합니다.
return dir.length > 1 && getPackageType(resolvePath(dir, '..'))
}
동기 버전
// coffeescript-sync-hooks.mjs
import { readFileSync } from 'node:fs/promises'
import { registerHooks } from 'node:module'
import { dirname, extname, resolve as resolvePath } from 'node:path'
import { cwd } from 'node:process'
import { fileURLToPath, pathToFileURL } from 'node:url'
import coffeescript from 'coffeescript'
const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/
function load(url, context, nextLoad) {
if (extensionsRegex.test(url)) {
const format = getPackageType(url)
const { source: rawSource } = nextLoad(url, { ...context, format })
const transformedSource = coffeescript.compile(rawSource.toString(), url)
return {
format,
shortCircuit: true,
source: transformedSource,
}
}
return nextLoad(url)
}
function getPackageType(url) {
const isFilePath = !!extname(url)
const dir = isFilePath ? dirname(fileURLToPath(url)) : url
const packagePath = resolvePath(dir, 'package.json')
let type
try {
const filestring = readFileSync(packagePath, { encoding: 'utf8' })
type = JSON.parse(filestring).type
} catch (err) {
if (err?.code !== 'ENOENT') console.error(err)
}
if (type) return type
return dir.length > 1 && getPackageType(resolvePath(dir, '..'))
}
registerHooks({ load })
훅 실행
# main.coffee {#maincoffee}
import { scream } from './scream.coffee'
console.log scream 'hello, world'
import { version } from 'node:process'
console.log "Brought to you by Node.js version #{version}"
# scream.coffee {#screamcoffee}
export scream = (str) -> str.toUpperCase()
위의 훅 모듈을 사용하여 node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee
또는 node --import ./coffeescript-sync-hooks.mjs ./main.coffee
를 실행하면, main.coffee
의 소스 코드가 디스크에서 로드된 후 Node.js가 실행하기 전에 JavaScript로 변환됩니다. 로드된 파일의 import
문을 통해 참조되는 모든 .coffee
, .litcoffee
또는 .coffee.md
파일에 대해서도 마찬가지입니다.
Import map
이전 두 예제는 load
훅을 정의했습니다. 이것은 resolve
훅의 예입니다. 이 훅 모듈은 어떤 지정자를 다른 URL로 재정의할지 정의하는 import-map.json
파일을 읽습니다(이는 "import map" 사양의 작은 하위 집합에 대한 매우 단순한 구현입니다).
비동기 버전
// import-map-hooks.js
import fs from 'node:fs/promises'
const { imports } = JSON.parse(await fs.readFile('import-map.json'))
export async function resolve(specifier, context, nextResolve) {
if (Object.hasOwn(imports, specifier)) {
return nextResolve(imports[specifier], context)
}
return nextResolve(specifier, context)
}
동기 버전
// import-map-sync-hooks.js
import fs from 'node:fs/promises'
import module from 'node:module'
const { imports } = JSON.parse(fs.readFileSync('import-map.json', 'utf-8'))
function resolve(specifier, context, nextResolve) {
if (Object.hasOwn(imports, specifier)) {
return nextResolve(imports[specifier], context)
}
return nextResolve(specifier, context)
}
module.registerHooks({ resolve })
훅 사용
다음 파일들을 사용하여:
// main.js
import 'a-module'
// import-map.json
{
"imports": {
"a-module": "./some-module.js"
}
}
// some-module.js
console.log('some module!')
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js
또는 node --import ./import-map-sync-hooks.js main.js
를 실행하면 some module!
이 출력되어야 합니다.
소스 맵 v3 지원
추가됨: v13.7.0, v12.17.0
[Stable: 1 - Experimental]
Stable: 1 Stability: 1 - Experimental
소스 맵 캐시와 상호 작용하기 위한 도우미. 이 캐시는 소스 맵 파싱이 활성화되고 모듈의 바닥글에 소스 맵 포함 지시문이 있을 때 채워집니다.
소스 맵 파싱을 활성화하려면 Node.js를 --enable-source-maps
플래그를 사용하여 실행하거나 NODE_V8_COVERAGE=dir
을 설정하여 코드 적용 범위를 활성화해야 합니다.
// module.mjs
// ECMAScript 모듈에서
import { findSourceMap, SourceMap } from 'node:module'
// module.cjs
// CommonJS 모듈에서
const { findSourceMap, SourceMap } = require('node:module')
module.findSourceMap(path)
추가됨: v13.7.0, v12.17.0
path
<string>- 반환값: <module.SourceMap> | <undefined> 소스 맵이 발견되면
module.SourceMap
을, 그렇지 않으면undefined
를 반환합니다.
path
는 해당 소스 맵을 가져올 파일의 확인된 경로입니다.
클래스: module.SourceMap
추가됨: v13.7.0, v12.17.0
new SourceMap(payload[, { lineLengths }])
{#new-sourcemappayload-{-linelengths-}}
payload
<Object>lineLengths
<number[]>
새로운 sourceMap
인스턴스를 생성합니다.
payload
는 소스 맵 v3 형식과 일치하는 키를 가진 객체입니다.
file
: <string>version
: <number>sources
: <string[]>sourcesContent
: <string[]>names
: <string[]>mappings
: <string>sourceRoot
: <string>
lineLengths
는 생성된 코드의 각 줄 길이를 나타내는 선택적 배열입니다.
sourceMap.payload
- 반환값: <Object>
SourceMap
인스턴스를 생성하는 데 사용된 페이로드에 대한 getter입니다.
sourceMap.findEntry(lineOffset, columnOffset)
lineOffset
<number> 생성된 소스의 0부터 시작하는 줄 번호 오프셋columnOffset
<number> 생성된 소스의 0부터 시작하는 열 번호 오프셋- 반환값: <Object>
생성된 소스 파일의 줄 오프셋과 열 오프셋이 주어지면, 찾으면 원본 파일의 SourceMap 범위를 나타내는 객체를 반환하고, 찾지 못하면 빈 객체를 반환합니다.
반환된 객체는 다음 키를 포함합니다.
- generatedLine: <number> 생성된 소스에서 범위 시작의 줄 오프셋
- generatedColumn: <number> 생성된 소스에서 범위 시작의 열 오프셋
- originalSource: <string> SourceMap에 보고된 원본 소스의 파일 이름
- originalLine: <number> 원본 소스에서 범위 시작의 줄 오프셋
- originalColumn: <number> 원본 소스에서 범위 시작의 열 오프셋
- name: <string>
반환값은 오류 메시지와 CallSite 객체에 표시되는 1부터 시작하는 줄 및 열 번호가 아닌, 0부터 시작하는 오프셋을 기반으로 SourceMap에 표시되는 원시 범위를 나타냅니다.
Error 스택과 CallSite 객체에서 보고하는 lineNumber와 columnNumber에서 해당하는 1부터 시작하는 줄 및 열 번호를 가져오려면 sourceMap.findOrigin(lineNumber, columnNumber)
를 사용하십시오.
sourceMap.findOrigin(lineNumber, columnNumber)
lineNumber
<number> 생성된 소스의 호출 사이트의 1-indexed 라인 번호columnNumber
<number> 생성된 소스의 호출 사이트의 1-indexed 열 번호- 반환값: <Object>
생성된 소스의 호출 사이트에서 1-indexed lineNumber
및 columnNumber
이 주어지면 원본 소스의 해당 호출 사이트 위치를 찾습니다.
제공된 lineNumber
및 columnNumber
이 소스 맵에 없으면 빈 객체가 반환됩니다. 그렇지 않으면 반환된 객체에는 다음 키가 포함됩니다.
- name: <string> | <undefined> 소스 맵에 제공된 경우 범위의 이름
- fileName: <string> SourceMap에 보고된 원본 소스의 파일 이름
- lineNumber: <number> 원본 소스의 해당 호출 사이트의 1-indexed lineNumber
- columnNumber: <number> 원본 소스의 해당 호출 사이트의 1-indexed columnNumber