diff --git a/packages/integrate-module/src/index.ts b/packages/integrate-module/src/index.ts index 834c78ef5..e21665746 100644 --- a/packages/integrate-module/src/index.ts +++ b/packages/integrate-module/src/index.ts @@ -4,10 +4,11 @@ import test from 'node:test' import { supportedExtensions } from 'file-type' +import { bar as subBar } from '@subdirectory/bar.mjs' +import { CompiledClass } from './compiled.js' import { foo } from './foo.mjs' import { bar } from './subdirectory/bar.mjs' import { baz } from './subdirectory/index.mjs' -import { bar as subBar } from '@subdirectory/bar.mjs' import './js-module.mjs' await test('file-type should work', () => { diff --git a/packages/integrate-module/tsconfig.json b/packages/integrate-module/tsconfig.json index 06ade40e4..60eab5c3b 100644 --- a/packages/integrate-module/tsconfig.json +++ b/packages/integrate-module/tsconfig.json @@ -7,8 +7,8 @@ "outDir": "dist", "baseUrl": "./", "paths": { - "@subdirectory/*": ["./src/subdirectory/*"], - }, + "@subdirectory/*": ["./src/subdirectory/*"] + } }, - "include": ["src"], + "include": ["src", "package.json"] } diff --git a/packages/register/esm.mts b/packages/register/esm.mts index 8f324a122..17ccec455 100644 --- a/packages/register/esm.mts +++ b/packages/register/esm.mts @@ -1,12 +1,16 @@ -import type { LoadHook, ResolveHook } from 'node:module' -import { fileURLToPath, pathToFileURL } from 'url' +import { createRequire, type LoadFnOutput, type LoadHook, type ResolveFnOutput, type ResolveHook } from 'node:module' +import { fileURLToPath, parse as parseUrl, pathToFileURL } from 'url' +import debugFactory from 'debug' import ts from 'typescript' + // @ts-expect-error import { readDefaultTsConfig } from '../lib/read-default-tsconfig.js' // @ts-expect-error -import { AVAILABLE_EXTENSION_PATTERN, AVAILABLE_TS_EXTENSION_PATTERN, compile } from '../lib/register.js' +import { AVAILABLE_TS_EXTENSION_PATTERN, compile } from '../lib/register.js' + +const debug = debugFactory('@swc-node') const tsconfig: ts.CompilerOptions = readDefaultTsConfig() tsconfig.module = ts.ModuleKind.ESNext @@ -17,21 +21,40 @@ const host: ts.ModuleResolutionHost = { readFile: ts.sys.readFile, } +const addShortCircuitSignal = (input: T): T => { + return { + ...input, + shortCircuit: true, + } +} + +const INTERNAL_MODULE_PATTERN = /^(data|node|nodejs):/ + export const resolve: ResolveHook = async (specifier, context, nextResolve) => { - if (specifier.startsWith('file:') && !AVAILABLE_EXTENSION_PATTERN.test(specifier)) { - return nextResolve(specifier) + debug('resolve', specifier, JSON.stringify(context)) + + if (INTERNAL_MODULE_PATTERN.test(specifier)) { + debug('resolved original caused by internal format', specifier) + + return addShortCircuitSignal({ + url: specifier, + }) } - // entrypoint - if (!context.parentURL) { - return { - importAttributes: { - ...context.importAttributes, - swc: 'entrypoint', - }, + const parsedUrl = parseUrl(specifier) + + // as entrypoint, just return specifier + if (!context.parentURL || parsedUrl.protocol === 'file:') { + debug('resolved original caused by protocol', specifier) + return addShortCircuitSignal({ url: specifier, - shortCircuit: true, - } + format: 'module', + }) + } + + // import attributes, support json currently + if (context.importAttributes?.type) { + return addShortCircuitSignal(await nextResolve(specifier)) } const { resolvedModule } = ts.resolveModuleName( @@ -45,21 +68,39 @@ export const resolve: ResolveHook = async (specifier, context, nextResolve) => { // local project file if ( resolvedModule && - (!resolvedModule.resolvedFileName.includes('/node_modules/') || - AVAILABLE_TS_EXTENSION_PATTERN.test(resolvedModule.resolvedFileName)) + !resolvedModule.resolvedFileName.includes('/node_modules/') && + AVAILABLE_TS_EXTENSION_PATTERN.test(resolvedModule.resolvedFileName) ) { - return { + debug('resolved by typescript', specifier, resolvedModule.resolvedFileName) + + return addShortCircuitSignal({ + ...context, url: pathToFileURL(resolvedModule.resolvedFileName).href, - shortCircuit: true, - importAttributes: { - ...context.importAttributes, - swc: resolvedModule.resolvedFileName, - }, - } + format: 'module', + }) } - // files could not resolved by typescript - return nextResolve(specifier) + try { + // files could not resolved by typescript or resolved as dts, fallback to use node resolver + const res = await nextResolve(specifier) + debug('fallback resolved by node', specifier, res.url, res.format) + return addShortCircuitSignal(res) + } catch (resolveError) { + // fallback to cjs resolve as may import non-esm files + try { + const resolution = pathToFileURL(createRequire(process.cwd()).resolve(specifier)).toString() + + debug('resolved by node commonjs', specifier, resolution) + + return addShortCircuitSignal({ + format: 'commonjs', + url: resolution, + }) + } catch (error) { + debug('resolved by cjs error', specifier, error) + throw resolveError + } + } } const tsconfigForSWCNode = { @@ -69,24 +110,27 @@ const tsconfigForSWCNode = { } export const load: LoadHook = async (url, context, nextLoad) => { - const swcAttribute = context.importAttributes.swc - - if (swcAttribute) { - delete context.importAttributes.swc - - const { source } = await nextLoad(url, { - ...context, - format: 'ts' as any, - }) + debug('load', url, JSON.stringify(context)) - const code = !source || typeof source === 'string' ? source : Buffer.from(source).toString() - const compiled = await compile(code, fileURLToPath(url), tsconfigForSWCNode, true) - return { - format: 'module', - source: compiled, - shortCircuit: true, - } - } else { + if (['builtin', 'json', 'wasm'].includes(context.format)) { + debug('load original caused by internal format', url) return nextLoad(url, context) } + + const { source, format } = await nextLoad(url, { + ...context, + }) + + debug('loaded', url, format) + + const code = !source || typeof source === 'string' ? source : Buffer.from(source).toString() + const compiled = await compile(code, url, tsconfigForSWCNode, true) + + debug('compiled', url, format) + + return addShortCircuitSignal({ + // for lazy: ts-node think format would undefined, actually it should not, keep it as original temporarily + format, + source: compiled, + }) }