介绍

typescript-library-starter
它是一个开源的 TypeScript 开发根底库的脚手架工具,能够帮忙咱们疾速初始化一个 TypeScript 我的项目,这是它的官网地址),有趣味的同学能够理解下。

初始化我的项目

git clone https://github.com/alexjoverm/typescript-library-starter.git typeScript-sdk-democd typeScript-sdk-demonpm install

文件目录介绍

装置依赖完结后,关上我的项目工程目录,TypeScript library starter 生成的目录构造如下:

├── CONTRIBUTING.md├── LICENSE ├── README.md├── code-of-conduct.md├── node_modules├── package-lock.json├── package.json├── rollup.config.ts    // rollup 配置文件├── src                 // 源码目录├── test                // 测试目录├── tools               // 公布到 GitHub pages 以及 公布到 npm 的一些配置脚本工具├── tsconfig.json       // TypeScript 编译配置文件└── tslint.json         // TypeScript lint 文件

工具集成

应用 TypeScript library starter 创立的我的项目集成了很多优良的开源工具:

  • 应用 RollupJS 帮忙咱们打包。
  • 应用 PrettierTSLint 帮忙咱们格式化代码以及保障代码格调一致性。
  • 应用 TypeDoc 帮忙咱们主动生成文档并部署到 GitHub pages。
  • 应用 Jest 帮忙咱们做单元测试。
  • 应用 Commitizen 帮忙咱们生成规范化的提交正文。
  • 应用 Semantic release 帮忙咱们治理版本和公布。
  • 应用 husky 帮忙咱们更简略地应用 git hooks。
  • 应用 Conventional changelog 帮忙咱们通过代码提交信息主动生成 change log。

关联近程git仓库

以后创立好的代码库与 github 近程仓库是还未关联的,也就不能提交到近程仓库,如何关联呢?

$ git remote add origin 仓库地址

仓库地址 为之前新建的仓库,查看如下:

接下来查看是否关联胜利:

$ git remote -v

开发npm包

在咱们的 src 目录下新建 index.ts 进行性能代码的编写,在此咱们简略写一个 add 函数作为此次的公布的 npm 包所提供的性能。

// src/index.ts export default class TsSdk {    /**   * return sum   * @static   * @param {number} a    *  @param {number} a    */  static add(a: number, b: number): number {    return a+b  }}

然而在开发过程中往往须要开一个本地服务来测试咱们的代码,也就是要写一个 demo,这时候就要用到 webpack 了。

demo编写

咱们会利用 Node.js 的 express 库去运行咱们的 demo,利用 webpack 来作为 demo 的构建工具。

依赖装置

咱们先来装置一些编写 demo 须要的依赖包,如下:

npm install -D webpack webpack-dev-middleware webpack-hot-middleware ts-loader  tslint-loader express
  • webpack 是打包构建工具
  • webpack-dev-middlewarewebpack-hot-middleware 是 2 个 express 的 webpack 中间件
  • ts-loadertslint-loader 是 webpack 须要的 TypeScript 相干 loader
  • express 是 Node.js 的服务端框架。

    编写 webpack 配置文件

    根目录下创立 webpack 配置文件 examples/webpack.config.js

    const fs = require('fs')const path = require('path')const webpack = require('webpack')module.exports = {mode: 'development',/** * 咱们会在 examples 目录下建多个子目录 * 咱们会把不同章节的 demo 放到不同的子目录中 * 每个子目录的下会创立一个 app.ts * app.ts 作为 webpack 构建的入口文件 * entries 收集了多目录个入口文件,并且每个入口还引入了一个用于热更新的文件 * entries 是一个对象,key 为目录名 */entry: fs.readdirSync(__dirname).reduce((entries, dir) => {  const fullDir = path.join(__dirname, dir)  const entry = path.join(fullDir, 'app.ts')  if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {    entries[dir] = ['webpack-hot-middleware/client', entry]  }  return entries}, {}),/** * 依据不同的目录名称,打包生成指标 js,名称和目录名统一 */output: {  path: path.join(__dirname, '__build__'),  filename: '[name].js',  publicPath: '/__build__/'},module: {  rules: [    {      test: /\.ts$/,      enforce: 'pre',      use: [        {          loader: 'tslint-loader'        }      ]    },    {      test: /\.tsx?$/,      use: [        {          loader: 'ts-loader',          options: {            transpileOnly: true          }        }      ]    }  ]},resolve: {  extensions: ['.ts', '.tsx', '.js']},plugins: [  new webpack.HotModuleReplacementPlugin(),  new webpack.NoEmitOnErrorsPlugin()]}

    编写 server 文件

    在 examples 目录下创立 server.js 文件:

    const express = require('express')const webpack = require('webpack')const webpackDevMiddleware = require('webpack-dev-middleware')const webpackHotMiddleware = require('webpack-hot-middleware')const WebpackConfig = require('./webpack.config')const app = express()const compiler = webpack(WebpackConfig)app.use(webpackDevMiddleware(compiler, {publicPath: '/__build__/',stats: {  colors: true,  chunks: false}}))app.use(webpackHotMiddleware(compiler))app.use(express.static(__dirname))const port = process.env.PORT || 9090module.exports = app.listen(port, () => {console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`)})

    编写 demo 代码

    首先在 examples 目录下创立 index.html 和 global.css,作为所有 demo 的入口文件已全局款式文件。
    index.html

    <!DOCTYPE html><html lang="en"><head>  <meta charset="utf-8">  <title>typescript-npm-demo examples</title>  <link rel="stylesheet" href="/global.css"></head><body style="padding: 0 20px">  <h1>typescript-npm-demo examples</h1>  <ul>    <li><a href="basic">Basic</a></li>  </ul></body></html>

    global.css

    html, body {font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";color: #2c3e50;}ul {line-height: 1.5em;padding-left: 1.5em;}a {color: #7f8c8d;text-decoration: none;}a:hover {color: #4fc08d;}

    而后在 examples 目录下创立 basic 目录,作为本章节的 demo 目录,在该目录下再创立 index.html 和 app.ts 文件

basic/index.html

<!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8">    <title>Basic example</title>  </head>  <body>    <p>add result is equal to: <i class="result"></i></p>    <script src="/__build__/basic.js"></script>  </body></html>

basic/app.ts

import TsSdk from '../../src/index.ts'const addResult = TsSdk.add(1, 2)document.querySelector('.result').innerText = addResult

运行 demo

接着咱们在 package.json 中去新增一个 npm script:

"dev": "node examples/server.js"
$ npm run dev

相当于执行了 node examples/server.js,会开启咱们的 server。
接着咱们关上 chrome 浏览器,拜访 http://localhost:9090/ 即可拜访咱们的 demo 了

四、单元测试


Jest 配置

package.json
文件中有 jest 字段,对应 Jest 配置:

"jest": {    // 将ts转换成js,因为实用ts编写测试代码  "transform": {    ".(ts|tsx)": "ts-jest"  },  // 测试环境,这里原来是 node ,咱们能够改为 jsdom ,不然拜访不到例如 window 对象。  // 示意它是一个类浏览器的测试环境,咱们能够应用浏览器环境中的一些 API。  "testEnvironment": "jsdom",  // 要测试文件的正则表达式。  // 示意 test 目录下所有以 .test.ts 和 .spec.ts 的文件都须要跑测试。  "testRegex": "/test/.*\\.(test|spec)\\.(ts)$",  // 模块文件扩展名,当你去引入一个模块并没有指定扩展名的时候,它会顺次尝试去增加这些扩展名去找你引入的模块文件。  // 示意优先找 .ts 的模块、而后是 .tsx,最初是 .js。  "moduleFileExtensions": [    "ts",    "tsx",    "js"  ],  // 测试覆盖率的阈值设定,当咱们的测试覆盖率达不到阈值的时候,测试会失败。  // 示意全局的代码分支覆盖率要达到 90%,办法覆盖率要达到 95%,代码行数覆盖率达到 95%,申明覆盖率达到 95%。  "coverageThreshold": {    "global": {      "branches": 90,      "functions": 95,      "lines": 95,      "statements": 95    }  },  // 收集指定文件的测试覆盖率(即便你没为这些文件编写测试),它的值为 glob patterns 类型。  // 示意收集 src 目录以及它的所有子目录中的 js 和 ts 文件的测试覆盖率。  "collectCoverageFrom": [    "src/*.{js,ts}",    "src/**/*.{js,ts}"  ],},

编写单元测试

在 test 目录下新建 index.spec.ts 测试文件,在上面输出咱们的测试代码

import TsSdk from "../src/typescript-sdk"/** * TsSdk test */describe('TsSdk test', () => {    test('should return a number muti value', () => {      const a = 1      const b = 3        const result = TsSdk.add(a, b)        expect(result).toBe(4)    })  })

执行 npm run test 查看咱们的测试后果!

因为咱们的性能函数较简略,所以覆盖率很简略就达到了百分之百。

⚠️留神: 在本人编写过程中,不是语法层面谬误时,测试失败是因为测试覆盖率的阈值设定,当咱们的测试覆盖率达不到阈值的时候,测试会失败。能够依据本人须要将阈值调低,或者尽可能编写欠缺的单元测试。

五、打包

咱们会利用 rollup 来打包咱们的 ts-sdk 库,它是一个十分驰名的编译打包工具,相比 webpack,它非常适合去编译和打包一些 JS 库。

因为应用 typescript-library-starter 初始化咱们的我的项目,咱们曾经领有了 rollup 打包的相干配置和相干插件的装置,接下来咱们就来对生成的 rollup.config.ts 做小小的梳理。

rollup.config.ts

import resolve from 'rollup-plugin-node-resolve'import commonjs from 'rollup-plugin-commonjs'import sourceMaps from 'rollup-plugin-sourcemaps'import camelCase from 'lodash.camelcase'import typescript from 'rollup-plugin-typescript2'import json from 'rollup-plugin-json'const pkg = require('./package.json')const libraryName = 'typescript-sdk'export default {  // 示意打包入口文件。  input: `src/${libraryName}.ts`,  // 示意输入的指标文件,它是一个对象数组,咱们能够指定输入的格局,比方 umd 格局、es 模式等。  output: [    { file: pkg.main, name: camelCase(libraryName), format: 'umd', sourcemap: true },    { file: pkg.module, format: 'es', sourcemap: true },  ],  // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')   // 申明它的内部依赖,能够不被打包进去。  external: [],  // 监听文件的变动,从新编译,只有在编译的时候开启 --watch 才失效。  watch: {    include: 'src/**',  },  // 编译过程中应用的插件,其中 rollup-plugin-typescript2 就是用来编译 TypeScript 文件,useTsconfigDeclarationDir 示意应用 tsconfig.json 文件中定义的 declarationDir。  plugins: [    // Allow json resolution    json(),    // Compile TypeScript files    typescript({ useTsconfigDeclarationDir: true }),    // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)    commonjs(),    // Allow node_modules resolution, so you can use 'external' to control    // which external modules to include in the bundle    // https://github.com/rollup/rollup-plugin-node-resolve#usage    resolve(),    // Resolve source maps to the original source    sourceMaps(),  ],}

批改 package.json

如果曾经在 rollup.config.ts 中批改了 libraryName 为 *, 那么在 package.json 文件中你须要做相干的批改:

{  "main": "dist/***.umd.js",  "module": "dist/***.es5.js",  "typings": "dist/types/***.d.ts",}

⚠️阐明:在控制台执行 npm run build,会编译输入 dist 目录,其中 lib 目录是单个 .ts 文件编译后的 .js 文件。types 目录是所有 .ts 文件编译后生产的 .d.ts 申明文件。

***.es5.js是编译后生成的 es 模式的入口文件,用在 package.json 的 module 字段,***.umd.js 文件是编译后生成的 umd 模式的入口文件,用在 package.json 的 main 字段。

减少 2 个 npm scripts:

{  "prepub": "npm run test:prod && npm run build",  "pub": "sh release.sh"}

当咱们运行 npm run pub 的时候,会优先执行 prepub 脚本,在 prepub 中咱们运行了 test:prod 和 build 2 个脚本。&& 符号示意后面一个命令执行胜利后才会执行前面的工作。

npm run test:prod 实际上运行了 npm run lint && npm run test -- --no-cache。 先运行 lint 去校验咱们的源码和测试文件是否遵循 tslint 标准,再运行 test 去跑测试。

npm run build 实际上运行了 tsc --module commonjs、rollup -c rollup.config.ts 和 typedoc --out docs --target es6 --theme minimal --mode file src。先运行 tsc 去编译咱们的 TypeScript 文件,dist/lib 和 dist/types 下的文件就是该命令产生的,而后运行 rollup 去构建 Add.umd.js 及 Add.es.js,最初运行 typedoc 去构建我的项目的文档。

运行完 prepub 后就会再运行 pub 命令,实际上执行了 sh release.sh 命令,然而目前咱们没有这个脚本,接下来咱们就须要来编写部署脚本 release.sh。

编写部署脚本

release.sh:

// 部署脚本是 shell 脚本,shell 脚本就是封装了多行控制台命令,来逐行解释他们的含意。// 用来示意它是一个 shell 脚本。#!/usr/bin/env sh// 通知脚本如果执行后果不为 true 则退出。set -e// 在控制台输入 Enter release version:。echo "Enter release version: "// 示意从规范输出读取值,并赋值给 $VERSION 变量。read VERSION// 其中 read -p 示意给出提示符// -n 1 示意限定最多能够有 1 个字符能够作为无效读入// -r 示意禁止反斜线的本义性能。因为咱们的 read 并没有指定变量名,那么默认这个输出读取值会赋值给 $REPLY 变量。read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r// echo 输入空值示意跳到一个新行,# 在 shell 脚本中示意正文。echo  # (optional) move to a new line// 示意 shell 脚本中的流程管制语句,判断 $REPLY 是不是大小写的 y,如果满足,则走到前面的 then 逻辑。if [[ $REPLY =~ ^[Yy]$ ]]then// 在控制台输入 Releasing $VERSION ...。  echo "Releasing $VERSION ..."  # commit// 示意把代码所有变动提交到暂存区。  git add -A  git commit -m "[build] $VERSION"// 批改 package.json 中的 version 字段到 $VERSION,并且提交一条批改记录,提交正文是 [release] $VERSION。  npm version $VERSION --message "[release] $VERSION"// 把代码公布到骨干分支  git push origin master//是把仓库公布到 npm 上,咱们会把 dist 目录下的代码都公布到 npm 上,因为咱们在 package.json 中配置的是 files 是 ["dist"]。  # publish  npm publishfi

六、公布

公布之前,咱们要对代码进行压缩,这样能够使打出的包更小。
装置 rollup-plugin-terser :

npm install rollup-plugin-terser -D

批改rollup.config.ts

//....import { terser } from "rollup-plugin-terser"//.....export default {  //....  plugins: [    //....    terser()  ],}

运行部署脚本

接下来咱们就运行 npm run pub 脚本部署,按提醒间接来,就能够发包了!
留神了,npm必须切换为初始源!

扩大:错误处理

  • 执行打包命令中的tsc --module commonjs命令,会报错

问题:在tsconfig.json中曾经设置 exclude node_modules了,tsc 还是执行到 node_modules 外面去了?
起因❗️:exclude只是通知Typescript这不是源代码,不要去转译和查看它。然而Typescript还是会去node_modules中查找第三方库的申明文件,这个行为也能够通过types或typesRoots选项配置。

解决办法❗️:

// 正文"typeRoots": [    //   "node_modules/@types"    ]

总结:功败垂成✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️

参考链接:

  • https://www.yuque.com/u188805...
  • https://www.tslang.cn/docs/ha...