基于typescript发布npm包的流程

基于typescript发布npm包的流程

  • 最近把在项目中写的插件封装了一下发到了npm,基于ts,顺便把流程记录下来~
  • 也可以直接到GitHub下载该模板,快速生成自己的npm包~ 地址给到,star给到了么❤️~

主要是有几个流程:

  • 初始化仓库 / 初始化文件夹 / 初始化 npm / 初始化 tsc
  • 修改 tsconfig.json 配置
  • 添加 tslint 校验代码规则以及 editorconfig,prettier 统一代码风格
  • 设置 git 提交的校验钩子
  • 配置webpack
  • 编写插件代码
  • 添加单元测试
  • 写一个单元测试示例
  • 完善文档信息
  • 完善 package.json 的描述信息
  • 发布到 npm
  • 添加 Travis(持续集成)
  • ...

初始化仓库 / 初始化文件夹 / 初始化 npm / 初始化 tsc

第一步就是各种初始化,比较简单 执行下面几个命令 然后打开文件夹就好~

  1. Github 创建远程仓库
  2. git clone https://github.com/xxx/xxx.git
  3. npm init -y
  4. npm i typescript -D
  5. tsc --init

修改 tsconfig.json 配置

经过第一步的tsc --init 不出意外在根目录应该就有了tsconfig.json文件,该文件是ts的编译配置文件,可以各种自定义的配置,参考我的如下:

{
  "compilerOptions": {
    "outDir": "./lib",
    // "sourceMap": true,
    "noImplicitAny": false, //在表达式和声明上有隐含的 any类型时报错。
    "target": "es5", //指定ECMAScript目标版本 "ES3"(默认)
    "lib": ["dom", "dom.iterable", "esnext"], //    编译过程中需要引入的库文件的列表
    "allowJs": true, //允许编译javascript文件。
    "skipLibCheck": true, //忽略所有的声明文件( *.d.ts)的类型检查
    "esModuleInterop": true, //允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查
    "allowSyntheticDefaultImports": true, // 同上
    "strict": true, //启用所有严格类型检查选项
    "forceConsistentCasingInFileNames": true, //禁止对同一个文件的不一致的引用
    "module": "commonjs", //指定生成哪个模块系统代码
    "moduleResolution": "node", //决定如何处理模块。
    "resolveJsonModule": true,
    "isolatedModules": true, //将每个文件作为单独的模块(与“ts.transpileModule”类似)。
    // "noEmit": true,//不生成输出文件
    "jsx": "react",
    "baseUrl": "./",
    "paths": { "*": ["types/*"] }
    // "suppressImplicitAnyIndexErrors": true, //阻止对缺少索引签名的索引对象报错
  },
  "exclude": ["node_modules"],
  "include": ["src/**/*"]
}

还有很多其他配置,具体请传送至ts官网

添加 tslint 校验代码规则以及 editorconfig,prettier 统一代码风格

这个其实可以不配置,不过现在前端发展都是各种工程化/团队化了,还是推荐配置下,反之也不难~
首先安装

  • npm i prettier tslint tslint-config-prettier -D

大概解释下每个包的用途:
prettier: 按照一定的规则来美化你的代码,可配合eslint/tslint等一起使用,效果更佳
tslint: typescript代码校验规则
tslint-config-prettier: 使prettier搭配tslint一起使用

  • 新建 tslint.json 文件并配置

一些命名规范可以去自定义配置,供参考

{
  "extends": ["tslint:recommended", "tslint-config-prettier"],
  "rules": {
    "no-console": false,
    "object-literal-sort-keys": false,
    "member-access": false,
    "ordered-imports": false
  },
  "linterOptions": {
    "exclude": ["**/*.json", "node_modules"]
  }
}
  • 新建 .prettierrc 文件并配置

供参考

{
  "trailingComma": "all",
  "tabWidth": 4,
  "semi": false,
  "singleQuote": true,
  "endOfLine": "lf",
  "printWidth": 120,
  "overrides": [
    {
      "files": ["*.md", "*.json", "*.yml", "*.yaml"],
      "options": {
        "tabWidth": 2
      }
    }
  ]
}
  • 新建 .editorconfig 文件并配置

用来配置一些编辑器的习惯,供参考

# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4

[{*.json,*.md,*.yml,*.*rc}]
indent_style = space
indent_size = 2
  • 在 package.json 中配置 scripts 快捷命令

供参考

{ 
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json"
}
  • 新建.gitignore 文件并配置

Git 应当使用 .gitignore 文件忽略那些编译结果,以及 NPM 依赖的包文件
供参考

/node_modules/
/dist/
*.log
  • 新建.npmignore 文件并配置

npm 打包发布的时候,会默认把当前目录下所有文件打包。但是 Git 仓库中,有些东西是不需要 发布到 NPM 的,因此我们需要使用一个文件 .npmignore 来忽略这些文件
供参考

/.git/
/.vscode/
/node_modules/
.gitignore
.npmignore
.prettierrc
.editorconfig
tslint.json
tsconfig.json
note.md
*.log

设置 git 提交的校验钩子

安装

  • npm i pre-commit -S

pre-commit可以自动在.git/下生成 pre-commit 脚本
安装完毕后在package.json中进行配置:

 "pre-commit": [
    "lint"
  ],

此处的lint命令就是scripts的lint,再次提交 git commit 会先执行 scripts 下的 {"lint": "tslint -p tsconfig.json"}进行 tslint 代码检查

如果想忽略代码检查可以执行git commit -m'描述' --no-verify(或者-n)进行直接提交

配置webpack并编写插件代码

  • 配置webpack的目的是启动一个端口为 3001 的本地服务 供自己和他人 example 使用

目录结构

目录信息
doc/ 目录存放的是写的文档
example/ 目录是本地开始测试时候要用到的
lib/ 目录是编译tsx文件后将要发布到npm的目录 (tsconfig.json中需要配置outDir为该目录)
src/ 目录是该插件具体开发的目录
src/component 目录是该插件目录
src/types 目录是开发过程中定义类型的目录

  • example/src/index.html


    
        
        
        
        NPMPluginTemplate
    
    
        
  • example/src/index.tsx
import React from 'react'
import { render } from 'react-dom'
import App from './app'
render(, document.getElementById('root'))
  • example/src/app.tsx (注意,PluginDemo组件是从根目录的src/component中引入的)
import * as React from 'react'
import { PluginDemo } from '../../src/component/PluginDemo/index'

// tslint:disable-next-line:no-empty-interface
interface IProps {}

// tslint:disable-next-line:no-empty-interface
interface IStates {}
class App extends React.Component {
    render() {
        return (
            
                
            
        )
    }
}

export default App
  • src/component/PluginDemo/index.tsx
import PluginDemo from './PluginDemo'

export { PluginDemo }
  • src/component/PluginDemo/PluginDemo.tsx
import * as React from 'react'

interface IObjects {
    [propsName: string]: any
}

// tslint:disable-next-line:no-empty-interface
interface IProps {
    title?: string
}

interface IStates {
    name?: string
}
class PluginDemo extends React.Component {
    constructor(props: IProps) {
        super(props)
        this.state = {
            name: '',
        }
    }
    render() {
        const { title } = this.props
        return (
            
{title}
) } } export default PluginDemo

根目录输入命令touch webpack.confifg.js 来新建webpack配置文件
这里的webpack只是简单的处理tsx文件/图片等等,供参考

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlWebpackPlugin = new HtmlWebpackPlugin({
    template: path.join(__dirname, './example/src/index.html'),
    filename: './index.html',
})

module.exports = {
    entry: path.join(__dirname, './example/src/index.tsx'),
    output: {
        path: path.join(__dirname, 'example/dist'),
        filename: 'bundle.js',
    },
    module: {
        rules: [
            {
                test: /\.tsx?/,
                loader: 'ts-loader',
            },
            {
                // pre/nomal/post - loader的执行顺序 - 前/中/后
                enforce: 'pre',
                test: /\.tsx?/,
                loader: 'source-map-loader',
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            },
            {
                test: /\.(png|jpg|gif|mp4)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 20,
                    },
                },
            },
        ],
    },
    //映射工具
    // devtool: 'source-map',
    //处理路径解析
    resolve: {
        //extensions 拓展名
        extensions: ['.tsx', '.ts', '.js', '.jsx', '.json'],
    },
    plugins: [htmlWebpackPlugin],
    devServer: {
        port: 3005,
    },
}

package.json中增加脚本

 "scripts": {
    "start": "webpack-dev-server --open development",
    "build": "tsc",
    "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
    "lint": "tslint -p tsconfig.json"
  },
  • 其中start是本地启动服务并开发,build是根据根目录的tsconfig.json文件来执行tsx解析并最终打包到根目录的lib/文件夹内
  • 运行命令 npm start 即可看到服务启动,打开浏览器http://localhost:xxxx/ ,也正常显示.
  • 运行命令 npm run build 即可看到lib文件夹内已经有了解析后的tsx文件.

到这儿,我们npm包基本的开发流程算是通了~接下来就是测试~

添加单元测试以及编写测试示例

本测试是基于Jest + Enzyme的,所以首先要安装模块

安装 jest

  • npm i jest @types/jest ts-jest -D

创建 jestconfig.json 文件

  • touch jestconfig.json
{
  "transform": {
    "^.+\\.(t|j)sx?$": "ts-jest"
  },
  "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
  "moduleFileExtensions": ["tsx", "ts", "js", "jsx", "json", "node"]
}

更多配置请移至jest官网

添加 package.json 里的 scripts

  • "test": "jest --config jestconfig.json"

根目录src/下新建 test 目录

  • mkdir test

编写一个简单的测试用例,观察测试流程是否跑通

  • touch demo.test.ts
test('1加1等于2', () => {
    expect(1 + 1).toBe(2)
})
  • 运行命令npm run test
  • all passed

此处有一点要注意,tsconfig.json 中如果设置了 isolatedmodules 为 true 的话,直接 test(...)会报错

All files must be modules when the '--isolatedModules' flag is provided

将其改成false就好

ok,测试的流程通了,但是我们要测react组件,所以接下来还要安装另一个包Enzyme

安装 Enzyme

  • npm i enzyme @types/enzyme enzyme-adapter-react-16 -D

改文件名

  • 因为此时要测试 react,要将文件名改为 demo.test.tsx

编写 react 组件的测试用例

  • demo.test.tsx

更多API请移至enzyme官网

import React from 'react'
import { shallow, configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import { PluginDemo } from '../component/PluginDemo/index'

configure({ adapter: new Adapter() })


const setup = () => {
    // 模拟 props
    const props = {
        title: '测试组件的props传值',
    }
    // 通过 enzyme 提供的 shallow(浅渲染) 创建组件
    const wrapper = shallow()
    return {
        props,
        wrapper,
    }
}

describe('测试demo组件', () => {
    const { wrapper, props } = setup()
    // 通过传递模拟的props,测试组件是否正常渲染
    it('props', () => {
        // 详细用法见 Enzyme 文档 http://airbnb.io/enzyme/docs/api/shallow.html
        expect(wrapper.text()).toEqual('测试组件的props传值')
    })
})
  • 运行命令npm run test
  • all passed~

编写文档信息

touch README.md

介绍该插件,越详尽越好...

完善 package.json 的描述信息

供参考

{
  "name": "npm-plugin-template",
  "version": "1.0.0",
  "description": "An NPM plug-in template by typescript",
  "main": "lib/index.js",
  "scripts": {
    "start": "webpack-dev-server --open development",
    "build": "tsc",
    "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
    "lint": "tslint -p tsconfig.json",
    "test": "jest --config jestconfig.json"
  },
  "keywords": [
    "npm",
    "plugin",
    "typescript"
  ],
  "author": "FunkyTiger",
  "pre-commit": [
    "lint"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/funky-tiger/npm-plugin-template.git"
  },
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/funky-tiger/npm-plugin-template/issues"
  },
  "homepage": "https://github.com/funky-tiger/npm-plugin-template#readme",
  "dependencies": {
    "@types/html-webpack-plugin": "^3.2.1",
    "@types/node": "^12.6.8",
    "@types/react": "^16.8.20",
    "@types/react-dom": "^16.8.4",
    "@types/react-router-dom": "^4.3.4",
    "@types/webpack": "^4.32.1",
    "media-show-card": "^1.0.3",
    "pre-commit": "^1.2.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.0.1",
    "ts-node": "^8.3.0"
  },
  "devDependencies": {
    "@types/enzyme": "^3.10.3",
    "@types/jest": "^24.0.16",
    "css-loader": "^3.1.0",
    "enzyme": "^3.10.0",
    "enzyme-adapter-react-16": "^1.14.0",
    "file-loader": "^4.1.0",
    "html-webpack-plugin": "^3.2.0",
    "jest": "^24.8.0",
    "prettier": "^1.18.2",
    "source-map-loader": "^0.2.4",
    "style-loader": "^0.23.1",
    "ts-jest": "^24.0.2",
    "ts-loader": "^6.0.3",
    "tslint": "^5.18.0",
    "tslint-config-prettier": "^1.18.0",
    "typescript": "^3.5.2",
    "url-loader": "^2.1.0",
    "webpack": "^4.34.0",
    "webpack-cli": "^3.3.4",
    "webpack-dev-server": "^3.7.2"
  }
}

发布到 npm

未注册 ? npm adduser : 发布

登录

  • npm login

发布

  • npm publish
  > 此处注意 npm login / npm publish 时要切换到官方镜像
  > 官方 npm config set registry https://registry.npmjs.org/
  > 国内 npm config set registry http://registry.npm.taobao.org/

添加 Travis(持续集成)

本文章篇幅已够长,添加持续集成,添加 Travis 构建徽章,添加代码覆盖率...等等会在以后的文章中说明 
2019-08-01 更新
持续集成已完成 点击传送 ❤️

本模板地址:npm-plugin-template

你可能感兴趣的:(typescript,npm,webpack)