最近JSを利用するときは、依存モジュールはnpmを利用し、ES6やTypeScriptの仕様を開発には使った上で、ブラウザ用にコンパイルして配信するようになってきている。また同時にネットワークの負荷を下げるためにminifyを行う場合もある。
minifyはライセンスが絡むと少し難しい。例えばコメントを全て削除してしまうとライセンスコメントまで消えてしまう。この問題にはみんながそれぞれの手法で対処しているみたい。1年ほど前の記事でクライアントサイドJavaScriptのライセンス管理 | エンジニアブログ | GREE Engineering というものがあり、いろんなJSのコンパイルのためのライブラリが独自でライセンスの形式を決めていて、それにマッチしないものは消えてしまう、みたいな辛いことが起きてそうだった。
そこで今回は自分の勉強も兼ねて、npmのモジュールを含めてブラウザ用にコンパイルし、ライセンスコメントも残すための最小のコンパイル環境を作ってみる。
やりたいこと
npmでインストールしたモジュールを使い、ブラウザで動かすようのJSを書き、それをコンパイルしてminifyする。この時に依存モジュールのライセンスコメントを出力する。
利用する技術
- npm https://www.npmjs.com/
- 依存モジュール管理
- gulp http://gulpjs.com/
- ビルド環境
- browserify http://browserify.org/
- ブラウザ用JSへのコンパイル
- uglify
- minifyに利用
- licensify https://github.com/twada/licensify
- ライセンスを残すために利用
依存モジュールをインストールする
プロジェクトのrootでnpm initして適切にpackage.jsonの設定をしたあと、必要なモジュールを入れていく。
# ビルド用 npm install gulp --save-dev npm install browserify --save-dev npm install gulp-uglify --save-dev npm install vinyl-source-stream --save-dev npm install vinyl-buffer --save-dev npm install licensify --save-dev # ブラウザのJSで利用したいもの npm install jquery --save-dev npm install lodash --save-dev
JSを用意する
jqueryとlodashを利用するJSを用意する。今回はとりあえずcommonjs形式でrequireしているだけ。
src/js/app.js
var $ = require('jquery'); var _ = require('lodash');
gulpでライセンスを残しながらJSをビルドする環境を作る
以下の様なgulpfile.jsを用意することで、先ほど書いたやりたいことを実現できるbuild-jsタスクを用意できる。
gulpfile.js
var gulp = require('gulp'); var browserify = require('browserify'); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); var uglify = require('gulp-uglify'); gulp.task('build-js', function () { return browserify({ entries: './src/js/app.js' // どのファイルからビルドするか }).plugin('licensify') // licensifyプラグインの有効化 .bundle() // browserifyの実行 .pipe(source('app.js')) .pipe(buffer()) .pipe(uglify({ preserveComments: 'license' // ライセンスコメントを残しつつminify })) .pipe(gulp.dest('./static/js')); // 出力 });
これを実行することで、src/js/app.jsのファイルがビルドされて最終的にminifyされたJSがstatic/js/app.jsに出力される。
もう少し詳しく解説する。
licensifyプラグインを通しbrowserifyする
ライセンスコメントを確実に残すためにlicensify を利用する。browserifyのAPIでファイルを指定し、pluginメソッドでlicensifyを指定することで、licensifyプラグインを有効にした状態でJSをビルドすることが出来る。
browserifyをつかった後にvinyl-source-streamを使ったり、vinyl-bufferを使ったりしているのは、gulp と browserify と vinyl の話 - <body> やgulpとBrowserifyでJSをビルドしてみた - Qiita を参考に。
ライセンスコメントを残す設定でuglifyする
gulp-uglifyのpreserveCommentsにlicenseを指定することでlicenseが記述されたコメントを残すことが出来る。
そもそもlicensifyを使わずにこのオプションを使えばライセンスコメントを残せると思っていたのだが、以下の二つの理由によりlicensifyを通したほうが確実と判断した。
- 全ての形式のライセンスコメントに対応しているかわからない
- いろいろ試してみたところなぜか出力されないライセンスコメントがあった
- それがuglifyの奥底で起こってそうでデバッグにかなり時間が取られそうだったので諦めた
実際に実行してみる
以下のコマンドでビルドが行える。./node_moduels/.bin/gulpを利用しているのはグローバルなgulpコマンドを利用したくないため。
./node_modules/.bin/gulp build-js
するとstatic/js/app.jsにしたのように出力されるのでうまく行っていることが分かる。
/** * Modules in this bundle * @license * * jquery: * licenses: MIT * author: jQuery Foundation and other contributors * maintainers: dmethvin <dave.methvin@gmail.com>, scott.gonzalez <scott.gonzalez@gmail.com>, m_gol <m.goleb@gmail.com>, timmywil <timmywillisn@gmail.com> * homepage: http://jquery.com * version: 2.1.4 * * lodash: * license: MIT * author: John-David Dalton <john.david.dalton@gmail.com> * maintainers: jdalton <john.david.dalton@gmail.com>, mathias <mathias@qiwi.be>, phated <blaine@iceddev.com>, kitcambridge <github@kitcambridge.be>, d10 <demoneaux@gmail.com> * contributors: John-David Dalton <john.david.dalton@gmail.com>, Benjamin Tan <demoneaux@gmail.com>, Blaine Bublitz <blaine@iceddev.com>, Kit Cambridge <github@kitcambridge.be>, Mathias Bynens <mathias@qiwi.be> * homepage: https://lodash.com/ * version: 3.10.1 * * This header is generated by licensify (https://github.com/twada/licensify) */ !function e(t,n,r){function i(u,a){if(!n[u]){if(!t[u]){var s="function"==typeof require&&require;if(!a&&s)return s(u,!0);if(o)return o(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[u]={exports:{}};t[u][0].call(l.exports,function(e){var n=t[u][1][e];return i(n?n:e)},l,l.exports,e,t,n,r)}return n[u].exports}for(var o="function"==typeof require&&require,u=0;u<r.length;u++)i(r[u]);return i}({1:[function(e,t,n){/*! * jQuery JavaScript Library v2.1.4 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2015-04-28T16:01Z */ !function(e,n){"object"==typeof t&&"object"==typeof t.exports?t.exports=e.document?n(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t="length"in e&&e.length,n=Z.type(e);return"function"===n||Z.isWindow(e)?!1:1===e.no // 以下ずっとminifyされたjsが続く