I try to write a TypeScript/MJS package for NodeJS (v20) application, but face errors like : "exports is not defined in ES module scope" or also "Unexpected token 'export'" or "error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. "
Here is my @contoso/hello package code:
src/index.ts
export const sayHello = () => "Hello, World!";
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
package.json
{
"name": "@contoso/hello",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"prepublishOnly": "yarn build"
},
"license": "ISC",
"devDependencies": {
"typescript": "^5.4.2",
"@types/node": "^14.0.0"
},
"files": ["dist"]
}
and here is my web-hello (client) application
src/index.ts
import { sayHello } from '@consoso/hello';
import express from 'express';
const app = express();
const port = 3000;
app.get('/say-hello', (req, res) => {
res.send(sayHello());
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "NodeNext"
},
"include": [ "src/**/*" ],
"exclude": [ "node_modules" ]
}
as for package.json i tried different variations, any of them was sucessful. Here is one of them
{
"name": "web-hello",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"start": "node dist/index.js",
"start:dev": "nodemon dist/index.js",
"start:debug": "node --inspect-brk dist/index.js"
},
"license": "ISC",
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.11.25",
"typescript": "^5.4.2"
},
"dependencies": {
"@contoso/hello": "^1.0.0",
"express": "^4.18.3"
}
}
the build, pass, but when yarn start the result is:
C:\proj\web-hello\node_modules\@contoso\hello\dist\index.js:1
export const sayHello = () => "Hello, World!";
^^^^^^
SyntaxError: Unexpected token 'export'
at internalCompileFunction (node:internal/vm:77:18)
at wrapSafe (node:internal/modules/cjs/loader:1288:20)
at Module._compile (node:internal/modules/cjs/loader:1340:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
if I put "type": "module" in both package.json I get
PS C:\proj\web-hello> yarn start
yarn run v1.22.21
warning ..\package.json: No license field
$ node dist/index.js
Debugger listening on ws://127.0.0.1:15097/947cf4a8-6334-40aa-b29e-39e14f523f8b
For help, see: https://nodejs.org/en/docs/inspector
Debugger attached.
Waiting for the debugger to disconnect...
file:///C:/proj/web-hello/dist/index.js:5
Object.defineProperty(exports, "__esModule", { value: true });
^
ReferenceError: exports is not defined in ES module scope
This file is being treated as an ES module because it has a '.js' file extension and 'C:\proj\web-hello\package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
at file:///C:/proj/web-hello/dist/index.js:5:23
at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
at async loadESM (node:internal/process/esm_loader:28:7)
at async handleMainPromise (node:internal/modules/run_main:113:12)
Node.js v20.11.0
if type:module is set only in the package and removed from the client (web)
PS C:\proj\web-hello> yarn build
yarn run v1.22.21
warning ..\package.json: No license field
$ tsc
src/index.ts:1:26 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("@ingerop/hello")' call instead.
To convert this file to an ECMAScript module, change its file extension to '.mts', or
add the field `"type": "module"` to 'C:/proj/web-hello/package.json'.
1 import { sayHello } from '@contoso/hello';
~~~~~~~~~~~~~~~~