從零開發一個健壯的npm包

御風天流發表於2019-02-16

最近寫 node 的時候遇到一個需求,需要清理某目錄下超過3天圖片,本來想在 npm 找個包直接用用,結果沒找到合適的,於是就自己擼一個了。

本文主要講述如何從零開始開發一個完善健壯的 npm 包,主要涉及到一些工具的使用配置,包的功能不是重點。

一、配置eslint

ESLint是一個程式碼風格檢測工具,比如使用空格還是tab,要不要加分號,使用駝峰命名還是下劃線等等。可以保證一個團隊的程式碼風格保持一致。

npm install eslint -g
eslint init

根據 eslint 提供的選項結合自己的需求,一路選擇好後,會在根目錄建立一個 .eslintrc.json 檔案,裡面一系列的規則配置,這個時候你再寫程式碼,如果不符合規範,編輯器就會報錯提示,如果某些目錄不想使用校驗,可以建立一個 .eslintignore,把不需要校驗的目錄放進入。為了方便執行校驗,我們在 package.json 裡配置一下 scripts:

"scripts": {
  "lint": "eslint --fix src"
}

可以配合githook強制每次提交的時候校驗程式碼:使用git鉤子做eslint校驗

二、編寫程式碼

我們在src目錄下編寫我們的程式碼,拆分成具體步驟為

  1. 讀取目錄下所有檔案
  2. 篩選出我們需要處理的檔案,比如建立或者修改時間超過3天的圖片或者日誌
  3. 刪除這些檔案

我們分成3個函式:

// readAllFileInfo.js
// 使用fs.readdir讀取目錄下所有檔案
fs.readdir(filePath, function(err, files) {
    if (err) {
      reject(err);
    } else {
      Promise.all(files.map(file => {
        return filterFile(file, options);
      }))
        .then(deleteFiles => {
          resolve(deleteFiles.filter(deleteFile => deleteFile));
        });
    }
  });

// filterFile.js
// 使用fs.stat讀取檔案資訊,然後篩選出需要刪除的檔案
fs.stat(fileName, (err, stats) => {
  if (err) {
    reject(err);
  } else {
    const time = stats[expiredType];
    const distanceTime = formatDate(date);
    const extName = path.extname(fileName);
    if (now - time > distanceTime && extName === `.${ext}`) {
      deleteFile(fileName)
        .then((res) => {
          resolve(res);
        });
    } else {
      resolve();
    }
  }
});

// deleteFile.js
// 使用fs.unlink刪除檔案
fs.unlink(fileName, err => {
  if (err) {
    reject(err);
  } else {
    resolve(fileName);
  }
});

這裡主要講一下解決問題的思路,首先整理一下解決這個問題需要哪些步驟,然後每一個步驟可以抽象成一個函式,想一下函式的傳參和返回值,最後可以設計一下更加相容易擴充套件的 API,具體程式碼可以檢視倉庫:https://github.com/wulv/del-e…

三、使用babel

在低版本的 node 可能還不支援某些 es6 語法,比如物件解構等,所以需要使用 babel 編譯成低版本也能識別的語法。我們把 src 目錄裡的程式碼編譯到 lib 目錄,然後我們在 package.json 裡,把 "main" 改為 "lib/index.js",這樣對外暴露出去的程式碼就不會出現相容性問題。

下載 babel-cli 依賴:

npm install --save-dev babel-cli
// 下載預設,預設就是別人配置好的一系列規則,編譯在規則內的語法
npm install --save-dev babel-preset-es2015

配置好 .babelrc:

{
    "presets": ["es2015"]
}

package.json 裡配置一下 scripts:

"scripts": {
  "build": "babel src -d lib",
  "build:watch": "npm run build -- --watch"
}

這樣執行 npm run build 就會編譯原始檔到 lib 目錄了。

四、編寫測試

為了保障程式的穩定性,我們一定要寫測試用例,特別是當你的程式依賴越來越多的時候,比如你改了A模組,你覺得你的改動都沒問題,但一發布出去就會出現意想不到的 bug 因為影響到了你不知道的某個模組。這個時候測試就尤為重要,可以幫你避免很多低階錯誤。我們使用 mochachai 做測試框架和斷言,下載依賴。

npm install mocha chai --save-dev

我們在專案根目錄建立一個 test 目錄。建立 index.formatDate.js,內容為:

`use strict`;
const chai = require(`chai`);
const formatDate = require(`../lib/formatDate`);

const expect = chai.expect;
const S = 1000;

describe(`format dete`, () => {
  it(`test 2s`, function() {
    expect(formatDate(`2s`)).to.be.equal(2 * S);
  });
});

這裡只是演示一下基本的語法,程式碼太多,具體看倉庫。然後在 package.json 裡再新增一個 scripts:

"test": "npm run build && mocha -t 5000"

五、Travis-CI+Coveralls

Travis-CI 是一個持續整合構建專案,結合github 可以實現很強大的功能,比如你給一個倉庫提交 PR 後,可以自動幫你跑完測試用例,如果測試沒有通過,就不能 mergemasterCoveralls 是一個自動化測試覆蓋率的服務,用於收集測試覆蓋率報告,對於開源專案免費,配置好這個後,就可以生成一個顯示你程式碼測試覆蓋率的 badgeCoveralls 可以使用 GitHub 賬號登入,登入之後可以在 https://coveralls.io/repos/new 新增需要收集報告的 repo

首先安裝一下 istanbul 這個工具來檢測程式碼的測試覆蓋率:

npm install istanbul --save-dev

然後在 package.json 中的 scripts 裡新增:

"cover": "istanbul cover node_modules/mocha/bin/_mocha"

執行 npm run cover 就可以看到你的程式碼測試覆蓋率了

========= Coverage summary =========
Statements   : 92.65% ( 63/68 )
Branches     : 75% ( 15/20 )
Functions    : 100% ( 14/14 )
Lines        : 92.65% ( 63/68 )
====================================

將測試覆蓋率報告提交給 Coveralls,首先安裝 coveralls:

npm install coveralls --save-dev

然後在 package.json 中的 scripts 裡新增

 "coveralls": "npm run cover -- --report lcovonly && cat ./coverage/lcov.info | coveralls"

然後建立 .travis.yml 檔案,

sudo: false
language: node_js
os:
  - linux
  - osx
node_js:
  - 6
  - 8
  - 9
  - 10
branches:
  only:
  - master
install:
- npm install
script:
  - npm run lint
  - npm run build
  - npm run cover
after_success:
- npm run coveralls

將程式碼推到 github 後,開啟 https://travis-ci.org/ ,點開右上角頭像 -> profile,把自己倉庫的開關開啟:

README.md 裡新增程式碼測試覆蓋率的 badge,我們可以使用 http://shields.io 來新增 badge,比如還有下載量,star數等這些badge

[![Build Status](https://travis-ci.org/wulv/del-expired-file.png)](https://travis-ci.org/wulv/del-expired-file)
[![Coverage Status](https://img.shields.io/coveralls/wulv/del-expired-file/master.svg?style=flat)](https://coveralls.io/github/wulv/del-expired-file?branch=master)

最終可以看到以下效果,當你看到一個 npm 如果顯示了測試覆蓋率,是不是頓時放心很多了呢?

六、釋出

至此我們基本已經寫好了一個相對比較健壯的倉庫了,現在釋出到 npm 上,如果沒有 npm 賬號的話,需要先註冊一下,然後執行

npm adduser

輸入使用者名稱和密碼,npm publish,這樣你的倉庫就釋出好了,如果要更新版本,要遵循 Semver(語義化版本號) 規範:

  1. 升級補丁版本號:npm version patch
  2. 升級小版本號:npm version minor
  3. 升級大版本號:npm version major

總結

總結一下上面使用到的工具和技術:

我們可以看到,如果只是實現功能,把目標定在僅僅實現需求上,寫這個 npm 包,用一個檔案三個函式就完成了,但是這樣的話,這個包估計也就只有你自己用了,以後的維護,修改都會比較麻煩,而使用這一套工具後,就健壯很多了。工作上也是這樣,我們需要追求卓越,不斷打磨自己的手藝,做出更完美的作品。

打個廣告,杭州有贊誠招前端開發工程師,我們在4月舉辦了一次前端技術開放日,詳情檢視連結:https://tech.youzan.com/fe-op…。公司福利多多:

  • 標配:MacBook,報銷:螢幕、滑鼠、機械鍵盤
  • 五險一金、980元/月的餐補、加班叫車費報銷、年度outing和體檢、每人每年有機會參加外部大會/培訓等
  • 各種高大上的聚餐、千奇百怪的團建,長期投食的零食架…
  • 高配電視、遊戲機、桌球、乒乓球、檯球、健身器械,還有四驅賽道等,等你來戰~

有意向的話請傳送簡歷到wulv#youzan.com

參考連結

相關文章