使用 TypeScript 開發自己的套件
最近用 TypeScript 寫了一個簡單的前端套件並發佈到 NPM 上,套件功能是幫 highlight.js 加上對 Laravel Blade 樣板語法的支援。雖然這個功能已經有人幫忙寫了套件,但是我發現這些套件在部分語法上都有缺失,也長年沒有人維護了,所以想說自己來寫一個新的。
將套件成功發佈到 NPM 之後,才發現原來發佈套件並不困難,不過仍然有許多地方需要注意,因此簡單的寫篇文章紀錄一下。
初始化套件
新增一個空的資料夾。
mkdir highlight-blade
在資料夾內進行套件初始化,新增一個 package.json
檔案。
cd highlight-blade
npm init
執行 npm init
後需要輸入一些套件的基本資訊。 許多資訊不用一開始就輸入,之後可以在 package.json
中設定。
package name: (highlight-blade)
version: (1.0.0) 0.0.1
description: A highlight.js plugin for laravel blade template
entry point: (index.js) dist/index.js
test command: vitest
git repository: https://github.com/yilanboy/highlight-blade.git
keywords: highlight.js,laravel,blade
author: yilanboy
license: (ISC) MIT
type: (commonjs) module
這裡說明一點,因為我是使用 TypeScript 開發,需要進行編譯才能生成瀏覽器可以執行的 JavaScript 檔案。 這些 JavaScript 檔案我會放在 dist/
資料夾底下,所以預設的 entry point
我使用 dist/index.js
。
來看看 npm init
生成的 package.json
檔案。
{
"name": "highlight-blade",
"version": "0.0.1",
"description": "A highlight.js plugin for laravel blade template",
"keywords": [
"highlight.js",
"laravel","
blade"
],
"homepage": "https://github.com/yilanboy/highlight-blade#readme",
"bugs": {
"url": "https://github.com/yilanboy/highlight-blade/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/yilanboy/highlight-blade.git"
},
"license": "MIT",
"author": "yilanboy",
"type": "module",
"main": "dist/index.js",
"scripts": {
"test": "vitest"
}
}
初始化後的 package.json
其實還缺少一些設定,我會在後續依依補上。首先來安裝依賴套件與 TypeScript 吧。
安裝依賴套件
有了 package.json
後,就可以來安裝依賴套件了!我們需要使用 highlight.js 的 API 來寫 Laravel Blade 的語法著色定義。因此需要先安裝 highlight.js。
npm install highlight.js
安裝 TypeScript
TypeScript 這個套件只有開發的時候會用到,所以安裝的時候需要加上 --save-dev
的參數。這樣從 NPM 上面下載套件時,NPM 就不會連 TypeScript 也一起下載。
npm install --save-dev typescript
使用 tsc
指令生成 TypeScript 設定檔案 tsconfig.json
。
node_modules/.bin/tsc --init
以下是我個人的 tsconfig.json
設定,我是參考 Matt Pocock 大的 The TSConfig Cheat Sheet 來設定,你可以根據你的需要進行調整。
/* https://www.totaltypescript.com/tsconfig-cheat-sheet */
{
"compilerOptions": {
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": true,
/* Strictness */
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
/* If transpiling with TypeScript: */
"module": "NodeNext",
"outDir": "dist",
"sourceMap": true,
/* if you're building for a library: */
"declaration": true,
/* If your code runs in the DOM: */
"lib": [
"es2022",
"dom",
"dom.iterable"
]
},
"include": [
"src/**/*"
]
}
include
代表需要編譯哪個目錄下的 TypeScript 檔案,outDir
代表會將編譯後的 JavaScript 檔案放在哪裡。
注意不要將編譯產生的檔案加入版本控制中,因此
dist/
資料夾應該加入.gitignore
中。
之後就可以新增一個 src/
資料夾,開始進行開發。先在 src/
底下新增一個檔案 index.ts
。
import blade from './blade.js'
export default blade;
接下來在 src/
底下新增一個檔案 blade.ts
,使用 highlight.js API 來定義 Laravel Blade 的語法著色。
import type {CallbackResponse, HLJSApi, Language} from 'highlight.js';
export default function (hljs: HLJSApi): Language {
// 在這裡使用 highlight.js 的 API 來定義 Laravel Blade 的樣板語法著色
// 以下省略...
}
然後輸入 tsc
指令進行編譯。
node_modules/.bin/tsc
編譯後你就會發現多出了一個 dist/
資料夾,底下會有編譯後的檔案。
dist/
├── blade.d.ts
├── blade.js
├── blade.js.map
├── index.d.ts
├── index.js
└── index.js.map
1 directory, 6 files
編譯出來的檔案會包含類型定義檔案 .d.ts
,它的主要目的是為 TypeScript 提供有關 JavaScript API 的類型資訊,這樣如果別人使用 TypeScript 引入我們的套件時,就不會出現套件型別未知的錯誤。
我們可以在 package.json
中設定類型定義檔案的路徑。
{
/* ... */
"main": "dist/index.js",
"types": "dist/index.d.ts"
/* ... */
}
程式碼修改後要一直輸入 tsc
指令進行編譯有點太麻煩了,我們可以在 package.json
中加上自己的腳本。
{
/* ... */
"scripts": {
"test": "vitest",
"build": "tsc",
"dev": "tsc --watch"
}
/* ... */
}
這樣就可以使用 npm run dev
指令來隨時偵測程式碼的修改並自動產生編譯後的 JavaScript 檔案,或是使用 npm run build
來編譯出 JavaScript 檔案。
使用 Vitest 撰寫測試
你可能發現我在腳本中有一個 "test": "vitest"
,但卻無法執行,那是因為我們還沒有安裝 Vitest。Vitest 是一個很好用的單元測試框架,可以用來驗證程式碼是否真的如我們所預期的正常執行。
安裝 Vitest。
npm install --save-dev vitest
新增一個資料夾 tests/
,並在底下新增一個測試檔案 highlight.test.ts
。
在裡面寫一些基本的測試。
import {describe, expect, it} from 'vitest';
import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';
import xml from 'highlight.js/lib/languages/xml';
import php from 'highlight.js/lib/languages/php';
import blade from '../src/index.js';
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('blade', blade);
hljs.registerLanguage('php', php);
describe('highlight laravel blade template', () => {
it('should highlight the @if directive as keyword', () => {
const code = `
@if ($isTrue)
<p>Yes</p>
@else
<p>No</p>
@endif
`;
const result = hljs.highlightAuto(code, ['blade']);
expect(result.value)
.to.contain('<span class="hljs-keyword">@if</span>')
.to.contain('<span class="language-php"><span class="hljs-variable">$isTrue</span></span>')
.to.contain('<span class="hljs-keyword">@else</span>')
.to.contain('<span class="hljs-keyword">@endif</span>');
});
// 以下省略...
});
然後就可以開始執行測試了。
npm test
# or
npm run test
發佈到 NPM
發佈套件相當簡單,如果你的套件沒有與其他套件撞名的話,只要申請一個 NPM 的帳號,就能直接發佈你的套件。
申請帳號這裡不多贅述,申請後輸入指令,就能透過瀏覽器登入獲得 NPM 的授權。
npm login
發佈套件之前,我們需要告訴 NPM 我們要上傳哪些檔案到 NPM 上面,注意不是直接上傳 src/
底下的 TypeScript 檔案,這些檔案瀏覽器是看不懂的,我們應該要上傳的是編譯後的 JavaScript 檔案。
在 package.json
告訴 NPM 我們要上傳編譯後的資料夾。
{
/* ... */
"files": [
"/dist"
],
/* ... */
}
接下來就可以發佈套件了。
npm publish --access public

發佈成功後,就可以從 NPM 上面下載自己寫的套件了
npm install highlight-blade
檢查可以發佈的版本
原本推送 1.0.0 版本到 NPM 時,發現被 NPM 的政策擋了下來,理由是之前已經發佈過這個版本了。 我一開始覺得很疑惑,我根本就沒有送過 1.0.0 版本啊!後來才發現原來是之前有退回發佈的關係 (Unpublish)。 使用過的版本號碼,即使退回發佈也不能再次使用。
套件發佈後的 72 小時內,你可以選擇退回發佈。
你可以使用這個指令來查看你目前有發佈過哪些版本號碼。
npm view --json
輸出結果有一個部分會列出你之前使用過的號碼。
{
/* ... */
"time": {
"created": "2025-01-15T08:20:13.194Z",
"modified": "2025-01-21T03:49:51.769Z",
"1.0.0": "2025-01-07T10:37:07.072Z",
"1.0.1": "2025-01-07T10:45:59.308Z",
"1.0.2": "2025-01-07T13:46:38.976Z",
"1.0.3": "2025-01-07T14:04:26.164Z",
"0.0.1": "2025-01-15T08:20:13.369Z",
"0.0.2": "2025-01-17T04:58:30.219Z",
"0.0.3": "2025-01-17T05:03:49.809Z",
}
/* ... */
}
使用 GitHub Action 發佈到 NPM
用人工的方式發佈到 NPM 容易出錯,建議使用 CI/CD 的方式發佈套件比較好。在資料夾底下新增一個檔案 .github/workflows/publish.yaml
,這樣當我們在 GitHub 上面發佈一個新的版本,GitHub Action 就會被觸發,開始執行測試並將編譯後的 JavaScript 檔案發佈到 NPM 上面。
name: Publish package to npm
on:
release:
types: [ published ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 23
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
# 這裡需要安裝 rollup-linux-x64-gnu 來避免執行 vitest 時出現錯誤
# 詳細可以參考這個 issue: https://github.com/vitejs/vite/discussions/15532
run: npm install && npm install @rollup/rollup-linux-x64-gnu --save-optional
- name: Run tests
run: npm test
- name: Publish to npm
# --provenance:這個參數用於在發佈的套件中包含來源資訊。這有助於提高套件的可信度和安全性,讓使用者可以追溯套件的來源。
run: npm run build && npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}