読者です 読者をやめる 読者になる 読者になる

YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

TypeScript 2.0.0とVisual Studio Code 1.4でChrome拡張を書く

はじめに

こんにちは。ナルゲンボトル愛好家です。ふと思い立ってChrome拡張作ろうかなと思ったんですが、どうせならTypeScriptでやるのがいいじゃねえかってことでやりました。で、最近ブログ書いてないし、なんかまとめたらいい感じかなと思ったのでまとめます。

構成

.
├── dist // 生成された拡張
│   ├── context-menu.js
│   ├── icons
│   │   └── icon48.png
│   └── manifest.json
├── gulpfile.js
├── .eslintrc.json
├── package.json
├── static
│   ├── icons
│   │   └── icon48.png
│   └── manifest.json
├── ts
│   └── src
│       └── context-menu.ts // Chrome拡張をTypeScriptでやってくやつ
└── tsconfig.json

毎回思うんですが、ちょっとのことやりたいだけなのに xxxx.json みたいな設定ファイルばっかりでほんとにだるい。だれかなんとかしてほしい。

下準備

typescript@2.0.0 を Visual Studio Code の参照先にする

あたりまえだけどまずTypeScriptが必要。今回は新しいやつを早く使いたかったんで @2.0.0 をグローバルに入れた。バージョン使い分けるほど使ってません。

$ npm i -g typescript@2.0.0

最近はエディタは軟派にVisual Studio Codeをよく使ってるんだけど、こいつがデフォルトで使うTypeScriptの設定を変えてやらないと新しい文法とか出てきた時にlintがうるさいので変えておく。Visual Studio CodeのUser Settingsでこんな感じの設定をしておく。ワークスペースごとに使うTypeScriptのバージョン違うという人がいたら、Workspace Settingにして、Workspaceのtypescriptを参照するようにしたらいいんじゃねえかと思いますが、やったことはありません。

"typescript.tsdk": "/Users/ymotongpoo/.nvm/versions/node/v6.3.0/lib/node_modules/typescript/lib"

Chrome Extension APIの型定義ファイルを参照する

TypeScriptの恩恵にあずかりたいので型定義ファイルを持ってくる。当然Chrome本体にモジュールはあるため、型定義ファイルだけあれば良いので、 id:otiai10 に感謝を唱えつつモジュールを落とす。

$ npm i @types/chrome

あとは普通に使えば良い。tsconfig.jsonとcontext-menu.tsはこんな感じでやっていく。tsconfig.jsoncompilerOptionsChromeがES5で頼むって感じだったはずなので target に気をつけるのと、あとでbrowserifyするんで modulecommonjs にしときました。momentは単に今回使ったので特に重要じゃないです。

{
    "compilerOptions": {
        "target": "ES5",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false
    },
    "files": [
        "./ts/src/context-menu.ts",
        "./node_modules/@types/chrome/index.d.ts",
        "./node_modules/@types/filesystem/index.d.ts",
        "./node_modules/@types/filewriter/index.d.ts",
        "./node_modules/@types/moment/index.d.ts"
    ]
}

context-menu.tsでは参照しときましょう。

/// <reference path="../../node_modules/@types/chrome/index.d.ts" />

いい感じになった。

f:id:ymotongpoo:20160805131904p:plain

gulpの設定

grunt だとか gulp だとかなんか周辺ツール多すぎてホント嫌になってくるんですが、まあそういう形で回っちゃってるものはしょうがないので文句言いながら gulpfile.js 書きます。必要なパッケージを入れときます。

$ npm i -g --save-dev browserify
$ npm i --save-dev vinyl-source-stream tsify gulp-jsmin
$ npm i -u typescript@2.0.0

ローカルにもtypescript入れるのはtsifyでtypescriptモジュールをrequireしてるため。tsifyだけにしちゃうと依存してるデフォルトのバージョンが入っちゃうんでこうやってる。最初はgulp-typescript使ったりしてたんだけど、公式サイト見に行ったら使わないで一発でbrowserifyしてて楽だったので真似した。

const gulp = require('gulp');
const del = require('del');
const browserify = require('browserify');
const source = require('vinyl-source-stream');
const tsify = require('tsify');
const jsmin = require('gulp-jsmin');

const config = {
    browserify: {
        opts: {
            basedir: '.',
            entries: ['./ts/src/context-menu.ts'],
            cache: {},
            packageCache: {},
            debug: false
        }
    },
    source: {
        target: 'context-menu.js'
    }
};

gulp.task('copy', [], () => {
    return gulp.src('./static/**/*')
        .pipe(gulp.dest('./dist'));
});

gulp.task('build', ['copy'], () => {
    return browserify([], config.browserify.opts)
        .plugin(tsify)
        .bundle()
        .pipe(source(config.source.target))
        .pipe(gulp.dest('./dist'));
});

gulp.task('minify', ['build'], () => {
    gulp.src(['./dist/**/*.js])
        .pipe(jsmin())
        .pipe(gulp.dest('./dist'));
});

gulp.task('clean', [], () => {
    return del(paths.dist.dir);
});

gulp.task('default', ['minify']);

使ってたmoment.jsの型定義ファイルだけインストールして、肝心のmomentパッケージ自体をインストールし忘れたことで、当然concatされないことを気付かずに悩むみたいなバカなことをしたけど、まあ動いちゃえば単純。

distをパッケージにする

普通にここに書いてあるとおりにやるだけです。Macな人はコマンドが若干異なるけどほぼ一緒です。

$ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' --pack-extension=dist

というわけで dist.crx が出来て嬉しいですね。

おわりに

設定ファイル何個書かせるんだよ。めんどくさい。