[Guide] 發佈 npm 套件 - 從手動到自動(0):專案與套件建置篇
keywords: deploy
, publish
, release
, CLI
, npm
對於 JavaScript 的開發者來說要發布 JavaScript 套件到 npm 上面,只要對於 npm 的專案架構本身已經有基本的理解,並不是太困難的事。在這一系列的文章中,會說明如何從專案開發到發佈 npm 套件的這個流程,從手動透過 npm 的指令發佈到自動化持續整合(Continuous Integration, CI)的過程做一個整理。
這裡,就讓我們先從手動開始。在這篇文章中會談到:
- 建立隸屬於某 scope 底下的 npm 套件
- 撰寫一個範例套件
- 透過 rollup 打包範例套件
- 透過 Jest 測試範例套件
這篇文章主要是建立一個範例的 npm 套件,以供後續說明發佈使用,並且說明套件中通常會包含打包和測試的流程,若想要直接看最後套件完整的程式碼,可以看此 Github 上的 Commit。
⚠️ 撰寫文章時使用的 npm 版本為 6.13.7。
註冊 npm 帳號
首先要請你到 npm 的官網上註冊自己的帳號。
建立 npm (scoped) 專案
一般的情況下,會習慣直接使用 npm init
這樣 的指令來啟動 npm 的專案,但如果你是要發佈到 npm 上的話,就需要留意專案的名稱在 npm 上是不能夠重複的,為了避免專案重複的問題,在 npm v2 以後開始支援 scopes 的用法,也就是讓專案在名稱上就可以看出是隸屬於某個使用者或某個組織底下,特色是專案名稱會用 @
開頭,如此避免專案名稱經常重複的問題。
舉例來說,JavaScrip 中經常用來支援語法相容性的 Babel 工具,現在就會隸屬於 @babel
下:
在建立專案時,你也可以考慮是否要讓它隸屬於個人或組織底下(scope)。在這裡因為只是示範用的專案,因此我就把這個專案直接隸屬於個人帳號底下,如此避免和他人的名稱相碰。
要建立 scope 的專案,只需在該專案資料夾中,初始化專案時加上 --scope=@<username>
的語法。例如:
$ mkdir function-benchmarker # 建立一個名為 function-benchmarker 的專案資料夾
$ cd function-benchmarker # 進入該資料夾中
$ npm init --scope=@pjchender # 建立隸屬於個人帳號底下的 npm 套件
執行後 npm cli 會問你一些專案設定的問題,這裡我們先直接使用預設值就好,所以就一路 enter 到底即可。如果想要直接全部採用預設值,不要一一按下 enter 的話,可以直接在 npm init
的最後加上 -y
即可,像是這樣:
$ npm init --scope=@pjchender -y
如此就初始化好一個 npm 的專案:
⚠️ 注意:scoped 的套件預設會是私人的(private),後續會再說明如何透過 npm 指令將其設為公開的套件。
撰寫套件
這裡主要是用來示範套件的發佈,就來寫一個簡單可以用來檢測函式效能方法。在根目錄新增一個 src
目錄,並在裡面新增一支 index.js
:
// ./src/index.js
const benchmarker = (testFunction, times = 1000000) => {
if (typeof testFunction !== 'function') {
throw new Error('Did not provide a valid function for test.');
}
const startTime = new Date().getTime();
let i = 0;
while (i < times) {
i++;
testFunction();
}
const endTime = new Date().getTime();
return endTime - startTime;
};
// 匯出函式
module.exports = benchmarker;
這裡的 benchmarker
方法,主要是把傳入的函式(testFunction
)預設執行一百萬次(times
)後,最後回傳所花的時間(ms)。
這裡因為是要開放給其他開發者可以使用的套件,所以最後會把 benchmarker
這個方法透過 CommonJS 的語法匯出(module.exports
)。
打包套件(可略過)
💡 提醒:多數套件都會經過打包的動作,以縮小檔案體積、增加語法相容性等等,但如何打包並不是這篇文章的重點,因此你只需要知道有打包這個動作,並且打包後的檔案會被放到 dist 資料夾中 。
npm 套件再發布前通常會經過「打包(bundle)」的動作,除了縮小檔案體積外,也可以讓套件同時支援在 CommonJS 或 ESModules 下使用,或者透過 Babel 等工具讓套件支援較舊的 JS 語法。打包的工具有許多,像是最常聽到的 webpack、rollupjs、Parcel、或過去常使用的 Gulp 等等,你可以選擇自己習慣的。
打包套件的過程不是這系列文章的重點,所以不會詳細說明設定檔中各欄位的意思,你只需要知道打包後會把原本放在 src
資料夾中原始碼,進行打包的動作後放到 dist
資料夾中。
這裡我們選擇使用 rollupjs 進行打包,首先先安裝 rollup 和相關 plugin 到專案中:
$ npm install rollup rollup-plugin-terser -D