最終更新日 2024-09-25

jsmode - requireのモジュールの作り方

基本的にはnode用のモジュールと似ている

基本的にはnodejsのrequire用に作成する exports や module.exports とほぼ同じ書き方です。

exports編 ①

abc.js (モジュール側)
function abc() {
    return 3;
}

var aaa = {a:"abc", b:"def"};

exports.abc = abc;
exports.aaa = aaa;
 

このファイルを、currentmacrodirectory か、もしくは、macrodir\jsmode_modules 内に配置する
(他の場所でも良いが、その際は、適宜呼び出し引数を調整すること)

abc.mac (呼び出し側)
execjs macrodir + @"\jsmode_modules\require.js";
js {
    var myabc = require("abc");
    message(myabc.abc());
    message(myabc.aaa.a);
}
        

exports編 ②

abc.js (モジュール側)
var mylib = exports;

mylib.abc = function() {
    return 3;
}

mylib.aaa = {a:"abc", b:"def"};
 
abc.mac (呼び出し側)
execjs macrodir + @"\jsmode_modules\require.js";
js {
    var myabc = require("abc");
    message(myabc.abc());
    message(myabc.aaa.a);
}
        

module.exports編

abc.js (モジュール側)
var mylib = {};
 
mylib.abc = function() {
    return 3;
}
 
mylib.aaa = {a:"abc", b:"def"};

module.exports = mylib;
 
abc.mac (呼び出し側)
execjs macrodir + @"\jsmode_modules\require.js";
js {
    var myabc = require("abc");
    message(myabc.abc());
    message(myabc.aaa.a);
}
        

やっては駄目な例

module.exports に オブジェクトを突っ込むのはOKですが、
exportsにそのままオブジェクトを代入してはいけません。
この制限はnodejs用のrequireと同じです。

abc.js (モジュール側)
var mylib = {};
 
mylib.abc = function() {
    return 3;
}
 
mylib.aaa = {a:"abc", b:"def"};

exports = mylib; // これは駄目。オブジェクトをまるまる代入する際は、module.exports であることが必須
 

require用とexecjs用の互換のモジュール

require用だけだと単純ですが、execjs用も考慮すると、実装がやや複雑になります。
理由としては、execjsでの実装は、常に「他の人の実装と衝突する配慮」が必要となってしまうからです。
又、公開以降は決して変更しないguidを設定しておくことで、他者が「あのライブラリの関数だ」と判断できるようになります。

require用 と execjs 用の 両対応の testfuncの実装例

test_module.js (モジュール側)
(function() {
    // 一意のGUIDの作成。GUIDでなくとも他と決して被らないような文字列でOK
    // コピペ厳禁。moduleごとに別々のguidを用意することに意味がある。
    var guid = "{D38DB248-D890-4B19-96BE-0EA0A213D98B}";

    // アウトプット枠への表示用
    var op_dllobj = null;
    function output(msg) {
        if (!op_dllobj) {
            op_dllobj = hidemaru.loadDll(hidemaruGlobal.hidemarudir() + "\\HmOutputPane.dll");
        }
        if (op_dllobj) {
            var msg_replaced = msg.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n");
            return op_dllobj.dllFunc.Output(hidemaruGlobal.hidemaruhandle(0), msg_replaced);
        }
        return false;
    }

    // これが実際の関数
    function __testfunc__(some) {
        return 3;
    }

    // require用だと単純で済む
    if (typeof(module) != 'undefined' && module.exports) {
        module.exports.testfunc = __testfunc__;

    // execjs用だと、グローバルへの何らかのシンボルの公開を指すので、
    // 他のプログラムとの衝突を考慮して、下記のような記述が必要となってくる。
    } else {
        if (typeof (testfunc) != 'undefined') {
            if (testfunc.guid == null || testfunc.guid != guid) {
                output("本モジュールとは異なるtestfuncが、すでに定義されています。\r\n上書きします。\r\n");
            }
        }

        // 一致していたら上書きはしない
        if (testfunc.guid == guid) {
            return;
        }

        testfunc = __testfunc__; // グローバルにtestfuncを公開
        testfunc.guid = guid;    // このモジュールであるという印を埋め込んでおく
    }
})();
 
abc1.mac (呼び出し側)
execjs currentmacrodirectory + @"\test_module.js";
js {
    var a = testfunc(); // 別のtestfuncが定義されていても、モジュールが勝手に上書きしてしまう。衝突の心配が常に必要。
    message(a);
}
        
abc2.mac (呼び出し側)
execjs macrodir + @"\jsmode_modules\require.js";
js {
    var mymod = require("test_module"); // 自分でmymodと決めているわけだから、かぶることはない。
    message(mymod.testfunc());
}