Hyoban

Hyoban

Don’t do what you should do, do you want.
twitter
github
email
telegram

我如何开始写一个 TypeScript 库

要全自己折腾的话,或许会陷入无尽的坑,所以我选择从 antfu 的 starter-ts 开始,按照自己的习惯进行一些改造。

技术栈选择#

TypeScript + ESLint + Prettier#

TypeScript 自不必多说,我使用 ESLint 来检查代码风格和潜在的问题,用 Prettier 格式化代码。

如果你和 prettier 的 printWidth 也做过斗争并且不能忍受的话,请看 Why I don't use Prettier,可能会喜欢用 ESLint 来格式化代码,并且我们现在有了 ESLint Stylistic 这种开箱即用的配置。

要配置自己的 ESLint config, 可以参考 antfu 的 @antfu/eslint-config。我自己的配置则不包含 ESLint Stylistic 的配置:

我个人更偏向于用单独的格式化工具,原因如下:

  1. 我希望尽可能的开启一些需要类型检查的 ESLint 规则,而它们一般会导致 lint 时间增加,影响开发时保存的体验。
  2. 这是 ESLinttypescript-eslintPrettier 更推荐的做法。

我可能也会切换到使用 ESLint 来格式化代码:

  1. Prettier 的偏好在某些情况下会让我感到不适,特别是大多数行为都是无法配置的。
  2. 单独使用 ESLint 来完成多件事情让我这个强迫症感到很舒适。

pnpm + bunchee + tsx + vitest#

为了方便的测试我们写的库,pnpmworkspace 是必不可少的,可以很方便的开一个 playground。此外,它默认不提升依赖的特性也能够防止我们不小心引用了没有定义的依赖。我的 .npmrc 为:

ignore-workspace-root-check=true
public-hoist-pattern=[]

使用 bunchee 来完成打包任务,它读取 package.json 中的 exports 字段作为打包的输入输出,无需手动指定配置。

如果你希望更清楚精细的控制打包流程,可以使用 rollup 配合一些插件来自己写配置。这里有一些常用的插件推荐。

  1. rollup-plugin-dts
  2. rollup-plugin-swc 或者 rollup-plugin-esbuild
  3. @rollup/plugin-node-resolve
  4. @rollup/plugin-commonjs

如果你想看看类似 bunchee 的其它选择,可以看看 unbuildtsup

开发过程中,非必要的情况下,基本上没人想先打包再跑代码。因此,我使用 tsx 来直接执行 ts 文件,用 vitest 来测试代码。

正确设置 package.json#

便捷地维护包的基本信息#

作为一个起手模板,它需要提前写好包的基本信息,并可以在开一个新坑的时候快速的查找替换。

基本信息处于两个位置,一个是 package.json,一个是 README.md,通过全局替换 pkg-placeholder$description$ 可以让你的包快速就位并发布。

设置包导出的内容#

首先你可以阅读 Ship ESM & CJS in one PackageTypes for SubmodulesmoduleResolution 总结 这几篇文章来了解同时发布 esm 和 cjs 两种格式包相关的基础信息。
然后可以使用 publintarethetypeswrongmodern-guide-to-packaging-js-library 这些工具来检查你的包是否符合规范。

npx publint
npx -p @arethetypeswrong/cli attw --pack .

我最终得出的配置如下:

{
  "sideEffects": false,
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js"
      },
      "require": {
        "types": "./dist/index.d.cts",
        "default": "./dist/index.cjs"
      }
    }
  },
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "typesVersions": {
    "*": {
      "*": ["./dist/*", "./dist/index.d.ts"]
    }
  },
  "files": ["dist"]
}

保持依赖定义正确#

使用 knip 查找项目中未使用的文件、依赖项和导出。

npx knip

使用 taze 来手动更新依赖或者使用 renovatebot 来定时自动更新。

自动修复 lint#

为了保证 commit 的代码符合代码规范,可以使用 simple-git-hooks 来在 commit 之前进行检查。

{
  "simple-git-hooks": {
    "pre-commit": "pnpm lint-staged"
  },
  "lint-staged": {
    "*": "eslint --fix"
  }
  "scripts": {
    "prepare": "simple-git-hooks",
  }
}

不过这种方案存在的问题是,如果你的 ESLint 配置发生了变化,那 commit 的代码依然可能存在需要修复的问题。所以我更倾向于在 CI 中进行检查。使用 git-auto-commit-action 来自动修复并 apply 到当前分支。这样还有一个好处是你不需要 require 其它贡献者完全设置好正确的环境。

发版流程#

要使我们的包版本号 +1,大抵有以下几个步骤:

  1. 进行发版前的检查
  2. 更新版本号
  3. 发布到 npm
  4. commit && tag
  5. 写这次更新的日志,在 GitHub 上发布 release

如果每次都要手动做这些事情,那就太麻烦了,所以我们可以使用 release-it 来帮助我们自动化这些步骤。借助 release-it 的 hooks 功能和自定义插件,我们能够方便的定义需要的发版流程。

release-it 对于 pnpm workspace 的支持几乎为零,在 monorepo 发布多个包的情况下需要切换到其它方案,我写了个插件提供简单的支持。

在这个插件中我还按照自己的喜好自动决定下一个版本号,以及整合 bumppchangelogithub

我的 starter#

我将我收拾完的 starter 开源了,如果你和我的喜好一样的话,可以基于它来改造自己的 starter。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。