exadel-inc / esl

Lightweight and flexible UI component library based on web components technology for creating basic UX modules
https://esl-ui.com
MIT License
57 stars 9 forks source link

[❔build] proposal: CJS or ESM output for esl-utils module #1775

Open fshovchko opened 1 year ago

fshovchko commented 1 year ago
fshovchko commented 1 year ago

@ala-n Upon the investigation based on the ESL project, I came to the following conclusion: I made an attempt to modify our build process using newly added package.json export maps (more details on that https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html https://habr.com/ru/articles/695482/)

In the end i got something like that:

"module": "modules/mjs/modules/all.js", "exports": { "./*": { "import": "./modules/mjs/modules/all.js", "require": "./modules/cjs/modules/all.js", "types": "./modules/types/modules/all.d.ts", "default": "./modules/mjs/modules/all.js" } },

To crate separate export folders, it involved adding additional typescript config files for each module type. These config files are extensions of our existing tsconfig.buid.json file. tsconfig-esm.json was added for esm and default module types:

{ "extends": "./tsconfig.build.json", "compilerOptions": { "module": "esnext", "outDir": "./modules/mjs", "target": "esnext" } }

tsconfig-cjs.json was added for commonjs modules:

{ "extends": "./tsconfig.build.json", "compilerOptions": { "module": "commonjs", "outDir": "./modules/cjs", "target": "es2015" } }

In order to prevent TypeScript from generating two type maps, it was separated into standalone process aswell: ` { "extends": "./tsconfig.build.json", "compilerOptions": { "outDir": "./modules/types", "declaration": true, "emitDeclarationOnly": true } }

`

The result package worked well in test repository, https://github.com/fshovchko/esl-monorepo-test throught webpack build process. Same goes for node environment that was using CommonJs module type. But using ESM modules in Node resulted in a simillar error: https://github.com/Esri/calcite-design-system/issues/5077. In order to fix the issue, the file extensions should be specified in import/export statements. And esl's package.json should have "type": "module" set. But using the statement will result in error in non-module environment. One of the workaround to it can be using a separate package.json just for esm modules that will only consist of one field {"type": "module"}.

Another workaround that I found was proposed by a TypeScript itself (https://www.typescriptlang.org/docs/handbook/esm-node.html). Changing our module to "module": "NodeNext" seems to resolve all of the issues. It works well with CommonJs and ESM, doesn't require specified extensions in package.json, or basically any other configuration from our side. Looking at its compiled code, it looks like cjs based coade, with probably some extra fetures added to it. The code was tested in Node v14 and everything worked fine. Some extra explanation about "NodeNext" here: https://stackoverflow.com/questions/71463698/why-we-need-nodenext-typescript-compiler-option-when-we-have-esnext

About the Node version.

In package json it can be specified as following: "engines": { "node": ">=14.0.0" },