jspmについて私が知っていること
今更感がありますが、jspmというツールについて私が知っていることです。
jspmとは
jspmとはブラウザ向けのモジュール管理システムです。
pros
npm、bower等と違いES6で導入されたモジュールのローディングシステムを利用して、
ランタイムでトランスパイル等を行う仕組みを導入しており、これまでのwatch等を利用して行っていたトランスパイルの手間を省けるのが大きなポイントとなります。
また、jspm install <モジュール名> でモジュールをjspm、npm、githubの3つのレポジトリからインストール可能な上、
モジュールは変換が行われ、ブラウザから直接SystemJSを利用してロード可能な状態でインストールされます。
なので必要なモジュールがあれば、node・ブラウザ等を特に気にせずインストールできます(できないものもあります)。
cons
jspm_packagesというディレクトリを新たに作成するため、node_modulesとは違うモジュールを管理する必要があります。
つまり同じモジュールがnpmとjspmで必要になっても両方にインストールする必要があるということです。
また、常にコンパイルを行うため、モジュールサイズが増えるとロードにかなり時間がかかるようになります。
さらに、使うモジュールにもよりますが、インストールしただけでは動かないモジュールも存在します。
使い方
インストール
npm install jspm@beta -g npm install jspm@beta --save-dev
jspm-cliはグローバル環境にjspmを入れていても、自動でローカルにインストールされたjspmを使用します。
なんで両方インストールします。
初期化
現在jspmは0.16がstableとなっていますが、0.17βも存在しており、
0.16と0.17ではかなり大きな変更点があるので、0.17βを基準に説明します。
jspmではjspm.config.jsという設定ファイルをコマンドライン、ブラウザ共に使用します。
中身はこんな感じのjsファイルになっています。
SystemJS.config({ paths: { "npm:": "jspm_packages/npm/", "test-package/": "src/" }, browserConfig: { "baseURL": "/" }, devConfig: { "map": { "plugin-babel": "npm:systemjs-plugin-babel@0.0.18" } }, transpiler: "plugin-babel", packages: { "test-package": { "main": "test-package.js", "meta": { "*.js": { "loader": "plugin-babel" } } } } }); SystemJS.config({ packageConfigPaths: [ "npm:@*/*.json", "npm:*.json" ], map: {}, packages: {} });
ただ、このファイルを0から作成する必要はなくて、jspm init
というコマンドで自動生成されます。
jspm init
を実行すると
最初に以下のように聞かれます。
もちろんYESで生成します。
次に設定方法を選びます。
設定方法はQuick Standard Customの三種類ありますが、特に特別な設定がなければQuickで問題ありません。
なのでQuickで設定を行います。
次にパッケージ名を決定します。個々で決定したパッケージ名は各モジュールのプレフィックスとして使用されます。
次はbaseURLの設定です。
baseURL自体は非常にわかりづらいですが、これはpackage.jsonからソースディレクトリへの相対パスとなります。
ここも特に変更はせず.
で問題ないです。
ま、問題あったら後で変えようの精神。
どんどん行きます。
次は設定ファイルへのパスです。これは上記のbaseURL内に含まれる必要があります。つまり、baseURLがここのパスを包含する形になります。
たぶん、jspm init
をpackage.jsonディレクトリで実行していると思うので、基本的には.
で問題ないです。
次行きましょう
お次はdevの設定です。dev環境で設定ファイルを分ける場合はYesにしてください。
設定ファイルを分けた場合は、--dev
オプションを付けてインストールしたモジュールの設定は全てdev環境の設定ファイルに記述されます。
めんどくさいのでNoで!
ほいで次はブラウザ向けのパスの設定です。ブラウザ上でファイルをロードするときのプレフィックスに使われます。
当然デフォルトのままで。
次はNode local projectへのパスという意味不明な設定ですが、これは凄く単純な設定でパッケージからディレクトリへのエイリアス設定です。
ここでいうと、test-packageという名前がsrcへのエイリアスとなります。まあ使わなくてもいいので気にしないでください。
srcのままで
次は上記のブラウザ版ですが、2つに分かれているユースケースが勉強不足によりわからないのでsrcのままで。
画像は省略します。
次はjspm_packagesへのパス設定。ここは変える人いないんじゃないかな。そのまま!
次はブラウザからjspm_packagesへのパス。そのまま!
画像もわすれた!
次はエントリーポイントとなるモジュールファイルへのパス。srcディレクトリ内にある必要があります。
次はトランスパイラーの設定。typescript選ばないならbabel一択でいいのでは?typescriptの場合は別途tsをインストールすれば良いので、
一旦babelで
やっと終わった。
成功すると以下のようなログが出ます。多分
依存のインストール
jspm install <module name> [options]
基本はこれ。例としてreactをインストールする場合
jspm install react
これでreact.jsがインストールされます。
しかしこれでインストールできないモジュールもあります。
デフォルトではjspmはモジュールをjspmレジストリから探索します。しかし、jspmレジストリにあるモジュールはまだ数が少ない為、
対応していないモジュールも数多くあるのです。
その場合は
jspm install npm:js-cookie
というようにnpm:
をプレフィックスとして付与します。
jspm組み込みのレポジトリとしてnpmの他にgithubもあります。
またレポジトリは拡張も可能で自分オリジナルのレポジトリにも対応可能です。
まともにインストールできないモジュール
npmのScoped Packageはjspmの仕様のようなのですが、期待したとおりにはインストールされません。
例えばcyclejsのcoreをインストールする場合
jspm install npm:@cycle/core
になるのですが、実際にインストールされる依存名はcoreになります。
つまり、namespaceが省略されてしまうのですね。
というわけでこのような場合はalias名を設定します。
jspm install @cycle/core=npm:@cycle/core
このように変更すれば@cycle/coreとしてモジュールがインストールされます。
使う
インストールした依存モジュールは普通にimportして使えます。
import * as react from 'React';
しかし困ったことにimport方法はやってみるまでわからないのです。
なのでマジで適当に書いてランタイムエラーがでるかチェックします…
Reactは上記のimportでうまくいきますが、
import React from 'react';
だと駄目です…
なのでほんとにインストールしたモジュールの実装方法次第です。
その辺の情報は以下に詳しい
Babel と TypeScript の ES6 modules の import の解釈の違い
ビルドする
さて開発するときは全てのモジュールをロードして、ランタイムでコンパイルして使えばよかったのですが、production環境でそれはいただけないですよね。
なのでミニファイしろ。
ミニファイにはsystemjs-builder
なるものを使います。
多分gulpのやつもあるはずなのだが、自分で作ってしまっているのでいつもそれを使っています。
調べたらあった。
Gulp SystemJS Build Tool
事前準備 npm isntall through2 async gulp-util lodash vinyl colors --save-dev
ソース
/** * @fileoverview * @author Taketoshi Aono */ 'use strict'; const through = require('through2'); const fs = require('fs'); const Builder = require('systemjs-builder'); const async = require('async'); const path = require('path'); const gutil = require('gulp-util'); const _ = require('lodash'); const Vinyl = require('vinyl'); module.exports = function(options) { const builder = new Builder('./'); const files = []; let mergedConfig = {}; const configFiles = Array.isArray(options.configFile)? options.configFile: [options.configFile]; configFiles.forEach(configFile => { const configStr = fs.readFileSync(configFile, 'utf8'); Function('SystemJS', configStr)({config: function(config) { config.baseURL = options.baseURL; if (options.replace && config.map) { _.forIn(options.replace, function(v, k) { config.map[k] = v; }); } mergedConfig = _.merge(mergedConfig, config); }}); }); builder.config(mergedConfig); function transform(file, encoding, callback) { files.push(file); callback(); } function flush(callback) { const self = this; async.forEachSeries(files, function(file, next) { require('colors'); const src = files[0].history[0]; console.log(('Strart build process ').yellow.bold + src); builder.buildStatic(src, options.build || {}) .then(function(output) { file.contents = new Buffer(output.source); if(options.build.sourceMaps === true) { self.push(new Vinyl({ cwd: file.cwd, base: file.base, path: file.path + '.map', contents: new Buffer(output.sourceMap.toString()) })); } self.push(file); next(); }) .catch(function(err) { console.log('Build error'); console.log(err); next(); }); }, callback); } return through.obj(transform, flush); };
これをplugin/build.jsに保存して、
gulp.task('minify', () => { const build = require('./plugins/build'); return gulp.src('lib/main.js') .pipe(build({ configFile: ["jspm.config.js"], build: { minify: true, sourceMaps: false, mangle: true, browser: true, globalDefs: { DEBUG: false } } })) .pipe(gulp.dest("dist")); });
みたいな感じで使ってます。
まとめ
インストール
npm install jspm@beta -g npm install jspm@beta --save-dev
初期化
jspm init
モジュールのインストール
jspm install <何かモジュール>
ミニファイ
gulp minify
後半雑になっちゃいましたが仕方ないと思います。
だって大変なんだもん。
以上です。