

GitHub - azu/tsconfig-to-dual-package: Node.js dual package tool that add packag...
source link: https://github.com/azu/tsconfig-to-dual-package
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

tsconfig-to-dual-package
A Node.js dual package tool for TypeScript.
You can support CommonJS and ESModules in one package via this tool.
This tool add package.json
which is { "type": "module" }
or { "type": "commonjs" }
based on tsconfig's module
and outDir
option.
You can use this tool with tsc
command.
$ tsc -p . && tsc -p ./tsconfig.cjs.json && tsconfig-to-dual-package # add "{ourDir}/package.json"
Install
Install with npm:
npm install tsconfig-to-dual-package --save-dev
Requirements: This tool depended on typescript
package for parsing tsconfig.json
file.
It means that You need to install typescript
as devDependencies in your project.
- PeerDependency:
typescript
:*
(any version)
- Node.js 16+
Usage
Usage
$ tsconfig-to-dual-package [Option] <tsconfig.json>
Options
--cwd [String] current working directory. Default: process.cwd()
--debug [Boolean] Enable debug output
--help [Boolean] show help
Examples
# Find tsconfig*.json in cwd and convert to dual package
$ tsconfig-to-dual-package
# Convert specified tsconfig.json to dual package
$ tsconfig-to-dual-package ./config/tsconfig.esm.json ./config/tsconfig.cjs.json
How it works
This tool adds package.json
to tsconfig's outDir
for dual package.
Each generated package.json
has type
field that is commonjs
or module
.
You can see example repository in following:
For example, This project package.json
is following:
{
"name": "my-package",
"version": "1.0.0",
"type": "module",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"module": "./module/index.js",
// Note: Normally same .js extension can not be used as dual package
// but this tool add custom `package.json` to each outDir(=lib/, module/) and resolve it.
"exports": {
".": {
"import": {
"types": "./module/index.d.ts",
"default": "./module/index.js"
},
"require": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"default": "./lib/index.js"
}
}
}
And, This project has tsconfig.json
and tsconfig.cjs.json
:
tsconfig.json
: for ES Module
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"newLine": "LF",
"outDir": "./module/", // <= Output ESM to `module` directory
"target": "ES2018",
"strict": true,
},
"include": [
"**/*"
]
}
tsconfig.cjs.json
: for CommonJS
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node",
"outDir": "./cjs/" // <= Output CommonJS to `cjs` directory
},
"include": [
"**/*"
]
}
Then, You can run tsconfig-to-dual-package
after you compile both CommonJS and ES Module with following command:
{
"scripts": {
"build": "tsc -p ./tsconfig.json && tsc -p ./tsconfig.cjs.json && tsconfig-to-dual-package",
}
}
tsconfig-to-dual-package
command adds package.json
to module
and cjs
directory.
As a result, you can publish both CommonJS and ESModule in a single package. It is called dual package.
- package.json // { "type": "module" }
- index.ts // Node.js treat this as ESModule
- tsconfig.json // output to `module` directory
- tsconfig.cjs.json // output to `cjs` directory
- cjs/
- package.json // { "type": "commonjs" }
- index.js // Node.js treat it as CommonJS module
- module/
- package.json // { "type": "module" }
- index.js // Node.js treat it as ESModule
For more details, please see Dual CommonJS/ES module packages in Node.js official document.
Limitation
This tool copy almost fields from package.json
to generated {outDir}/package.json
.
However, it does not copy main
, module
, exports
, types
fields because it points invalid file path.
It defined in OMIT_FIELDS constant.
Used by
Motivation
As a result, TypeScript and Node.js ESM support is conflicting.
It is hard that you can support dual package with same .js
extension.
Of course, you can use tsc-multi or Packemon to support dual packages.
However, These are build tools. I want to use TypeScript compiler(tsc
) directly.
tsconfig-to-dual-package
do not touch TypeScript compiler(tsc
) process.
It just put package.json
({ "type": "module" }
or "{ "type": "commonjs" }
) to outDir
for each tsconfig.json after tsc
compile source codes.
@aduh95 describe the mechanism in nodejs/node#34515 (comment)
For reference, the
library-package/package.json
contains:{ "name": "library-package", "version": "1.0.0", "main": "./index-cjs.js", "exports": { "import": "./index-esm.js", "require": "./index-cjs.js" }, "type": "module" }
Setting
"type": "module"
makes Node.js interpret all.js
files as ESM, includingindex-cjs.js
. When you remove it, all.js
files will be interpreted as CJS, includingindex-esm.js
. If you want to support both with.js
extension, you should create two subfolders:$ mkdir ./cjs ./esm $ echo '{"type":"commonjs"}' > cjs/package.json $ echo '{"type":"module"}' > esm/package.json $ git mv index-cjs.js cjs/index.js $ git mv index-esm.js esm/index.js
And then have your package exports point to those subfolders:
{ "name": "library-package", "version": "1.0.0", "main": "./cjs/index.js", "exports": { "import": "./esm/index.js", "require": "./cjs/index.js" }, "type": "module" }
Also, Node.js documentation describe this behavior as follows
The nearest parent package.json is defined as the first package.json found when searching in the current folder, that folder's parent, and so on up until a node_modules folder or the volume root is reached.
// package.json { "type": "module" }
# In same folder as preceding package.json node my-app.js # Runs as ES module
If the nearest parent package.json lacks a "type" field, or contains "type": "commonjs", .js files are treated as CommonJS. If the volume root is reached and no package.json is found, .js files are treated as CommonJS.
Pros and Cons
Pros
- You can use TypeScript compiler(
tsc
) directly- No additional bundler, transpiler, build tool
Cons
- You need to run
tsconfig-to-dual-package
aftertsc
compile - This tool copy
package.json
tooutDir
. This approach may affect path finding forpackage.json
like read-pkg-up - Dual package hazard - I recommend that you should not use this approach for stateful package.
- For example, a singleton and
instanceof
check for user-input may cause unexpected behavior. - This Dual package has a risk of loading double(
require
andimport
load separate resources). - Very large package may want to prevent loading double package. For example, a large dictionary included package.
- For example, a singleton and
Dual package is hard to use some API like
__diranme
,__filename
without transpiler
What should I do support dual package?
- Example repository: tsconfig-to-dual-package-example
- Pull Request: feat: support dual package by azu · Pull Request #2 · azu/tsconfig-to-dual-package-example
- Steps:
- Install
tsconfig-to-dual-package
:npm install --save-dev tsconfig-to-dual-package
- Add
"type": "module"
to package.json vianpm pkg set type=module
- Add
tsconfig.json
andtsconfig.cjs.json
- Create
tsconfig.json
and set it to usemodule: "esnext"
- Create
tsconfig.cjs.json
and set it to usemodule: "commonjs"
- Add
tsconfig-to-dual-package
to build script"build": "tsc -p ./tsconfig.json && tsc -p ./tsconfig.cjs.json && tsconfig-to-dual-package"
- Add
"main"
/"types"
(for backward compatibility)/"files"
/"exports"
fields topackage.json
"files": ["lib/", "module/"]
(lib/ = cjs, module/ = esm)"main"
/"types"
/"exports"
{ "main": "./lib/index.js", "types": "./lib/index.d.ts", "exports": { ".": { "import": { "types": "./module/index.d.ts", "default": "./module/index.js" }, "require": { "types": "./lib/index.d.ts", "default": "./lib/index.js" }, "default": "./module/index.js" }, "./package.json": "./package.json" } }
Check Check Check
- Publish!
npm publish
- After Check!
- publint
- Load test via require/import
- Install
Is there a migration script?
It is not for everyone, but I wrote a migration script for TypeScript project.
- Migration Script: Convert TypeScript project to Node.js dual package
- This script make almost migration automatic
- Use
npm pkg
command for changepackage.json
- Use tsconfig-to-dual-package to build dual package
- Use eslint-cjs-to-esm to migrate source code to ESM from CJS
- Use publint to check
package.json
Example Result:
Should I migrate to dual package?
- If your package is a library, you should migrate to dual package if possible
- Because dual package reduce interop issues between CJS and ESM
- If your package is just logics, you can move to dual package
- If your package is a Command Line Tool(CLI), you not need to migrate to dual package
- Because CLI is not loaded from
require
function - You can move to Pure ESM package
- Because CLI is not loaded from
References
Related
- publint: Lint your
exports
field inpackage.json
- eslint-cjs-to-esm: help you to migrate CJS to ESM
- isaacs/rimraf: A
rm -rf
util for nodejs: use same approach
Changelog
See Releases page.
Running tests
Install devDependencies and Run npm test
:
npm test
Contributing
Pull requests and stars are always welcome.
For bugs and feature requests, please create an issue.
- Fork it!
- Create your feature branch:
git checkout -b my-new-feature
- Commit your changes:
git commit -am 'Add some feature'
- Push to the branch:
git push origin my-new-feature
- Submit a pull request :D
Author
License
MIT © azu
Recommend
-
6
TypeScript: Spelling out tsconfig. Part 1 I’m a big fan of TypeScript. Each new project I prefer to write on TS rather than native JavaScript. In this article, I will not discuss the reasons for choosing TypeScript or its adv...
-
10
Disclaimer: pkg was created for use within containers and is not intended for use in serverless environments. For those using Vercel, this means that there is no requirement to use pkg in your projects as the...
-
10
tsconfig.json文件说明一般在 typescript 的项目中,我们都能看到 tsconfig.json 这个文件,它指定了此项目的编译选项,也指定了此项目的根目录,因此这个文件一般也是在项目的根目录下。既然如此,就单单 typescrip...
-
5
关于 Angular 应用 tsconfig.json 中的 lib 属性发布于 今天 03:53 SAP Spartacus 应用的 tsconfig.json 文件里,有一个 lib 属性,值为 es2020 和 dom:Typ...
-
5
sort-compiler-options Sort tsconfig.json compilerOptions in the same order as the TSConfig Reference (Support v4.5.5 or less) In...
-
5
tsconfig.json的esModuleInterop使用场景是怎样的?更新日期: 2022-07-08阅读: 13标签:
-
6
tsconfig常用配置全解 ...
-
11
V2EX › TypeScript tsconfig.json 如何配置忽略某个文件夹下所有.ts 文件的报错?
-
9
ISS recommends Apple shareholders vote for CEO Tim Cook, other execs’ pay packages Monday, February 20, 2023 11:36 am
-
5
Nokia G42 combines repairability and 5G connectivity in a handsome purple package
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK