此页面需要javascript支持,请在浏览器中启用javascript

[FATAL]记录一次失败的 esbuild + ts + pnpm 构建 monorepo 的尝试

Nodejs
esbuild
ts
pnpm
monorepo
共1017个字,阅读时间 5 分钟
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://icebreaker.top/articles/2021/11/15-fatal-esbuild-pnpm-monorepo

Image

[FATAL] 记录一次失败的 esbuild + ts + pnpm 构建 monorepo 的尝试

尝试

本人尝试使用 pnpm 来构建 monorepo ,来尝试 lerna 的其他的可能性。

构建工具,也没有选用传统的 webpack or rollup, 而选用了最快的 esbuild

于是这场闹剧就此拉开了序幕。

项目

项目目录上,参考 rollup/plugins

采用 [scripts](js脚本) + [shared](软连接) + packages 的方式,构建根目录。如下图所示:

Image

packages 里,每个 pkg 有独立的 src , types, test 还有 package.json, tsconfig.json , esbuild.config.js

Image

其中这里我把 esbuild 的配置项给模块化了,在根目录 scripts/build.js/util.js 存在 esbuild 基类的 build 配置和策略,在每个 pkg 里面,可放可不放 esbuild.config.js

  • 不放就是,直接使用默认的 esbuild 配置项进行构建。

  • 放了并导出一个 esbuild 配置项,那么就意味着我会进行一个配置项 merge 的操作,从而确保内部的配置项,优先级高于外部。

结果正式写了之后,问题纷至沓来,让人猝不及防。

pkg runtime: Brower? Server? OR Miniprogram !

这种运行 runtime 的不同,意味着,它们各个包需要不同的配置。

这时候依靠软连接构建的 shared/tsconfig.json 就行不通了。 我一个包要打 cjs 一个打 esm 等等,很多配置都不同,与其共享,还不如独立。

所以这种情况不如直接软连接一个 tsconfig.base.json 作为基类,在内部再做 overwrite

打包还好,可以根据一开始的 esbuild 配置策略进行 merge

同时 .eslintrc.js 这类也要根据不同的运行时,声明不同的配置:

比如 全局的 globalsjest 相关,内部小程序环境包,有 wx,uni 相关,同时不要忘记,给内部环境包声明 root: true !

给人用,没有.d.ts,怎么行?

我们使用 tsclib,通常会声明 "declaration": true,这是为了让,ts 自动生成 .d.ts ,这样只要我们在 package.json 里,声明 types 路径为生成好的 .d.ts 的 path , 智能提示类型检测就起作用了,这点也是 ts 相比 js+jsdoc/.d.ts 的爽点之一。

可是 esbuild 不支持输出 .d.ts, 见#95, 后来我依靠 esbuild-plugin-d.ts 暂时解决了这个问题。

不过,这个插件真的是慢 (毕竟 js 写的),之前单单 buildAll 耗时只有 0.59s , 现在生成 .d.ts 11.64s , 一下子时间 乘了 20 倍?!

同时很多 typescript 特性的缺失,也导致 ts 的功能无法充分发挥,比如 Only certain tsconfig.json fields are respected 章节中,提到的 However, esbuild currently only inspects the following fields in tsconfig.json files !

https://esbuild.github.io/content-types/#no-type-system

  • baseUrl
  • extends
  • importsNotUsedAsValues
  • jsxFactory
  • jsxFragmentFactory
  • paths
  • target
  • useDefineForClassFields

没了,All other fields will be ignored

与其这样,我还不如用 rollup + @rollup/plugin-typescript 来构建这一套机制呢。

本人极菜的 jest 单元测试

前面说了,每个 repo 内部都有一个 test 文件夹,来进行单元测试。

那么问题来了根目录 jest.config.jsmoduleNameMapper 如何配置?

如何让 vscode 插件 jest 和 jest runner 准确的运作,从而让我们点击 Debug 的时候,可以顺利的跑入 ts 源码里去?

import type {...} from '#types' 如何生效

有时候我们把 typessrc 分开写放入 2 个文件夹,然后配置 tsconfig.json

{
  "compilerOptions":{
    "paths": {
      "#types/*": ["types/*"],
      "#types": ["types/index.d.ts"]
    }
  }
}

这种在 tsc 时候是没有问题的,但是 esbuild build 之后带来了寻址失败, import type { ... } from '#types' 原封不同的放在那里!

我应该给 esbuild-plugin-d.ts 提 bugs

  1. 这个问题,尝试修复一下
  2. absWorkingDir 配置项无效,导致必须手动 resolve outDirtsconfig.json ....

有些坑是生态上的,有些坑是个人能力不足导致的。

如果您也踩过这样的坑,欢迎传道受业解惑,我拜你为师。

结论

感觉这个思路失败了。目前版本的 esbuild 能够开发,但是不适合构建 lib

后续笔者学习 vue-next 的方式, 使用 rollup + @rollup/plugin-typescript 来构建 lib 去了。

附录

simple-cloudbase-plugins

simple-cloudbase-router

目前废弃的分支 deprecated_dev