Userscript を TypeScript で書く
概要
UserScript を TypeScript で開発するための方法を記す.
方法はいくつかあるが,GrasyFork などに投稿する際には minify や難読化がかかっていてはいけないので,その点に引っかからないように bundle する必要がある.今回は Rollup を使う.
環境の準備
$ npm init
package name: (hoge)
version: (1.0.0)
description: 説明文を入力する
entry point: (index.js)
test command: jest
git repository:
keywords:
author: xxx
license: (ISC) MIT
$ npm install --save-dev typescript sass rollup prettier rollup-plugin-html rollup-plugin-scss@3 rollup-plugin-typescript ts-jest jest eslint-plugin-prettier eslint-config-prettier eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin @types/jest @types/tampermonkey
package.json を次のようにする.具体的には,
- entry point (
"main": "index.js",
) を消す - typeRoots を追加する
- scripts 内に bundle などを追加する
- Linux のみサポートなら下記で OK.
- Windows 上で開発したいなら,
"lint": "tsc --noEmit && eslint \"src/**/*.ts\"",
とかにする;
で区切ったままだとUnknown compiler option '--noEmit;'. Did you mean 'noEmit'?
と言われる- eslint で探すファイルのパスをシングルクォートで囲んだままだと,
No files matching the pattern
を言われる
- repository, bugs, homepage に git の情報を追加する(あれば)
{
"name": "hoge",
"version": "1.0.0",
"description": "説明文を入力する",
"typeRoots": [
"types",
"node_modules/@types"
],
"scripts": {
"lint": "tsc --noEmit; eslint 'src/**/*.ts'",
"lint:fix": "tsc --noEmit; eslint --fix 'src/**/*.ts'",
"bundle": "rollup -c",
"test": "jest"
},
"repository": {
"type": "git",
"url": "git+https://github.com/xxx/hoge.git"
},
"author": "iilj",
"license": "MIT",
"bugs": {
"url": "https://github.com/xxx/hoge/issues"
},
"homepage": "https://github.com/xxx/hoge#readme",
"devDependencies": {
"@types/jest": "^26.0.24",
"@types/tampermonkey": "^4.0.2",
"@typescript-eslint/eslint-plugin": "^4.28.5",
"@typescript-eslint/parser": "^4.28.5",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"rollup": "^2.55.1",
"rollup-plugin-html": "^0.2.1",
"rollup-plugin-scss": "^3.0.0",
"rollup-plugin-typescript": "^1.0.1",
"sass": "^1.37.5",
"ts-jest": "^27.0.4",
"typescript": "^4.3.5"
}
}
rollup.config.js を作成する.ここでは,src/main.ts をエントリポイントにしている.なお,以下の点に留意すること.
@match
の記述は適宜変更すること.@exclude
,@require
,@resource
などは適宜追加すること.@grant
の記述は適宜変更すること.
import typescript from "rollup-plugin-typescript";
import html from "rollup-plugin-html";
import scss from 'rollup-plugin-scss';
import packageJson from "./package.json";
const userScriptBanner = `
// ==UserScript==
// @name ${packageJson.name}
// @namespace xxx
// @version ${packageJson.version}
// @description ${packageJson.description}
// @author ${packageJson.author}
// @license ${packageJson.license}
// @supportURL ${packageJson.bugs.url}
// @match https://example.com/*
// @grant none
// ==/UserScript==`.trim();
export default [
{
input: "src/main.ts",
output: {
banner: userScriptBanner,
file: "dist/dist.js"
},
plugins: [
html({
include: "**/*.html"
}),
scss({
output: false
}),
typescript()
]
}
];
tsconfig.json を作成する.es2017
では新しすぎる場合は,es6
あたりにしておく.strict
は様子を見ながら,大丈夫そうなら true
にする.
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"typeRoots": [
"./types",
"./node_modules/@types"
],
"strict": true
},
"exclude": [
"node_modules"
]
}
.eslintrc.json を作成する.なお eslint の “indent” は prettier のインデントとバッティングするので,指定しないで prettier に任せる.
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:prettier/recommended"
],
"plugins": [
"@typescript-eslint"
],
"env": {
"browser": true,
"es2017": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"project": "./tsconfig.json",
"ecmaVersion": 2017
},
"rules": {
// "indent": [
// "error",
// 4
// ],
"@typescript-eslint/no-use-before-define": [
"error",
{
"functions": false,
"classes": false
}
],
"prettier/prettier": [
"error",
{
"printWidth": 120,
"tabWidth": 4,
"singleQuote": true
}
]
}
}
なお,@types/tampermonkey
をインストールしない場合は,types ディレクトリを作成し,以下の tampermonkey-module.d.ts を複製する.
開発
rollup.config.js に記載した .ts ファイル(今回は src/main.ts) をエントリポイントとしてコードを記述していく.
main.ts の中身は,たとえば次のような感じで OK.
import { Hoge } from './hogehoge';
(() => {
// 何かする
Hoge.doSomething();
console.log('Hello World!');
})();
適宜,types ディレクトリに *.d.ts を追加していく.
CSS, HTML の import
たとえば,rollup-plugin-scss 経由で scss の中身を string として import したいとき,types/scss.d.ts を作成し,次のように記述する.
declare module "*.scss" {
const content: string;
export default content;
}
import する側は,通常通り次のような書き方で OK. これで,rollup されるときには,scss の記法から css の記法に変換されたものが変数 css の中身に入るので,
import css from './hoge.scss';
export const hoge = (): void => {
GM_addStyle(css);
};
細かい設定を変えたいときは,rollup.config.js の中身を弄る.
HTML の中身を string として読みたいときも,大まかな手順は同じで,types/html.d.ts など適当な名前で定義ファイルを作成すればよい.
Linter
$ npm rum lint:fix
バンドル
以下を実行すると,dist/dist.js に内容が吐かれる.
$ npm run bundle
ブラウザに読ませる
開発時は,次のようなものを用意して,通常時に使うほうを無効化すると便利.
// ==UserScript==
// @name hoge-dev
// @namespace iilj
// @version 1.0.0
// @description 開発用のスクリプトです。ローカルからビルド済みスクリプトをロードします。
// @author iilj
// @license MIT
// @require file://[リポジトリの親ディレクトリへのパス]/hgoe/dist/bundle.js
// @supportURL https://github.com/xxx/hoge/issues
// @run-at document-end
// @match https://example.com/*
// ==/UserScript==
ただし,拡張機能に対してローカルファイルへのアクセスを許可する必要がある.ブラウザの拡張機能一覧画面から,拡張機能ごとに設定を行える.
なお,上記スクリプトは,rollup.config.js に記載したのと同じように,@exclude
, @require
, @resource
, @grant
などを設定する必要がある.
また,ファイルの場所は file:///home/hoge/...
のように,通常はルートから記述する.
以上