いまどきのJavaScriptライブラリ開発
最近、仕事上でJavaScriptライブラリを作って公開することが多くなってきました。その際のノウハウが溜まってきたので先日公開したSmartPhoto.jsを例にとって紹介したいと思います。
SmartPhoto.jsでは以下の5つの方法で開発したことが、いまどきっぽいなと感じています。
- Babel Browserifyを使ったモダンコーディング
- eslintを使ってソースコードの品質を担保
- jQuery未使用
- git tagとnpmとのバージョニングの紐付け
- CircleCIを使ったテスト
1. Babel Browserifyを使ったモダンコーディング
browserify ./src/index.js -t babelify -p licensify --standalone smartPhoto -o ./js/smartphoto.js
上記のように、browserify babelifyを使って、es2015でJavaScriptをかける環境にしています。一番便利なのが、モジュールのimportやexportが使えることですね。classをサポートしているのもいいです。また自分はhtmlもimportして使いたいためbabelrcにそのためのプラグインも追加しています。
babelrc
{ "presets": [ "es2015" ], "plugins": ["transform-html-import-to-string"] }
BrowserifyやWebpackをお使いの人はnpm経由でこのJavaScriptライブラリが使えるように、SmartPhotoクラスのexportもしています。
module.exports = SmartPhoto
2. eslintを使ってソースコードの品質を担保
またeslintを使ってソースコードの品質を担保しています。またプリセットとして、eslint-config-airbnbを使っているのですが、ルールが厳しすぎるので以下のように一部のルールを殺しています。
{ "extends": [ "airbnb/base" ], "env": { "browser": true }, "globals": { "document": true, "window": true, "event": true }, "rules":{ "comma-dangle": 0, "no-underscore-dangle": 0, "class-methods-use-this": 0, "no-param-reassign": 0, "no-mixed-operators": 0, "max-len": 0, "no-continue": 0, "no-undef": ["error", { "typeof": true }], "no-restricted-properties": 0 } }
3. jQuery未使用
SmartPhoto.jsはjQueryに対応した、jquery-smartphoto.jsというscriptも同時に配布しているのですが、実を言うと内部的には全くjQueryを使用していません。理由としては2つあって、一つはBrowserifyを使ってbundleした際のファイルサイズを抑えるためです。もしjQueryを利用する場合はjQueryもimportする必要があります。最近ではjQueryを使わないプロダクトの方がwebpackやbrowserifyなどのbundleツールとの相性がいいため重宝されるのも理由の一つです。
生のJavaScriptを書くのは以外と辛いです。普段どれだけjQueryに助けられていたかを改めて実感しました。。。ちなみに以下のサイトを参考にして実装しました。
Examples of how to do common event, element, ajax and utility operations with plain javascript.
以下のように、よく使うjQueryの便利な機能は以下のように小さなモジュールに分解してexportしておくと便利です。
module.exports.append = (element,string) => { const parser = new DOMParser(); const doc = parser.parseFromString(string, 'text/html'); element.appendChild(doc.querySelector('body').childNodes[0]); } module.exports.addClass = (element,className) => { if (element.classList) { element.classList.add(className); } else { element.className += ` ${className}`; } } module.exports.removeClass = (element,className) => { if (element.classList) { element.classList.remove(className); } else { element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); } }
なお、jQuery版のjquery-smartphoto.jsはインターフェースとして、$.fn.smartPhotoを呼び出せるようにしていますが、ただ内部的にはnewしているだけです。
/adaptor/jquery.js
const applyJQuery = (jQuery) => { jQuery.fn.smartPhoto = function(settings) { if (typeof settings === 'strings'){ } else { new smartPhoto(this.selector,settings); } return this; } }
4. git tagとnpmとのバージョニングの紐付け
npm install smartphoto --save
また、SmartPhotoはnpmに公開しているため上記のコマンドでダウンロードしてimportして使うことができます。npmに新しいバージョンのパッケージを公開する際に同時にgitにもtagがつくようにしています。
どのようにバージョン管理をしているかというと、まず公開前にテストのスクリプトを動かし、その後npm version patchというコマンドでpackage.jsonのバージョンを書き換え、jsをビルドした後、gitなどにタグ付けやpushを行うscriptを実行しています。以下がその部分のnpm scriptsです。
package.jsonの一部
"patch": "npm run test && npm version patch && npm run build:js && node ./tools/index.js", "minor": "npm run test && npm version minor && npm run build:js && node ./tools/index.js", "major": "npm run test && npm version major && npm run build:js && node ./tools/index.js"
tools/index.js
"use strict" const cmd = require('node-cmd'); const co = require('co'); const fs = require('fs-extra'); const pkg = require('../package.json'); const SystemPromise = (cmd_string) => { return new Promise((resolve, reject) => { cmd.get( cmd_string, (data, err, stderr) => { if ( err ) { console.log(err) } if ( stderr ) { console.log(stderr) } resolve(data) } ) }) } co(function *() { try { yield SystemPromise(`git add -A`); yield SystemPromise(`git commit -m "v${pkg.version}"`); yield SystemPromise(`git push origin v${pkg.version}`); yield SystemPromise(`git push origin master`); yield SystemPromise(`npm publish`); } catch ( err ) { console.log(err) } });
CircleCIを使ったテスト
また、テストを手動で毎回テストするのは非常に大変です。
CircleCIを使えばgithubにpushした段階で勝手にテストコードを実行してくれるので非常に便利です。circle.ymlではnode.jsのバージョンを指定して、テスト時のコマンドなどを定義することができます。
circle.yml
machine: node: version: 6.2.0 dependencies: override: - "npm install" test: override: - "npm run test"
テストには現在のところmocha, power-assert, nightmare.jsを使っています。そのあたりのことは以前にブログを書きました。
![]()
CircleCIでブラウザテストを走らせてみた | js | Horic Design
Horic Design
こんにちは。堀 悟大(@steelydylan )です。最近はブラウザでのテストの自動化に興味があります。今回はCircleCIを使ってgithubにpush...
まとめ
最近だいぶ、いまどきのライブラリの開発手法が確立されてきたんじゃないかなと思います。次はFlowを使った型定義とかでしょうか?
何か気づいた点がありましたら優しく教えてくだされば幸いです。
SmartPhoto.jsのページ
The most easy to use responsive image viewer especially for mobile devices