node-webkit特有の問題にはまった
node-webkitでアプリを開発していて、node-webkitの環境ならでは?の問題にはまったのでメモ。
前提
% bower install eonasdan-bootstrap-datetimepicker --save
nodeのcontextではなくJavaScriptのcontextでの話。
アプリでbootstrap 3対応のdatetimepickerを使おうとしたら、読み込み時点でnot defineでエラー。
読み込まれているJSは公式に書いてあるとおりの下記JSを結合したもの*1
<script type="text/javascript" src="/bower_components/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="/bower_components/moment/min/moment-with-langs.min.js"></script> <script type="text/javascript" src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <script type="text/javascript" src="/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js"></script>
エラーの箇所はdatetimepickerが依存するmomentがあるか確認している部分。
//bootstrap-datetimepicker.js ; (function (factory) { if (typeof define === 'function' && define.amd) { // AMD is used - Register as an anonymous module. define(['jquery', 'moment'], factory); } else { // AMD is not used - Attempt to fetch dependencies from scope. if (!jQuery) { throw 'bootstrap-datetimepicker requires jQuery to be loaded first'; } else if (!moment) { // ←ここでエラー throw 'bootstrap-datetimepicker requires moment.js to be loaded first'; } else { factory(jQuery, moment); } } }
breakpointをしかけて確認。デバッグはWebStormで。グローバル空間に確かにmomentがいない模様。
moment.jsのコードを確認。requirejsなどをつかっていないので、一番下、else節のmakeGlobalが呼ばれる。
//moment.js // CommonJS module is defined if (hasModule) { module.exports = moment; } else if (typeof define === "function" && define.amd) { define("moment", function (require, exports, module) { if (module.config && module.config() && module.config().noGlobal === true) { // release the global variable globalScope.moment = oldGlobalMoment; } return moment; }); makeGlobal(true); } else { makeGlobal(); //←これが呼ばれる }
makeGlobalを確認。
globalScope変数にmomentを追加してる。
//moment.js function makeGlobal(shouldDeprecate) { /*global ender:false */ if (typeof ender !== 'undefined') { return; } oldGlobalMoment = globalScope.moment; if (shouldDeprecate) { globalScope.moment = deprecate( "Accessing Moment through the global scope is " + "deprecated, and will be removed in an upcoming " + "release.", moment); } else { globalScope.moment = moment;//←コレ } }
globalScopeを確認。 nodeならglobal、ブラウザ空間ならthisなので、windowと読める。
//moment.js var moment, VERSION = "2.6.0", // the global-scope this is NOT the global object in Node.js globalScope = typeof global !== 'undefined' ? global : this, oldGlobalMoment, round = Math.round, i, ........
コード的にはwindowにmomentが定義されるように思える・・。もう一回じっくりデバッグ。
Window下にglobalがいるので、Window.global.momentになって、window.momentではなかった。
グローバルはwindowだと思っていたがWindowだった。
momentの読み込みとdatetimepickerの読み込みの間にworkaroundを入れて対処。
window.moment = global.moment;
https://github.com/rogerwang/node-webkit/wiki/Window
Window is a wrapper of DOM's window object, it has extended operations and can receive various window events.
Wikiをよく読みましょう、思い込みを捨てましょうという話。
*1:他にも読み込んでいるが関係ないので省略