diff --git a/src/cjs/index.ts b/src/cjs/index.ts index 57f273724..21d5b2bc0 100644 --- a/src/cjs/index.ts +++ b/src/cjs/index.ts @@ -205,7 +205,10 @@ const resolveTsFilename = ( if ( parent?.filename - && isTsFilePatten.test(parent.filename) + && ( + isTsFilePatten.test(parent.filename) + || tsconfig?.config.compilerOptions?.allowJs + ) && tsPath ) { for (const tryTsPath of tsPath) { diff --git a/src/esm/loaders.ts b/src/esm/loaders.ts index 8dadd6e87..1a47fe427 100644 --- a/src/esm/loaders.ts +++ b/src/esm/loaders.ts @@ -17,6 +17,7 @@ import { isJsonPattern, getFormatFromFileUrl, fileProtocol, + allowJs, type MaybePromise, type NodeError, } from './utils.js'; @@ -166,13 +167,11 @@ export const resolve: resolve = async ( } } - /** - * Typescript gives .ts, .cts, or .mts priority over actual .js, .cjs, or .mjs extensions - */ - if ( - // !recursiveCall && - tsExtensionsPattern.test(context.parentURL!) - ) { + // Typescript gives .ts, .cts, or .mts priority over actual .js, .cjs, or .mjs extensions + // + // If `allowJs` is set in `tsconfig.json`, then we'll apply the same resolution logic + // to files without a TypeScript extension. + if (tsExtensionsPattern.test(context.parentURL!) || allowJs) { const tsPaths = resolveTsPath(specifier); if (tsPaths) { for (const tsPath of tsPaths) { diff --git a/src/esm/utils.ts b/src/esm/utils.ts index cfecb1874..6c87ed8d2 100644 --- a/src/esm/utils.ts +++ b/src/esm/utils.ts @@ -19,6 +19,7 @@ const tsconfig = ( export const fileMatcher = tsconfig && createFilesMatcher(tsconfig); export const tsconfigPathsMatcher = tsconfig && createPathsMatcher(tsconfig); +export const allowJs = tsconfig?.config.compilerOptions?.allowJs ?? false; export const fileProtocol = 'file://'; diff --git a/tests/specs/smoke.ts b/tests/specs/smoke.ts index f61c713ed..f978694af 100644 --- a/tests/specs/smoke.ts +++ b/tests/specs/smoke.ts @@ -218,6 +218,14 @@ const files = { 'file.txt': 'hello', + 'import-typescript-parent.js': sourcemap.tag` + import './import-typescript-child.js'; + `, + + 'import-typescript-child.ts': sourcemap.tag` + console.log('imported'); + `, + node_modules: { 'pkg-commonjs': { 'package.json': JSON.stringify({ @@ -663,6 +671,17 @@ export default testSuite(async ({ describe }, { tsx }: NodeApis) => { expect(pTsconfigAllowJs.stderr).toMatch('Error: No error thrown'); expect(pTsconfigAllowJs.stdout).toBe(''); }); + + test('allowJs in tsconfig.json', async ({ onTestFail }) => { + const pTsconfigAllowJs = await tsx(['--tsconfig', 'tsconfig/tsconfig-allowJs.json', 'import-typescript-parent.js'], fixture.path); + onTestFail((error) => { + console.error(error); + console.log(pTsconfigAllowJs); + }); + expect(pTsconfigAllowJs.failed).toBe(false); + expect(pTsconfigAllowJs.stderr).toBe(''); + expect(pTsconfigAllowJs.stdout).toBe('imported'); + }); }); }); }