package.json
file, I added the field "type": "module"
:{
"name": "ts-node-test",
"version": "1.0.0",
"description": "",
"type": "module",
"scripts": {
"test": "cross-env NODE_OPTIONS=\"--loader ts-node/esm\" node index.cts"
},
"author": "",
"license": "ISC",
"devDependencies": {
"cross-env": "^7.0.3",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.4.5"
},
"volta": {
"node": "18.15.0"
}
}
tsconfig.json
is:{
"$schema": "https://json.schemastore.org/tsconfig",
"ts-node": {
"transpileOnly": true,
"files": true,
"experimentalResolver":true,
"esm": true,
},
"compilerOptions": {
"outDir": "./dist",
"allowJs": true,
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"importHelpers": true,
"lib": [
"DOM",
"ESNext"
],
"module": "ESNext",
"moduleDetection": "force",
"moduleResolution": "node",
"noEmitOnError": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"target": "esnext",
"typeRoots": [
"./node_modules/@types",
]
},
"include": [
"index.cts",
"module.cts"
]
}
index.cts
with the following code:import test from './module.cjs'
console.log(test(1, 2))
module.cts
is:export default (a: number, b: number) => a + b
3
It seems that .cts
extension in Node.js can use ESM module syntax for imports and exports.
Why?
files with an .mts extension can only use ESM (ECMAScript Module) syntax for imports and exports, while files with a .cts extension can only use CommonJS syntax for imports and exports.
That is indeed true but for .mjs
and .cjs
files respectively, i.e. after TypeScript compilation.
Within a TypeScript file (including .cts
), you can always use ESM import
/export
syntax. But the TypeScript compiler will transpile the syntax for you, according to the file extension, TSConfig module
value and package.json > type
value. More details in TS docs > Modules Reference > Module format detection
In your case, module.cts
file extension should make export default
syntax being transpiled into exports.default
or similar. You can check by inspecting the emitted JS file.
Similarly, index.cts
file extension should make import
syntax being transpiled into require()
(possibly wrapped by a custom function to handle default import).
Hence after TS transpilation, your emitted .cjs
file correctly use CommonJS require
and exports
syntax only.