How to build TypeScript/MJS package compatible with NodeJs?

57 Views Asked by At

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';
                           ~~~~~~~~~~~~~~~~
0

There are 0 best solutions below