Dynamic import ESM based dependency in Typescript CJS based repo
- Dominant language
- JavaScript
- Stars
- 736
- Forks
- 177
- PR merge metrics
- No merged PRs in 30d
Description
### Runtime
node.js
### Runtime version
20.16.0
### Module version
25.3.1
### Last module version without issue
_No response_
### Used with
_No response_
### Any other relevant information
_No response_
### What are you trying to achieve or the steps to reproduce?
I have CJS based repo written in Typescript which needs to use ESM-only dependency. I do it by dynamically importing that dependency. Code works correctly when run, but fails to compile during test run.
I use lab with `typescript` option as advised by docs. In my `tsconfig.json` I use `compilerOptions.module: "Node16"` to enable native ECMAScript module support (as per [Typescript docs](https://www.typescriptlang.org/tsconfig/#node16nodenext)). When running test I get
```
Unexpected token 'export'
```
Reason is found in [typescript.js file](https://github.com/hapijs/lab/blob/master/lib/modules/typescript.js#L29), which is used when `typescript` option is passed. Mentioned line overrides repos tsconfig option and disables possibility to dynamically import ESM dependency. When I comment out that override, code compiles correctly during test run.
I am not sure whether it is a bug or intended behaviour. If it is intended what is designed way to run my scenario?
---
How to reproduce?
`package.json`
```
{
"name": "lab-test",
"version": "1.0.0",
"main": "./dist/src/index.js",
"scripts": {
"dist": "rm -rf dist && tsc",
"start": "node ./dist/src/index.js",
"test": "lab --typescript"
},
"files": ["dist", "src"],
"license": "ISC",
"devDependencies": {
"@hapi/code": "^9.0.3",
"@hapi/lab": "^25.3.1",
"@types/node": "^20.16.0",
"typescript": "^5.6.2"
},
"dependencies": {
"p-map": "^7.0.2"
}
}
```
`tsconfig.json`
```
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16",
"target": "ES2023",
"outDir": "dist/",
"rootDir": "./",
"noEmit": false
},
"files": ["src/utils.ts", "src/index.ts"]
}
```
`src/index.ts`
```
import { isPMapImported } from './utils';
isPMapImported();
```
`src/utils.ts`
```
export const isPMapImported = async () => {
// p-map is ESM only dependency
const Pmap = (await import('p-map')).default;
console.log('Is Pmap imported: ', !!Pmap);
}
```
`test/utils.ts`
```
import * as Lab from "@hapi/lab";
import { expect } from "@hapi/code";
import { isPMapImported } from "../src/utils";
const lab = Lab.script();
const { describe, it, before } = lab;
export { lab };
describe("experiment", () => {
before(() => {});
it("is p-map imported", async () => {
await isPMapImported();
// assertion doesn't matter as compilation error will happen
expect(true).to.be.true();
});
});
```
When running:
- `npm run dist && npm run start` (runs dist index.js) I get log `Is Pmap imported: true`
- `npm run test` I get compilation error
When I comment out line 29 in node_modules/@hap/lab/lib/modules/typescript.js and run `npm run test` I get log `Is Pmap imported: true` and test succeeds
### What was the result you got?
Compilation error
```
Failed tests:
1) experiment is p-map imported:
Unexpected token 'export'
at wrapSafe (node:internal/modules/cjs/loader:1281:20)
at Module._compile (node:internal/modules/cjs/loader:1321:27)
at Object.require.extensions. [as .js] (/Users/leszek_pawlega/git/lab-test/node_modules/@hapi/lab/lib/modules/transform.js:41:28)
at Module.load (node:internal/modules/cjs/loader:1208:32)
at Function.Module._load (node:internal/modules/cjs/loader:1024:12)
at Module.require (node:internal/modules/cjs/loader:1233:19)
at require (node:internal/modules/helpers:179:18)
at /Users/leszek_pawlega/git/src/isPMapImported.ts:3:17
at isPMapImported (/Users/leszek_pawlega/git/src/isPMapImported.ts:3:17)
at /Users/leszek_pawlega/git/test/isPMapImported.ts:14:5
at Immediate. (/Users/leszek_pawlega/git/lab-test/node_modules/@hapi/lab/lib/runner.js:659:21)
```
### What result did you expect?
Test will pass
Contributor guide
Assessment
This issue has not been assessed yet.