On this page 前景提要 涉及的库 初始化项目 安装 [TypeScript](https://www.tslang.cn/docs/home.html) 配置 tsconfig.json 参考 commit 配置 eslint 安装 目录结构 Ps 配置 tsconfig.eslint.json 配置 .eslintrc.js 参考 commit 配置 rollup 安装 目录结构 配置 .babelrc 配置 rollup.config.ts 配置声明文件 试一下 参考 commit 配置 jest 安装 目录结构 配置 jest.config.js 动手写个 test 吧 再配置 eslint 增加 package.json scripts 试一下 参考 commit 配置 @microsoft/api-extractor 安装 配置 api-extractor.json 增加 package.json scripts 尝试一下 参考 commit gulp 自动化构建 安装 配置 package.json 配置 gulpfile 尝试一下 参考 commit changelog 自动生成 安装 配置 gulpfile 参考 commit Ps 优化开发流程 安装 package.json 优化发布流程 package.json 参考 commit changelog 例子 仓库地址 参考 On this page 前景提要 涉及的库 初始化项目 安装 [TypeScript](https://www.tslang.cn/docs/home.html) 配置 tsconfig.json 参考 commit 配置 eslint 安装 目录结构 Ps 配置 tsconfig.eslint.json 配置 .eslintrc.js 参考 commit 配置 rollup 安装 目录结构 配置 .babelrc 配置 rollup.config.ts 配置声明文件 试一下 参考 commit 配置 jest 安装 目录结构 配置 jest.config.js 动手写个 test 吧 再配置 eslint 增加 package.json scripts 试一下 参考 commit 配置 @microsoft/api-extractor 安装 配置 api-extractor.json 增加 package.json scripts 尝试一下 参考 commit gulp 自动化构建 安装 配置 package.json 配置 gulpfile 尝试一下 参考 commit changelog 自动生成 安装 配置 gulpfile 参考 commit Ps 优化开发流程 安装 package.json 优化发布流程 package.json 参考 commit changelog 例子 仓库地址 参考 TypeScript、Rollup 搭建工具库 Dec 31, 2019
公司内总是有许多通用的工具方法、业务功能,我们可以搭建一个工具库来给各个项目使用。
要实现的需求:🤔
支持编辑器的快速补全和提示
自动化构建
支持自动生成 changlog
代码通过 lint 和测试后才能提交、发布
eslint + @typescript-eslint/parser
rollup
jest
@microsoft/api-extractor
gulp
新建一个项目目录如 fly-helper , 并 npm init 初始化项目。
yarn add - D typescript
创建 src 目录,入口文件,以及 ts 的配置文件
fly-helper
|
|- src
|- index.ts
|- tsconfig.json
{
" compilerOptions " : {
" target " : " esnext " ,
" lib " : [ " dom " , " esnext " ] ,
" removeComments " : false ,
" declaration " : true ,
" sourceMap " : true ,
" strict " : true ,
" noImplicitAny " : false ,
" baseUrl " : " . " ,
" outDir " : " ./lib " ,
" esModuleInterop " : true ,
" moduleResolution " : " node " ,
" resolveJsonModule " : true
} ,
" include " : [ " src " ]
}
Ps:commit 中还增加了 .editorconfig ,来约束同学们的代码格式
yarn add - D eslint @ typescript - eslint / parser @ typescript - eslint / eslint - plugin
fly-helper
|- .eslintignore
|- .eslintrc.js
|- tsconfig.eslint.json
tsconfig.eslint.json 我们根目录中增加了一个 tsconfig 文件,它将用于 eslintrc.parserOptions.project ,由于该配置要求 incude 每个 ts、js 文件。而我们仅需要打包 src 目录下的代码,所以增加了该配置文件。
如果 eslintrc.parserOptions.project 配置为 tsconfig.json 。src 文件以外的 ts、js 文件都会报错。
Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: config.ts.
The file must be included in at least one of the projects provided.eslint
虽然可以配置 eslintrc.parserOptions.createDefaultProgram 但会造成巨大的性能损耗。
{
" compilerOptions " : {
" baseUrl " : " . " ,
" resolveJsonModule " : true
} ,
" include " : [ " **/*.ts " , " **/*.js " ]
}
const eslintrc = {
parser : ' @typescript-eslint/parser ' ,
extends : [
' eslint:recommended ' ,
' plugin:@typescript-eslint/recommended ' ,
] ,
plugins : [ ' @typescript-eslint ' ] ,
env : {
browser : true ,
node : true ,
es6 : true ,
} ,
parserOptions : {
project : ' ./tsconfig.eslint.json ' ,
ecmaVersion : 2019 ,
sourceType : ' module ' ,
ecmaFeatures : {
experimentalObjectRestSpread : true ,
} ,
} ,
rules : { } ,
} ;
module . exports = eslintrc ;
vue、react 等许多流行库都在使用 Rollup.js ,就不多介绍,直接看 官网 吧🤯
yarn add - D rollup rollup - plugin - babel rollup - plugin - commonjs rollup - plugin - eslint rollup - plugin - node - resolve rollup - plugin - typescript2
yarn add - D @ babel / preset - env
fly-helper
|
|- typings
|- index.d.ts
|- .babelrc
|- rollup.config.ts
{
" presets " : [
[
" @babel/preset-env " ,
{
" modules " : false
}
]
]
}
import path from ' path ' ;
import { RollupOptions } from ' rollup ' ;
import rollupTypescript from ' rollup-plugin-typescript2 ' ;
import babel from ' rollup-plugin-babel ' ;
import resolve from ' rollup-plugin-node-resolve ' ;
import commonjs from ' rollup-plugin-commonjs ' ;
import { eslint } from ' rollup-plugin-eslint ' ;
import { DEFAULT_EXTENSIONS } from ' @babel/core ' ;
import pkg from ' ./package.json ' ;
const paths = {
input : path . join ( __dirname , ' /src/index.ts ' ) ,
output : path . join ( __dirname , ' /lib ' ) ,
} ;
const rollupConfig : RollupOptions = {
input : paths . input ,
output : [
{
file : path . join ( paths . output , ' index.js ' ) ,
format : ' cjs ' ,
name : pkg . name ,
} ,
{
file : path . join ( paths . output , ' index.esm.js ' ) ,
format : ' es ' ,
name : pkg . name ,
} ,
] ,
plugins : [
eslint ( {
throwOnError : true ,
throwOnWarning : true ,
include : [ ' src/**/*.ts ' ] ,
exclude : [ ' node_modules/** ' , ' lib/** ' , ' *.js ' ] ,
} ) ,
commonjs ( ) ,
resolve ( {
customResolveOptions : {
moduleDirectory : ' node_modules ' ,
} ,
} ) ,
rollupTypescript ( ) ,
babel ( {
runtimeHelpers : true ,
exclude : ' node_modules/** ' ,
extensions : [ . . . DEFAULT_EXTENSIONS , ' .ts ' ] ,
} ) ,
] ,
} ;
export default rollupConfig ;
plugins 必须有顺序的使用
external 来设置三方库为外部模块,否则也会被打包进去,变得非常大哦
declare module ' rollup-plugin-babel ' ;
declare module ' rollup-plugin-eslint ' ;
由于部分插件还没有 @types 库,所以我们手动添加声明文件
我们在 index.ts 文件下,随意加入一个方法
export default function myFirstFunc ( str : string ) {
return ` hello ${ str } ` ;
}
由于使用了 RollupOptions 接口,直接执行会报错。我们要注释掉第2行import { RollupOptions } from 'rollup',和第17行 const rollupConfig 后面的 : RollupOptions。
然后执行 npx rollup --c rollup.config.ts
就生成了 index.js 和 index.esm.js 文件。分别对应着 commonjs 规范和 es 规范的文件。rollup 可是大力推行 es 规范啊,然后我们很多三方库都仍旧使用 commonjs 规范,为了兼容,我们两种规范都生成。
由于使用了 ts ,可以很方便的实现快速补全的需求,按照上面的例子,项目中使用这个包后,vscode 上输入就会有如下效果
yarn add - D @ types / jest eslint - plugin - jest jest ts - jest
fly-helper
|- test
|- index.test.ts
|- jest.config.js
module . exports = {
preset : ' ts-jest ' ,
testEnvironment : ' node ' ,
} ;
import assert from ' assert ' ;
import myFirstFunc from ' ../src ' ;
describe ( ' validate: ' , ( ) = > {
describe ( ' myFirstFunc ' , ( ) = > {
test ( ' return hello rollup ' , ( ) = > {
assert . strictEqual ( myFirstFunc ( ' rollup ' ) , ' hello rollup ' ) ;
} ) ;
} ) ;
} ) ;
const eslintrc = {
extends : [
' plugin:jest/recommended ' ,
] ,
plugins : [
' jest ' ,
] ,
} ;
"test": "jest --coverage --verbose -u"
coverage 输出测试覆盖率
verbose 层次显示测试套件中每个测试的结果,会看着更加直观啦
当我们 src 下有多个文件时,打包后会生成多个声明文件。
使用 @microsoft/api-extractor 这个库是为了把所有的 .d.ts 合成一个,并且,还是可以根据写的注释自动生成文档。
yarn add - D @ microsoft / api - extractor
{
" $schema " : " https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json " ,
" mainEntryPointFilePath " : " ./lib/index.d.ts " ,
" bundledPackages " : [ ] ,
" dtsRollup " : {
" enabled " : true ,
" untrimmedFilePath " : " ./lib/index.d.ts "
}
}
"api": "api-extractor run",
你可以尝试多写几个方法,打包后会发现有多个 .d.ts 文件,然后执行 yarn api
这里我已经增加了两个方法,请看 下面的 commit
执行后,会发现 声明都合在 index.d.ts 上啦。然后要把多余的给删除掉,后面改成自动删除它😕
😤还有一个 temp 文件夹,咱们配置一下 gitignore 不然它提交。tsdoc-metadata.json 可以暂时不管它,可以删除掉。
后面配置 package.json 的 typing 会自动更改存放位置
之后使用方法就有这样的提示,是不是会用的很方便嘞😉
yarn add - D gulp @ types / gulp fs - extra @ types / fs - extra @ types / node ts - node chalk
" main " : " lib/index.js " ,
" module " : " lib/index.esm.js " ,
" typings " : " lib/index.d.js " ,
" scripts " : {
" build " : " gulp build " ,
}
删除 lib 文件
呼叫 Rollup 打包
api-extractor 生成统一的声明文件,然后 删除多余的声明文件
完成
const clearLibFile : TaskFunc = async ( cb ) = > {
fse . removeSync ( paths . lib ) ;
log . progress ( ' Deleted lib file ' ) ;
cb ( ) ;
} ;
const buildByRollup : TaskFunc = async ( cb ) = > {
const inputOptions = {
input : rollupConfig . input ,
external : rollupConfig . external ,
plugins : rollupConfig . plugins ,
} ;
const outOptions = rollupConfig . output ;
const bundle = await rollup ( inputOptions ) ;
if ( Array . isArray ( outOptions ) ) {
outOptions . forEach ( async ( outOption ) = > {
await bundle . write ( outOption ) ;
} ) ;
cb ( ) ;
log . progress ( ' Rollup built successfully ' ) ;
}
} ;
const apiExtractorGenerate : TaskFunc = async ( cb ) = > {
const apiExtractorJsonPath : string = path . join ( __dirname , ' ./api-extractor.json ' ) ;
const extractorConfig : ExtractorConfig =
await ExtractorConfig . loadFileAndPrepare ( apiExtractorJsonPath ) ;
const isExist : boolean = await fse . pathExists ( extractorConfig . mainEntryPointFilePath ) ;
if ( ! isExist ) {
log . error ( ' API Extractor not find index.d.ts ' ) ;
return ;
}
const extractorResult : ExtractorResult = await Extractor . invoke ( extractorConfig , {
localBuild : true ,
showVerboseMessages : true ,
} ) ;
if ( extractorResult . succeeded ) {
const libFiles : string [ ] = await fse . readdir ( paths . lib ) ;
libFiles . forEach ( async ( file ) = > {
if ( file . endsWith ( ' .d.ts ' ) & & ! file . includes ( ' index ' ) ) {
await fse . remove ( path . join ( paths . lib , file ) ) ;
}
} ) ;
log . progress ( ' API Extractor completed successfully ' ) ;
cb ( ) ;
} else {
log . error (
` API Extractor completed with ${ extractorResult . errorCount } errors ` +
` and ${ extractorResult . warningCount } warnings `
) ;
}
} ;
const complete : TaskFunc = ( cb ) = > {
log . progress ( ' ---- end ---- ' ) ;
cb ( ) ;
} ;
export const build = series ( clearLibFile , buildByRollup , apiExtractorGenerate , complete ) ;
yarn add - D conventional - changelog - cli
import conventionalChangelog from ' conventional-changelog ' ;
export const changelog : TaskFunc = async ( cb ) = > {
const changelogPath : string = path . join ( paths . root , ' CHANGELOG.md ' ) ;
const changelogPipe = await conventionalChangelog ( {
preset : ' angular ' ,
releaseCount : 0 ,
} ) ;
changelogPipe . setEncoding ( ' utf8 ' ) ;
const resultArray = [ ' # 工具库更新日志\n\n ' ] ;
changelogPipe . on ( ' data ' , ( chunk ) = > {
chunk = chunk . replace ( /\/commits\//g , ' /commit/ ' ) ;
resultArray . push ( chunk ) ;
} ) ;
changelogPipe . on ( ' end ' , async ( ) = > {
await fse . createWriteStream ( changelogPath ) . write ( resultArray . join ( ' ' ) ) ;
cb ( ) ;
} ) ;
} ;
惊喜的发现 conventional-changelog 木得 @types 库,继续手动添加
declare module ' conventional-changelog ' ;
使用 conventional-changelog 需要注意一下
非常注意 commit 格式,格式采用 angular commit 规范 ,会识别 feat 和 fix 开头的 commit ,然后自动生成
每次更改需要先升级 version 再去生成。后面会有例子
yarn add - D husky lint - staged
" husky " : {
" hooks " : {
" pre-commit " : " lint-staged & jest -u "
}
} ,
" lint-staged " : {
" *.{.ts,.js} " : [
" eslint " ,
" git add "
]
}
之后提交代码都会先 lint 验证,再 jest 测试通过,才可以提交。规范团队协作的代码规范
" files " : [
" lib " ,
" LICENSE " ,
" CHANGELOG.md " ,
" README.md "
] ,
" sideEffects " : " false " ,
" script " : {
" changelog " : " gulp changelog " ,
" prepublishOnly " : " yarn lint & yarn test & yarn changelog & yarn build "
}
prepublishOnly 可以在 publish 的时候,先 lint 验证, 再 jest 测试 , 再生成 changlog ,最后打包,最后发布。
我们假装现在开始写第一个方法。我删除了上面的例子,增加了一个 calculate.ts
请看仓库地址 release/1.0.0 分支
然后我们提交这次更改,commit 内容为 feat: 新增 calculateOneAddOne 计算 1 + 1 方法
执行 npm version major 升级主版本号 1.0.0。
更多升级版本的操作
版本规范参考 语义化版本 2.0.0
yarn changelog 看看你的 changelog.md 就自动生成了🥳
If you're writing a package, strongly consider using pkg.module