diff --git a/package-lock.json b/package-lock.json index 9722c5fd8..d67f88d6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -325,6 +325,55 @@ "@jridgewell/trace-mapping": "0.3.9" } }, + "@cspotcode/zx": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@cspotcode/zx/-/zx-6.1.0.tgz", + "integrity": "sha512-Jmaai9nFG1asWIf2Ou+cY0qd9VLIBj5N3Xjzs4sy251WH5marTMH2VSIH0otBrFesWX7A9Xnw3oNJgX+CEN/WQ==", + "dev": true, + "requires": { + "@types/fs-extra": "^9.0.13", + "@types/minimist": "^1.2.2", + "@types/node": "^17.0", + "@types/which": "^2.0.1", + "chalk": "^5.0.1", + "globby": "^13.1.1", + "node-fetch": "^3.2.3", + "yaml": "^2.0.1" + }, + "dependencies": { + "@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "dev": true + }, + "chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", @@ -813,6 +862,15 @@ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", "dev": true }, + "@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", @@ -866,6 +924,12 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, "@types/node": { "version": "13.13.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", @@ -947,6 +1011,12 @@ "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", "dev": true }, + "@types/which": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", + "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", + "dev": true + }, "@types/yargs": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz", @@ -1939,6 +2009,12 @@ "array-find-index": "^1.0.1" } }, + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "dev": true + }, "date-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", @@ -2216,17 +2292,16 @@ "dev": true }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -2244,6 +2319,16 @@ "reusify": "^1.0.4" } }, + "fetch-blob": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.5.tgz", + "integrity": "sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==", + "dev": true, + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -2308,6 +2393,15 @@ "signal-exit": "^3.0.2" } }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, "fromentries": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", @@ -2377,9 +2471,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -2520,9 +2614,9 @@ "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "ignore-by-default": { @@ -3130,9 +3224,9 @@ } }, "marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", + "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", "dev": true }, "matcher": { @@ -3240,6 +3334,23 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, + "node-fetch": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.6.tgz", + "integrity": "sha512-LAy/HZnLADOVkVPubaxHDft29booGglPFDr2Hw0J1AercRh01UiVFm++KMDnJeH9sHgNB4hsXPii7Sgym/sTbw==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -4208,9 +4319,9 @@ "dev": true }, "shiki": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", - "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -4641,30 +4752,47 @@ } }, "typedoc": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.10.tgz", - "integrity": "sha512-hQYZ4WtoMZ61wDC6w10kxA42+jclWngdmztNZsDvIz7BMJg7F2xnT+uYsUa7OluyKossdFj9E9Ye4QOZKTy8SA==", + "version": "0.22.17", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.17.tgz", + "integrity": "sha512-h6+uXHVVCPDaANzjwzdsj9aePBjZiBTpiMpBBeyh1zcN2odVsDCNajz8zyKnixF93HJeGpl34j/70yoEE5BfNg==", "dev": true, "requires": { - "glob": "^7.2.0", + "glob": "^8.0.3", "lunr": "^2.3.9", - "marked": "^3.0.8", - "minimatch": "^3.0.4", - "shiki": "^0.9.12" + "marked": "^4.0.16", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" } } } @@ -4845,9 +4973,9 @@ "dev": true }, "vscode-oniguruma": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz", - "integrity": "sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", "dev": true }, "vscode-textmate": { @@ -4865,6 +4993,12 @@ "defaults": "^1.0.3" } }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true + }, "well-known-symbols": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", @@ -4993,6 +5127,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true + }, "yargs": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", diff --git a/package.json b/package.json index 96cc40187..f824654b0 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ }, "homepage": "https://typestrong.org/ts-node", "devDependencies": { + "@cspotcode/zx": "^6.1.0", "@microsoft/api-extractor": "^7.19.4", "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", @@ -137,7 +138,7 @@ "rimraf": "^3.0.0", "semver": "^7.1.3", "throat": "^6.0.1", - "typedoc": "^0.22.10", + "typedoc": "^0.22.17", "typescript": "4.7.2", "typescript-json-schema": "^0.53.0", "util.promisify": "^1.0.1" diff --git a/scripts/get-node-nightly.ts b/scripts/get-node-nightly.ts new file mode 100755 index 000000000..b04e8bede --- /dev/null +++ b/scripts/get-node-nightly.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env -S ts-node --esm --transpileOnly +import { existsSync, mkdirSync } from 'fs'; +import { unlinkSync, rmdirSync } from 'fs'; +import { dirname, resolve } from 'path'; +import { $ } from '@cspotcode/zx'; + +async function main() { + const __root = dirname(__dirname); + const downloadDir = resolve(__root, 'temp/node-nightly'); + const result = await fetch('https://nodejs.org/download/nightly/index.json'); + const index = await result.json(); + const latest = index[0]; + const { version } = latest; + existsSync(downloadDir) && rmdirSync(downloadDir, { recursive: true }); + mkdirSync(downloadDir, { recursive: true }); + await $`wget -O ${downloadDir}/download.tar.gz https://nodejs.org/download/nightly/${version}/node-${version}-linux-x64.tar.gz`; + process.chdir(downloadDir); + await $`tar -xzvf ${downloadDir}/download.tar.gz node-${version}-linux-x64/bin/node`; + + console.log(``); + console.log( + `export PATH="${downloadDir}/node-${version}-linux-x64/bin:$PATH"` + ); + console.log(``); +} + +main(); diff --git a/scripts/put-node-nightly-on-path.sh b/scripts/put-node-nightly-on-path.sh new file mode 100755 index 000000000..2a3890946 --- /dev/null +++ b/scripts/put-node-nightly-on-path.sh @@ -0,0 +1,2 @@ +# Dot-source this in a bash shell +export PATH="$PWD/$(echo temp/node-nightly/node-*/bin):$PATH" diff --git a/src/child/child-loader.ts b/src/child/child-loader.ts index e65ffe2fe..69b486bb0 100644 --- a/src/child/child-loader.ts +++ b/src/child/child-loader.ts @@ -1,30 +1,33 @@ import type { NodeLoaderHooksAPI1, NodeLoaderHooksAPI2 } from '..'; -import { filterHooksByAPIVersion } from '../esm'; +import { filterHooksByAPIVersion, NodeLoaderHooksAPI3 } from '../esm'; -let hooks: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2; +type IHooks = NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2 & NodeLoaderHooksAPI3; +type UHooks = NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2 | NodeLoaderHooksAPI3; +let hooks: IHooks; /** @internal */ -export function lateBindHooks( - _hooks: NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2 -) { - hooks = _hooks as NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2; +export function lateBindHooks(_hooks: UHooks) { + hooks = _hooks as IHooks; } -const proxy: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2 = { - resolve(...args: Parameters) { +const proxy: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2 & NodeLoaderHooksAPI3 = { + // @ts-expect-error incompatibility between API2 async and API3 sync + resolve(...args: Parameters) { return (hooks?.resolve ?? args[2])(...args); }, - load(...args: Parameters) { + load(...args: Parameters) { return (hooks?.load ?? args[2])(...args); }, - getFormat(...args: Parameters) { + getFormat(...args: Parameters) { return (hooks?.getFormat ?? args[2])(...args); }, - transformSource(...args: Parameters) { + transformSource( + ...args: Parameters + ) { return (hooks?.transformSource ?? args[2])(...args); }, }; /** @internal */ export const { resolve, load, getFormat, transformSource } = - filterHooksByAPIVersion(proxy) as NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2; + filterHooksByAPIVersion(proxy) as IHooks; diff --git a/src/esm.ts b/src/esm.ts index cb1280451..4ad5ddff2 100644 --- a/src/esm.ts +++ b/src/esm.ts @@ -38,12 +38,22 @@ export namespace NodeLoaderHooksAPI1 { url: string, context: {}, defaultGetFormat: GetFormatHook - ) => Promise<{ format: NodeLoaderHooksFormat }>; + ) => Promise; + export interface GetFormatReturn { + format: NodeLoaderHooksFormat; + } export type TransformSourceHook = ( source: string | Buffer, - context: { url: string; format: NodeLoaderHooksFormat }, + context: TransformSourceContext, defaultTransformSource: NodeLoaderHooksAPI1.TransformSourceHook - ) => Promise<{ source: string | Buffer }>; + ) => Promise; + export interface TransformSourceContext { + url: string; + format: NodeLoaderHooksFormat; + } + export interface TransformSourceReturn { + source: string | Buffer; + } } export interface NodeLoaderHooksAPI2 { @@ -53,29 +63,52 @@ export interface NodeLoaderHooksAPI2 { export namespace NodeLoaderHooksAPI2 { export type ResolveHook = ( specifier: string, - context: { - conditions?: NodeImportConditions; - importAssertions?: NodeImportAssertions; - parentURL: string; - }, + context: ResolveContext, defaultResolve: ResolveHook - ) => Promise<{ + ) => Promise; + export type ResolveContext = NodeLoaderHooksAPI3.ResolveContext; + export type ResolveReturn = NodeLoaderHooksAPI3.ResolveReturn; + export type LoadHook = NodeLoaderHooksAPI3.LoadHook; + export type LoadReturn = NodeLoaderHooksAPI3.LoadReturn; + export type NodeImportConditions = NodeLoaderHooksAPI3.NodeImportConditions; + export type NodeImportAssertions = NodeLoaderHooksAPI3.NodeImportAssertions; +} +/** in API3, resolve became synchronous */ +export interface NodeLoaderHooksAPI3 { + resolve: NodeLoaderHooksAPI3.ResolveHook; + load: NodeLoaderHooksAPI3.LoadHook; +} +/** in API3, resolve became synchronous */ +export namespace NodeLoaderHooksAPI3 { + export type ResolveHook = ( + specifier: string, + context: ResolveContext, + defaultResolve: ResolveHook + ) => ResolveReturn; + export interface ResolveContext { + conditions?: NodeImportConditions; + importAssertions?: NodeImportAssertions; + parentURL: string; + } + export interface ResolveReturn { url: string; format?: NodeLoaderHooksFormat; shortCircuit?: boolean; - }>; + } export type LoadHook = ( url: string, - context: { - format: NodeLoaderHooksFormat | null | undefined; - importAssertions?: NodeImportAssertions; - }, - defaultLoad: NodeLoaderHooksAPI2['load'] - ) => Promise<{ + context: LoadContext, + defaultLoad: LoadHook + ) => Promise; + export interface LoadContext { + format: NodeLoaderHooksFormat | null | undefined; + importAssertions?: NodeImportAssertions; + } + export interface LoadReturn { format: NodeLoaderHooksFormat; source: string | Buffer | undefined; shortCircuit?: boolean; - }>; + } export type NodeImportConditions = unknown; export interface NodeImportAssertions { type?: 'json'; @@ -96,15 +129,20 @@ export interface NodeImportAssertions { } // The hooks API changed in node version X so we need to check for backwards compatibility. +// if true, is API2 or newer const newHooksAPI = versionGteLt(process.versions.node, '16.12.0'); +const syncResolve = versionGteLt(process.versions.node, '19.0.0'); /** @internal */ export function filterHooksByAPIVersion( - hooks: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2 -): NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2 { + hooks: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2 & NodeLoaderHooksAPI3 +): NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2 | NodeLoaderHooksAPI3 { const { getFormat, load, resolve, transformSource } = hooks; // Explicit return type to avoid TS's non-ideal inferred type - const hooksAPI: NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2 = newHooksAPI + const hooksAPI: + | NodeLoaderHooksAPI1 + | NodeLoaderHooksAPI2 + | NodeLoaderHooksAPI3 = newHooksAPI ? { resolve, load, getFormat: undefined, transformSource: undefined } : { resolve, getFormat, transformSource, load: undefined }; return hooksAPI; @@ -127,6 +165,7 @@ export function createEsmHooks(tsNodeService: Service) { const extensions = tsNodeService.extensions; const hooksAPI = filterHooksByAPIVersion({ + // @ts-ignore resolve, load, getFormat, @@ -150,29 +189,49 @@ export function createEsmHooks(tsNodeService: Service) { const rememberIsProbablyEntrypoint = new Set(); const rememberResolvedViaCommonjsFallback = new Set(); - async function resolve( + function resolve( specifier: string, context: { parentURL: string }, defaultResolve: typeof resolve - ): Promise<{ url: string; format?: NodeLoaderHooksFormat }> { - const defer = async () => { - const r = await defaultResolve(specifier, context, defaultResolve); + ): + | NodeLoaderHooksAPI3.ResolveReturn + | Promise { + const defer = () => { + const r = defaultResolve(specifier, context, defaultResolve); return r; }; // See: https://github.com/nodejs/node/discussions/41711 // nodejs will likely implement a similar fallback. Till then, we can do our users a favor and fallback today. - async function entrypointFallback( - cb: () => ReturnType | Awaited> + function entrypointFallback( + cb: () => + | NodeLoaderHooksAPI3.ResolveReturn + | Promise ): ReturnType { + // tricky song-and-dance to be conditionally sync or async, depending on node version + let resolution: + | NodeLoaderHooksAPI3.ResolveReturn + | Promise; try { - const resolution = await cb(); + resolution = cb(); + } catch (esmResolverError) { + return syncResolve + ? onError(esmResolverError) + : Promise.reject(esmResolverError).catch(onError); + } + return syncResolve + ? onResolve(resolution as NodeLoaderHooksAPI3.ResolveReturn) + : Promise.resolve(resolution).then(onResolve, onError); + + function onResolve(resolution: NodeLoaderHooksAPI3.ResolveReturn) { if ( resolution?.url && isProbablyEntrypoint(specifier, context.parentURL) ) rememberIsProbablyEntrypoint.add(resolution.url); return resolution; - } catch (esmResolverError) { + } + + function onError(esmResolverError: unknown) { if (!isProbablyEntrypoint(specifier, context.parentURL)) throw esmResolverError; try { @@ -187,14 +246,15 @@ export function createEsmHooks(tsNodeService: Service) { ).toString(); rememberIsProbablyEntrypoint.add(resolution); rememberResolvedViaCommonjsFallback.add(resolution); - return { url: resolution, format: 'commonjs' }; + return { url: resolution, format: 'commonjs' as 'commonjs' }; } catch (commonjsResolverError) { throw esmResolverError; } } } - return addShortCircuitFlag(async () => { + // @ts-expect-error + return (syncResolve ? addShortCircuitFlagSync : addShortCircuitFlag)(() => { const parsed = parseUrl(specifier); const { pathname, protocol, hostname } = parsed; @@ -227,15 +287,9 @@ export function createEsmHooks(tsNodeService: Service) { // `load` from new loader hook API (See description at the top of this file) async function load( url: string, - context: { - format: NodeLoaderHooksFormat | null | undefined; - importAssertions?: NodeLoaderHooksAPI2.NodeImportAssertions; - }, - defaultLoad: typeof load - ): Promise<{ - format: NodeLoaderHooksFormat; - source: string | Buffer | undefined; - }> { + context: NodeLoaderHooksAPI3.LoadContext, + defaultLoad: NodeLoaderHooksAPI3.LoadHook + ): Promise { return addShortCircuitFlag(async () => { // If we get a format hint from resolve() on the context then use it // otherwise call the old getFormat() hook using node's old built-in defaultGetFormat() that ships with ts-node @@ -290,8 +344,8 @@ export function createEsmHooks(tsNodeService: Service) { async function getFormat( url: string, context: {}, - defaultGetFormat: typeof getFormat - ): Promise<{ format: NodeLoaderHooksFormat }> { + defaultGetFormat: NodeLoaderHooksAPI1.GetFormatHook + ): Promise { const defer = (overrideUrl: string = url) => defaultGetFormat(overrideUrl, context, defaultGetFormat); @@ -372,9 +426,9 @@ export function createEsmHooks(tsNodeService: Service) { async function transformSource( source: string | Buffer, - context: { url: string; format: NodeLoaderHooksFormat }, - defaultTransformSource: typeof transformSource - ): Promise<{ source: string | Buffer }> { + context: NodeLoaderHooksAPI1.TransformSourceContext, + defaultTransformSource: NodeLoaderHooksAPI1.TransformSourceHook + ): Promise { if (source === null || source === undefined) { throw new Error('No source'); } @@ -414,3 +468,12 @@ async function addShortCircuitFlag(fn: () => Promise) { shortCircuit: true, }; } +function addShortCircuitFlagSync(fn: () => T) { + const ret = fn(); + // Not sure if this is necessary; being lazy. Can revisit in the future. + if (ret == null) return ret; + return { + ...ret, + shortCircuit: true, + }; +}