TypeScript
module & moduleResolution
https://www.typescriptlang.org/tsconfig#module
仅关注 conmmonjs 和 esmodule。
ts 支持编译兼容 commonjs 和 esmodule。使能够通过import xx from 'xx'
的方式来将 commonjs 的文件转化为 default 导出,以支持对 commonjs 在 es 环境下的兼容(通过开启allowSyntheticDefaultImports
和esModuleInterop
)。但这种方式会导致混淆 commonjs 和 pureEs 的外部依赖,并不是个好的方式(同样都是 default 引入,但其实 commonjs 引入的是整个文件,而 es 引入的是 default)。
如果不考虑兼容,并且显式体现出二者的引入方式,可以通过:
{
"compilerOptions": {
"esModuleInterop": false,
"allowSyntheticDefaultImports": false,
"module": "CommonJS", // 必须编译为Common
}
}
commonjs 的引入方式与 esmodule 会明显不同:
import inspect = require("object-inspect");
若编译为 esmodule 会报错:
另,不同的 module 设置(es2015,es6),基本的 import 功能没有差异,版本越高存在越多高级 import 功能: If you are wondering about the difference between ES2015 (aka ES6) and ES2020, ES2020 adds support for dynamic imports, and import.meta.
其次 modules 还可以设置为 nodenext(4.5 +)(moduleResolution 也需要同时设置),这样编译后内容,会通过 package.json (type)自行判断:
https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/#esm-nodejs
module 设置为 commonjs, moduleResolution 会自动设置为 none,也就是 node,如 module 设置为 es2015,esnext,实际会让 moduleResolution 被设置为 classic,所以在 module:node12 出现之前,只有 module commonjs 是一个标准化的解析方式,其他的只是 ts 做了兼容处理,用于兼容 代码仍会经过 babel 处理的场景。所以目前的 模块处理策略,标准化的使用只有两种:
- module:commonjs,显式使用 commonjs 语法(import xx = require(‘xxx’)),也可使用 es 语法,配置参考上方,最终 —build 也会编译为commonjs 和 es 兼容的输出物
- module: nodenext,需要全程使用 esmodule。也就仅限 pure_esmodule 的场景使用,不兼容commonjs也就必须开启兼容 commonjs 相关的配置(同时 package type module,也就无法被 cjs 引入了)。
globalThis
globalThis 需要先定义类型在某个 .d.ts 文件中
import inspect = require("object-inspect");
export declare global {
var utils = {
inspect: inspect,
};
globalThis = {
utils,
};
}
再在某个 ts 文件中实现:
import inspect = require("object-inspect");
globalThis.utils = {
inspect,
};
且在使用前,需要先进行注入,也就是使用前必须前置加载:
import "./global";
global 与 globalThis 语义相同
但 global 中添加方法的设计,会影响模块的独立性,且必须将 在global添加内容的脚本前置,不是个好的设计
#global_method
reference & compsite
https://www.tslang.cn/docs/handbook/project-references.html
之前一直理解不了这个 reference 项目功能意义何在,理论上在有了 monorepo 的场景下也确实意义不大,因为都是基于npm包的独立构建(package.json),这里的 reference 则是通过 tsconfig.json,来实现的 ts 层面的独立项目,常见使用场景应该是 test 文件和 项目实际代码 的 分隔(test 只做基本编译)。
The path property of each reference can point to a directory containing a tsconfig.json file, or to the config file itself (which may have any name). When you reference a project, new things happen:
- Importing modules from a referenced project will instead load its output declaration file (.d.ts)
- If the referenced project produces an outFile, the output file .d.ts file’s declarations will be visible in this project
- Build mode (see below) will automatically build the referenced project if needed
By separating into multiple projects, you can greatly improve the speed of typechecking and compiling, reduce memory usage when using an editor, and improve enforcement of the logical groupings of your program.
实现条件:
- 作为独立项目的文件夹下存在 tsconfig;
- 被 reference 的项目 tsconfig 设置 compsite: true;
即使不用 tsc 做编译生成,reference 也能助于 monorepo 间的类型引用(reference 会先引入类型),并且按照说法,会加快类型检查和编译速度,节省编辑器损耗的内存。
The composite option enforces certain constraints which make it possible for build tools (including TypeScript itself, under —build mode) to quickly determine if a project has been built yet. When this setting is on:
- The rootDir setting, if not explicitly set, defaults to the directory containing the tsconfig.json file.
- All implementation files must be matched by an include pattern or listed in the files array. If this constraint is violated, tsc will inform you which files weren’t specified.
- declaration defaults to true
You can find documentation on TypeScript projects in the handbook.
开启 compsite 开启后,项目文件夹需要构成利于 ts 快速构建的结构,所以这里有一条项目内所有的implementation files(ts 文件),都必须被包含,这也就相当于省去了 ts 做文件匹配和剔除的时间。另外 declaration 为 true 则是为了默认输出 declare fiels。用于外部项目的快速获取。
所以 compsite 开启的副作用,就是所有的 ts 文件必须被 include。不然会出现类似的报错:
#tsconfig #reference_project
NPM
Pure ESM
如何评价 Pure ESM package? - Anthony Fu的回答 - 知乎 https://www.zhihu.com/question/510158466/answer/2314246241
cjs 不可引入 esm,esm 可以正常导入 cjs(编译兼容,或是用 require)
- 可控框架(生态有顶层组织,可控):应该支持 pure_esmodule 以实现高级功能和优化,并推动社区发展,要求生态内部以实现共同改进。
- 基础库(moment、lodash类):使用周期长,历史包袱重,必须提供兼容版本,以保证已有内容的稳定和可持续修复。
module #ES_Module
发布 CJS & ESM 兼容包
https://antfu.me/posts/publish-esm-and-cjs#compatibility
const { default: pkg } = await import('esm-only-package')
cjs 环境也可使用 esm 包,但是需要 顶层 await
另,package 也可通过 exports 多个内容达到兼容引入方式的效果
{
"name": "my-cool-package",
"exports": {
".": {
"require": "./index.cjs", // CJS
"import": "./index.mjs" // ESM
}
}
}
另提到 tsup 可以快速编译发布兼容引入方式的 lib 包:
tsup src/index.ts --format cjs,esm
另,ts的快速 package 模板:
{
"name": "my-cool-package",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
"watch": "npm run build -- --watch src",
"prepublishOnly": "npm run build"
}
}