From 90f20119356b95acb8f957a9b8957d75d35c1da5 Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Sun, 28 Apr 2024 10:39:56 -0700 Subject: [PATCH] fix(dev-tools): drop deprecated node option (#465) --- .../helpers/{esm-loader.js => esm-alias.ts} | 50 +++++++++++++------ modules/dev-tools/src/helpers/esm-register.ts | 4 ++ modules/dev-tools/src/test.ts | 2 +- package.json | 6 ++- 4 files changed, 45 insertions(+), 17 deletions(-) rename modules/dev-tools/src/helpers/{esm-loader.js => esm-alias.ts} (61%) create mode 100644 modules/dev-tools/src/helpers/esm-register.ts diff --git a/modules/dev-tools/src/helpers/esm-loader.js b/modules/dev-tools/src/helpers/esm-alias.ts similarity index 61% rename from modules/dev-tools/src/helpers/esm-loader.js rename to modules/dev-tools/src/helpers/esm-alias.ts index d6b5d362..c2915125 100644 --- a/modules/dev-tools/src/helpers/esm-loader.js +++ b/modules/dev-tools/src/helpers/esm-alias.ts @@ -1,21 +1,36 @@ /** - * Support module alias in ESM mode + * Support module alias in ESM mode by implementing Node.js custom module resolver * tsconfig-paths does not work in ESM, see https://github.com/dividab/tsconfig-paths/issues/122 * Adapted from https://github.com/TypeStrong/ts-node/discussions/1450 */ import path from 'path'; import fs from 'fs'; import {pathToFileURL} from 'url'; -import {resolve as resolveTs, getFormat, transformSource, load} from 'ts-node/esm'; import {getValidPath, ocularRoot} from '../utils/utils.js'; -export {getFormat, transformSource, load}; + +/** Node.js resolve hook, + * https://nodejs.org/api/module.html#resolvespecifier-context-nextresolve + */ +type ResolveHook = ( + specifier: string, + context: { + conditions?: unknown; + importAssertions?: unknown; + parentURL: string; + }, + nextResolve: ResolveHook +) => Promise<{ + url: string; + format?: 'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'; + shortCircuit?: boolean; +}>; // Load alias from file const pathJSON = fs.readFileSync(path.resolve(ocularRoot, '.alias.json'), 'utf-8'); -const paths = JSON.parse(pathJSON); +const paths: Record = JSON.parse(pathJSON); const matchPath = createMatchPath(paths); -export function resolve(specifier, context, defaultResolver) { +export const resolve: ResolveHook = (specifier, context, nextResolver) => { const mappedSpecifier = matchPath(specifier); if (mappedSpecifier) { if (mappedSpecifier.match(/(\/\*|\.jsx?|\.tsx?|\.cjs|\.json)$/)) { @@ -26,20 +41,25 @@ export function resolve(specifier, context, defaultResolver) { specifier = `${pathToFileURL(mappedSpecifier)}`; } } - const result = resolveTs(specifier, context, defaultResolver); - return result; -} + // @ts-ignore + return nextResolver(specifier); +}; + +/** Checks if a path matches aliased pattern. + * If so, returns mapped path, otherwise returns null + */ +type AliasTest = (specifier: string) => string | null; -/** Convert ocular alias object to TS config paths object */ -function createMatchPath(aliases) { - const tests = []; +/** Get alias mapping function from ocular config */ +function createMatchPath(aliases: Record): AliasTest { + const tests: AliasTest[] = []; for (const key in aliases) { const alias = aliases[key]; - let testFunc; + let testFunc: AliasTest; if (key.includes('*')) { const regex = new RegExp(`^${key.replace('*', '(.+)')}`); - testFunc = (specifier) => { + testFunc = (specifier: string) => { const match = specifier.match(regex); if (match) { return specifier.replace(match[0], alias.replace('*', match[1])); @@ -53,7 +73,7 @@ function createMatchPath(aliases) { defaultEntry = getValidPath(`${alias}/index.ts`, `${alias}/index.js`) || defaultEntry; } - testFunc = (specifier) => { + testFunc = (specifier: string) => { if (key === specifier) { return defaultEntry; } @@ -66,7 +86,7 @@ function createMatchPath(aliases) { tests.push(testFunc); } - return (specifier) => { + return (specifier: string) => { for (const test of tests) { const result = test(specifier); if (result) { diff --git a/modules/dev-tools/src/helpers/esm-register.ts b/modules/dev-tools/src/helpers/esm-register.ts new file mode 100644 index 00000000..67dea5cd --- /dev/null +++ b/modules/dev-tools/src/helpers/esm-register.ts @@ -0,0 +1,4 @@ +import {register} from 'node:module'; + +register('ts-node/esm', import.meta.url); +register('./esm-alias.js', import.meta.url); diff --git a/modules/dev-tools/src/test.ts b/modules/dev-tools/src/test.ts index ff7a2d5c..eafc2535 100644 --- a/modules/dev-tools/src/test.ts +++ b/modules/dev-tools/src/test.ts @@ -109,7 +109,7 @@ function runNodeTest(entry: string, command: string = '') { if (ocularConfig.esm) { execShellCommand( - `NODE_OPTIONS="--experimental-modules --es-module-specifier-resolution=node --loader ${ocularConfig.ocularPath}/dist/helpers/esm-loader.js" ${command} node "${entry}"` + `${command} node --import "${ocularConfig.ocularPath}/dist/helpers/esm-register.js" --es-module-specifier-resolution=node "${entry}"` ); } else { execShellCommand( diff --git a/package.json b/package.json index c649b4bc..ef9e45cb 100644 --- a/package.json +++ b/package.json @@ -28,5 +28,9 @@ }, "pre-commit": "pre-commit", "pre-push": "pre-push", - "dependencies": {} + "dependencies": {}, + "volta": { + "node": "18.19.0", + "yarn": "1.22.19" + } }