How to properly configure typescript tsconfig.json and npm package.json to create a hybrid npm package?
I mostly followed this article (but I also tried other methods)
This is the alpha version of my library gomshal on github. This last state that I describe here is in the branch hybrid-2.3. I wanted to use ESM as a basic import and a CommonJS as option. This is described in chapter 2.3 of the article. My lib files exports through index.ts class Gomshal, enums and interfaces.
Project structure
My sources are in "./src/lib". I have two tsconfig.json files: tsconfig.esm.json and tsconfig.cjs.json. Both includes same "./src/lib/**/*.ts" files as a source. Differecnes are in compilerOptions:
// tsconfig.cjs.json for CommonJS
"compilerOptions": {
"outDir": "./build/lib/cjs",
"target": "ES5",
"module": "CommonJS",
...
}
// tsconfig.esm.json for ES6 module
"compilerOptions": {
"outDir": "./build/lib/esm",
"target": "ES2015",
"module": "ES2015",
...
}
Besides, I also have a file package.json specific for commonjs with content: { "type": "commonjs" }. Important keys from my root package.json are:
{
"type": "module",
"main": "./build/lib/esm/index.js",
"module": "./build/lib/esm/index.js",
"typings": "./build/lib/esm/index.d.ts",
"files": [ "build/lib/**/*" ],
"exports": {
"./esm": "./build/lib/esm/index.js",
"./cjs": "./build/lib/cjs/index.js"
},
...
}
So everything here is targeting to ES6 module by default. After compilation (tsc -p tsconfig.esm.json and tsc -p tsconfig.cjs.json) two folders are created in the build directory (build/lib/cjs and build/lib/esm). Package json file from src/cjs is coppied to build/lib/cjs and everything looks ready to publish. Now when I try npm pack with this settings, copy generated tgz file and npm install into demo project (part of package.json: "type": "module", "dependencies": {"gomshal": "file:gomshal-1.4.2.tgz"}), minimal demo index.js:
import { Gomshal } from 'gomshal';
gomshal = new Gomshal();
The error ERR_MODULE_NOT_FOUND is returned:
node .\index.js
(node:23404) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/resolve.js:58
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find module D:\Workspace\gomshal\npmtest\node_modules\gomshal\build\lib\esm\gomshal imported from D:\Workspace\gomshal\npmtest\node_modules\gomshal\build\lib\esm\index.js
at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:58:13)
at Loader.resolve (internal/modules/esm/loader.js:85:40)
at Loader.getModuleJob (internal/modules/esm/loader.js:188:40)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:42:40)
at link (internal/modules/esm/module_job.js:41:36) {
code: 'ERR_MODULE_NOT_FOUND'
I tried other variants in root package.json ("main" targeting to cjs and "module" targeting esm compiled code and other combinations in "exports" key) but in this case I am probably closest to the hybrid package. My node version is 13.8.0, npm 6.13.6, tsc version 3.8.3.
However, I still can't configure my npm package to make the named ESM import import { Gomshal } from 'gomshal'; usable without error. What did i miss?