tampermonkey

使用者腳本(Userscript),或者我喜歡稱它們為猴子腳本(因為主要的幾個腳本引擎總喜歡以猴子作為命名,如 TamperMonkeyGreaseMonkey),作為瀏覽器端的擴充功能,為線上網站提供了更多客製化的服務。

顧名思義,使用者腳本就是使用者自行安裝、額外的腳本軟體,網站站主無法直接觀察到使用者用了什麼樣的腳本,也無法控管,因此若發生什麼狀況,網站是一概不負責的喔。所以安裝腳本時,請自行選擇可信賴的腳本來源,以避免發生資安問題、產生糾紛。

使用者腳本開發

使用者腳本的開發,基本上跟 JS 開發幾乎一致,然而使用者腳本的運行環境是由腳本引擎所提供的沙盒,與一般 JS 環境相仿但預設限制了更多存取控管,以提升安全性。

由於跟 JS 開發方式一致,熟悉 NodeJS 的夥伴們想必也想將 Webpack 的那一套開發流程帶入腳本開發,為腳本開發引進 NodeJS 的依賴管理、Webpack 所提供的特性(Code spliting、Optimization 等等)以及結合了 Webpack 生態圈的各式插件、加載器。

於是我製作了這麼一個 Webpack 插件── Webpack Userscript,將 Webpack 引進了使用者腳本開發的流程中。本插件支持了 Webpack 4 以後的版本。

其實它的任務很簡單,讓我簡單描述一下:

  1. 為 JS 檔冠上使用者腳本標頭[1]
  2. 將配置插件用的檔案加入 Webpack 編譯依賴中。
  3. 確保 JS 副檔名為使用者腳本慣例的 .user.js。(可選)
  4. 將使用者腳本標頭文本,額外生成於 .meta.js 中。(可選)

可使用以下命令進行安裝:

1
npm i webpack-userscript

設定方式與其他 Webpack 插件大同小異,都在 webpack.config.js 設定檔內進行。

使用者腳本標頭

一個使用者標頭的內文大致上長這個樣子:

1
2
3
4
5
6
7
8
9
// ==UserScript==
// @name @webpack-tampermonkey/test-simple-config
// @version 1.0.0
// @author MomoCow
// @description Simple config test for webpack-userscript.
// @homepage https://github.com/momocow/webpack-tampermonkey#readme
// @supportURL https://github.com/momocow/webpack-tampermonkey/issues
// @match *://*/*
// ==/UserScript==

在生成標頭時,首先插件會先去看專案的 package.json,在目前的插件版本中,以下模塊資訊將會作為標頭的預設值採用。

  • name
  • version
  • description
  • author
  • homepage
  • bugsbugs.url

其餘標頭欄位可經由 webpack.config.js 中,在建構插件實例時將設定作為第一參數給定。

可參考本文配置範例一節。

除此之外,設定中若沒有明定 @match@include 欄位用以匹配目標 URL 的話,預設設定為 @match *://*/*,匹配任意 URL(*://*/* 表示任意協定、任意主機、任意路徑)。

標頭可經由插件設定 headers 項進行配置,headers 項接受三種型態的設定,最簡單的用法為賦予一個物件,此物件內容即是標頭的鍵值對。

1
2
3
4
5
6
new WebpackUserscript({
headers: {
name: 'test',
match: 'https://google.com'
}
})

或是給予一個方法,此方法會收到一個參數,參數內容[2]可用於參考生成標頭物件,並且須將標頭物件返回。

1
2
3
4
5
6
7
new WebpackUserscript({
headers (data) {
return {
version: data.version + '.build-' + data.hash
}
}
})

或是給予一個字串,此字串為一個「可 require」的檔案路徑(也就是 .js.json 檔),並且 require 得到內容須為上述的標頭物件或是標頭物件生成方法任一。此設定檔也會被加入 Webpack 的編譯依賴中(影響到 watch 模式的重編觸發)。

.user.js

副檔名 .user.js 為使用者腳本的慣例,因此預設設定下,插件會確保檔案符合這個命名。

此行為為可選特性,可透過插件設定中的 renameExt 項設為 false,即禁用這個行為。

.meta.js

標頭生成後,插件預設會額外生成一個 .meta.js 副檔名的檔案,內容只有標頭文本而已。

.meta.js 為使用者腳本用以檢查更新的檔案,通常腳本引擎在 GUI 上都有更新個別腳本的按鈕,同時也有定期檢查更新的機制,只需將一段指向 .meta.js 檔案的 URL 賦給 updateURL 欄位即可[3],如此一來,可以減少檢查更新時占用的頻寬,不必整個腳本內容都下載,而是只下載標頭資訊的部分。

生成 .meta.js 為可選特性,可透過插件設定中的 metajs 項設為 false,即禁用這個行為。

Prettify

插件預設會進行標頭格式對齊,如下:

1
2
3
4
5
// ==UserScript==
// @name @webpack-tampermonkey/test-simple-config
// @version 1.0.0
// @author MomoCow
// ==/UserScript==

若將插件設定之 pretty 項設為 false 則會產生如下標頭。

1
2
3
4
5
// ==UserScript==
// @name @webpack-tampermonkey/test-simple-config
// @version 1.0.0
// @author MomoCow
// ==/UserScript==

配置範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const path = require('path')
const WebpackUserscript = require('webpack-userscript')
const dev = process.env.NODE_ENV === 'development'

module.exports = {
mode: dev ? 'development' : 'production',
entry: path.resolve(__dirname, 'src', 'index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: '<project-name>.user.js'
},
devServer: {
contentBase: path.join(__dirname, 'dist')
},
plugins: [
new WebpackUserscript({
headers: {
version: dev ? `[version]-build.[buildNo]` : `[version]`
}
})
]
}

這是我在另一個專案使用的開發設定,配合 webpack-dev-server 使用。

因為在開發腳本時,以往的模式,要嘛直接在腳本引擎提供的編輯器開發,直接保存然後刷新頁面,要嘛就要透過剪貼簿複製貼上。

這邊改由 webpack-dev-server 傳輸腳本,而開發者只需要利用腳本引擎的功能──自 URL 安裝腳本。以下以 TamperMonkey 為例,

想在 TamperMonkey 安裝腳本,其實最簡單的方式是直接在瀏覽器打開一段指向 .user.js 腳本檔案的 URL,且這段 URL 回應的 Content-Type 應為 text/javascript,若 TamperMonkey 已安裝在瀏覽器上,便會彈出腳本安裝的分頁。另外還可以透過 TamperMonkey 主控台中的「匯入匯出工具」標籤,使用 URL 匯入腳本。

透過 URL 使安裝過程變得相當容易,同時腳本的發布也可以經由 GitHub,直接使用代碼倉庫的檔案檢視器右上那個 Raw 得到的 URL 即可安裝腳本。

TamperMonkey 還會自行記憶來源 URL,即使沒有填寫 @updateURL 欄位,TamperMonkey 也會到這個 URL 檢查更新。

在範例中,webpack-dev-server 預設會開在 8080 埠,因此我們只需使用 http://localhost:8080/my-script.user.js 這個 URL 進行安裝。

範例中,還有一個值得一提的是 headers.version 的配置。在開發環境下,由於腳本更新頻繁,為了使腳本引擎能夠正確的反映更新,我們在 @version 欄位做了手腳,[version]-build.[buildNo] 其實是將 package.json 中的 version 欄位加上後綴,後綴內文包含了 [buildNo][buildNo] 為插件提供的一個可用變數,會隨著每次 Webpack 編譯而遞增,因此每次編譯後可以到腳本引擎的界面上進行腳本更新。

以 TamperMonkey 來說,可以在彈跳框中找到「檢查使用者腳本更新」更新所有腳本,或者到主控台點擊個別腳本的版本號欄位進行更新。


小結

透過這個插件,將開發階段的腳本更新、發布一事透過 webpack-dev-server 自動化,然而還是需要到腳本引擎手動觸發更新。HMR 尚未實際測試過,不過或許能夠為使用者腳本開發帶來更多的便利。


  1. 關於使用者腳本標頭的文件,可以參考 TamperMonkey 所提供的:https://tampermonkey.net/documentation.php?ext=dhdg↩︎

  2. 標頭生成方法的第一個參數內容,https://github.com/momocow/webpack-userscript#dataobject↩︎

  3. 可參考 Scriptish Wiki:https://github.com/scriptish/scriptish/wiki/Manual%3A-Metadata-Block#updateurl-new-in-scriptish↩︎