基於typescript發佈npm包的流程

基於typescript發佈npm包的流程

  • 最近把在項目中寫的插件封裝了一下發到了npm,基於ts,順便把流程記錄下來~
  • 也能夠直接到GitHub下載該模板,快速生成本身的npm包~ 地址給到,star給到了麼❤️~

主要是有幾個流程:javascript

  • 初始化倉庫 / 初始化文件夾 / 初始化 npm / 初始化 tsc
  • 修改 tsconfig.json 配置
  • 添加 tslint 校驗代碼規則以及 editorconfig,prettier 統一代碼風格
  • 設置 git 提交的校驗鉤子
  • 配置webpack
  • 編寫插件代碼
  • 添加單元測試
  • 寫一個單元測試示例
  • 完善文檔信息
  • 完善 package.json 的描述信息
  • 發佈到 npm
  • 添加 Travis(持續集成)
  • ...

初始化倉庫 / 初始化文件夾 / 初始化 npm / 初始化 tsc

第一步就是各類初始化,比較簡單 執行下面幾個命令 而後打開文件夾就好~css

  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的編譯配置文件,能夠各類自定義的配置,參考個人以下:html

{
  "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 統一代碼風格

這個其實能夠不配置,不過如今前端發展都是各類工程化/團隊化了,仍是推薦配置下,反之也不難~
首先安裝java

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

大概解釋下每一個包的用途:
prettier: 按照必定的規則來美化你的代碼,可配合eslint/tslint等一塊兒使用,效果更佳
tslint: typescript代碼校驗規則
tslint-config-prettier: 使prettier搭配tslint一塊兒使用node

  • 新建 tslint.json 文件並配置

一些命名規範能夠去自定義配置,供參考react

{
  "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 文件並配置

供參考webpack

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

用來配置一些編輯器的習慣,供參考git

# 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 快捷命令

供參考github

{ 
"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 使用

目錄結構

clipboard.png

目錄信息
doc/ 目錄存放的是寫的文檔
example/ 目錄是本地開始測試時候要用到的
lib/ 目錄是編譯tsx文件後將要發佈到npm的目錄 (tsconfig.json中須要配置outDir爲該目錄)
src/ 目錄是該插件具體開發的目錄
src/component 目錄是該插件目錄
src/types 目錄是開發過程當中定義類型的目錄

  • example/src/index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>NPMPluginTemplate</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>
  • example/src/index.tsx
import React from 'react'
import { render } from 'react-dom'
import App from './app'
render(<App />, 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<IProps, IStates> {
    render() {
        return (
            <React.Fragment>
                <PluginDemo />
            </React.Fragment>
        )
    }
}

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<IProps, IStates> {
    constructor(props: IProps) {
        super(props)
        this.state = {
            name: '',
        }
    }
    render() {
        const { title } = this.props
        return (
            <div
                style={{
                    width: '100vw',
                    height: '100vh',
                    color: 'skyblue',
                    textAlign: 'center',
                    fontSize: '30px',
                }}
            >
                {title}
            </div>
        )
    }
}

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(<PluginDemo {...props} />)
    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

相關文章
相關標籤/搜索