I'm trying to publish a TS package to NPM containing CommonJs and esm, but i'm getting 2 issues. 1- I can't seem to get to build the esm files as .mjs files, as a work around, I'm editing the files extensions manually before publishing them to npm but I'm not sure if it's the right thing to do, so all insights on that matter would be great. 2- when trying to import anything from that package, I'm not having any issues with the main file, but if I try to import from any other file, i get this error: Cannot find module 'nizar-npm-publish-test/mult/multiply' or its corresponding type declarations.ts(2307)

This is the structure of my TS package:

  • root/index.ts // has add function with export default
  • root/substract.ts // has substract function with export default
  • root/package.json
  • root/tsconfig.json
  • root/tsconfig.types.prod.json
  • root/mult/multiply.ts // has multiply function with export default
  • root/mult/divide.ts // has divide function with export default And this is the structure of my build:
  • build/@types/index.d.ts
  • build/@types/substract.d.ts
  • build/@types/mult/multiply.d.ts
  • build/@types/mult/divide.d.ts
  • build/cjs/index.js
  • build/cjs/substract.js
  • build/cjs/mult/multiply.js
  • build/cjs/mult/divide.js
  • build/esm/index.mjs
  • build/esm/substract.mjs
  • build/esm/mult/multiply.mjs
  • build/esm/mult/divide.mjs

This is what I'm using for the build: package.json:

 "main": "index.js",
  "files": [
    "build/cjs",
    "build/esm",
    "build/@types"
  ],
  "types": "build/@types",
  "exports": {
    ".": {
      "import": "./build/esm/index.mjs",
      "require": "./build/cjs/index.js"
    },
    "./*": {
      "import": "./build/esm/*.mjs",
      "require": "./build/cjs/*.js"
    },
    "./mult/*": {
      "import": "./build/esm/mult/*.mjs",
      "require": "./build/cjs/mult/*.js"
    },
    "./types":"./build/@types/index.d.ts",
    "./types/*": "./build/@types/*.d.ts",
    "./types/mult/*":"./build/@types/*.d.ts"
  },
  "scripts": {
    "clean": "rimraf build",
    "build": "yarn clean && yarn build:esm && yarn build:cjs && yarn build:types",
    "build:esm": "tsc -p tsconfig.prod.json --module ES2022 --outDir build/esm",
    "build:cjs": "tsc -p tsconfig.prod.json --module commonjs --outDir build/cjs",
    "build:types": "tsc --p tsconfig.types.prod.json -emitDeclarationOnly",
    "test": "echo \"Error: no test specified\" && exit 1",
    "launch": "npx ts-node src/index"
  },

tsconfig.json:

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "Default",
  "compilerOptions": {
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "incremental": true,
    "rootDir": ".",
    "outDir": "build",
    "sourceMap": false,
    "target": "ESNext",
    "noEmit": false,
    "module": "CommonJS",
    "moduleResolution": "node",
    "declaration": true
  },
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  },
  "include": ["."],
  "exclude": ["build", "node_modules", ".turbo", "coverage", "dist"],
  "moduleResolution": ["node_modules", "node"]
}

And the tsconfig.types.prod.json:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "composite": false,
    "outDir" : "build/@types"
  },
  "exclude": [
    "build",
    "coverage",
    "node_modules",
    ".turbo",
    "coverage",
    "dist",
    "**/__test__",
    "__test__",
    "__mocks__",
    "**/__mocks__",
    "*.config.js",
    "*.config.ts",
    ".env",
    "src/**/*.test.ts"
  ]
}

and this is the import: imports

and the ts error i talked about in the beginning: ts error on import

PS: They (add and multiply functions) both work, I just need to resolve the TS error.

2

There are 2 best solutions below

0
On

Update: I didn't need the .mjs part anymore in the workaround I found but, it's quite ugly for a solution and any help to improve it would be great:

First I updated the exports to this:

  "main": "build/cjs/index.js",
  "module": "build/esm/index.js",
  "types": "build/esm/index.d.ts",
  "files": [
    "build/cjs",
    "build/esm"
  ],
  "exports": {
    ".": {
      "import": "./build/esm/index.js",
      "require": "./build/cjs/index.js",
      "types": "./build/esm/index.d.ts"
    },
    "./*": {
      "import": "./build/esm/*.js",
      "require": "./build/cjs/*.js",
      "types": "./build/esm/*.d.ts"
    }

And ended up creating this new tree:

  • substract/package.json
  • mult/multiply/package.json
  • mult/divide/package.json

Each of these new package.json files now contain a similar object to this.

{
  "name": "substract",
  "description": "This way it's recognizable by the TypeScript compiler",
  "main": "../build/cjs/substract.js",
  "module": "../build/esm/substract.js",
  "types": "../build/esm/substract.d.ts"
}

The issue now is that if I have a lot of files that will be imported later, it will cause me to create as much package.json files.

0
On

So I resolved the entire problem, the package now is compatible nodejs APIs, Reactjs Vite, and NextJS. Here's a documented example: https://github.com/nizar-zouaoui/npm-publish.