A JSB Module Loader

I have wrote a module loader for cocos2d-x-JSB. I think you guys may want that, and feel free to give me some feedback.

  • What is a module loader
    A module loader helps you manage your js code. At this time, you can only connect js sources using function “require”.
    However “require” is not a good choice when you are developing a more complex project.
    There are some weak points using “require”.

    1. every time you call “require”, the required file will be executed once. This is not efficiency, most time you want to execute the file only once and use at any places as headers and libs.
    2. There is no such file scope in javascript except you are running your code in a function. So everything you defined in a file is in global scope. Then you may not remember all the global variables you defined in other files. You may need a container object or just use this module loader.
  • How to Use
    The use of this module loader is very similar to Node.js.

    1. define your module in a single file.
    2. every variable you defined in module file is not a global variable. and it’s transparency to the client users.
    3. add interfaces to exports.
    4. when you need the module just call “var lib = loadModule(‘somemodule.js’)”, then lib contains all exported interfaces.

Use Samples:

/* splash.js****/
function scene
{
director = cc.Director.getInstance;
winSize = director.getWinSize;
center = cc.p;
layer = cc.Layer.create;
s = cc.Scene.create;
s.addChild;
label = cc.LabelTTF.create;
label.setPosition;
layer.addChild;
return s;
}
exports.scene = scene;
/* end of splash.js/
/
* main.js /
require;
var splash = loadModule;
director = cc.Director.getInstance;
director.runWithScene);
/
end of main.js */

  • the implementation code

using namespace cocos2d;
using namespace std;
>
static string RemoveFileExt(const string& filePath) {
size_t pos = filePath.rfind(‘.’);
if (0 < pos) {
return filePath.substr(0, pos);
}
else {
return filePath;
}
}
>
static JSObject sModules = NULL;
>
void registerLoadModule
{
//create modules
sModules = JS_NewObject;
jsval vModules = OBJECT_TO_JSVAL;
JS_SetProperty;
>
JS_DefineFunction;
}
>
JSBool loadModule
{
if
{
//read argument
jsval
argv = JS_ARGV(cx, vp);
JSString* str = JS_ValueToString(cx, argv[0]);
JSStringWrapper path(str);
string moduleName = RemoveFileExt(path);
>
JSObject* modules = sModules;
>
jsval vModule;
JS_GetProperty(cx, modules, moduleName.c_str(), &vModule);
>
if( !JSVAL_IS_PRIMITIVE(vModule) )
{
JSObject module = JSVAL_TO_OBJECT;
jsval vExports;
JS_GetProperty;
JS_SET_RVAL;
}
else
{
JSObject
module = JS_NewObject(cx, NULL, NULL, NULL);
>
JSObject *exports = JS_NewObject(cx, NULL, NULL, NULL);
jsval vExports = OBJECT_TO_JSVAL(exports);
JS_SetProperty(cx, module, “exports”, &vExports);
>
ScriptingCore::getInstance()->runScript(path, module, cx);
vModule = OBJECT_TO_JSVAL(module);
JS_SetProperty(cx, modules, moduleName.c_str(), &vModule);
>
JS_SET_RVAL(cx, vp, vExports);
}
>
return JS_TRUE;
}
else
{
CCLog(“loadModule: wrong argument.”);
return JS_FALSE;
}
}

Looks good. Could you send a PR to our repo?

James Chen wrote:

Looks good. Could you send a PR to our repo?

Ok, I’ll do it this weekend.

Hi hammer,

Is it compatible with Cocos2d-html5? We must keep Cocos2d-JSB and Cocos2d-html5 as the same, so that one game could run on both of them.

Please take a look on https://github.com/cocos2d/cocos2d-html5/pull/959, it is another solution of module loader.
Pros: It supports module loading in engine component.
Cons: Developer have to use module loading no matter they like it or not; It is hard to compile them into a single file with google closure compiler in advance mode.

In my opinion, it is better to pack whole Cocos2d-html5 engine as a module, and provides developers two choice, module loader and normal file loading.

Do you have any suggestions on Cocos2d-html5 module supporting?

Shun Lin wrote:

Hi hammer,
>
Is it compatible with Cocos2d-html5? We must keep Cocos2d-JSB and Cocos2d-html5 as the same, so that one game could run on both of them.
>
>
Please take a look on https://github.com/cocos2d/cocos2d-html5/pull/959, it is another solution of module loader.
Pros: It supports module loading in engine component.
Cons: Developer have to use module loading no matter they like it or not; It is hard to compile them into a single file with google closure compiler in advance mode.
>
In my opinion, it is better to pack whole Cocos2d-html5 engine as a module, and provides developers two choice, module loader and normal file loading.
>
Do you have any suggestions on Cocos2d-html5 module supporting?

Hi Lin,
I have looked that pull request. I believe it’s from requirejs.org. Actually I have glanced that piece of code before I decide to make one myself.
To be honesty, I never used cocos2d-x-html5 before. So I have no idea what the modules should be look like. But I think they should share some common targets:

  1. create a local scope, prevent the global scope from been polluted.
  2. cache loaded module, reuse it when loaded.
  3. easy to understand, easy to use.
    I wish my answer helps.

@hammer
Thanks for your PR.
Do you think the demo codes of module should be:

/*** splash.js ***/
function(){
function scene() {
director = cc.Director.getInstance();
winSize = director.getWinSize();
center = cc.p(winSize.width/2, winSize.height/2);
layer = cc.Layer.create();
s = cc.Scene.create();
s.addChild(layer);
label = cc.LabelTTF.create("Hello, world", "Arial", 48);
label.setPosition(center);
layer.addChild(label);
return s;
}

exports.scene = scene;
}(); // Using closure will not pollute the global scope.
/*** end of splash.js ***/

What do you think?

James Chen wrote:

@hammer
Thanks for your PR.
Do you think the demo codes of module should be:
[…]
>
What do you think?

I don’t think closure is needed if you only use loadModule to load that file.
I have replaced the container object from CAPI for loadModule. Any object created directly with be added at “global.modules.yourmodule”.
But if you call require(“yourmodule.js”), that will pollute the global scope.
So my advice is that if a user decide to use loadModule, they should avoid use require at the same file.
And require still has its own place. For example you should put require(“jsb.js”) at the first line of main.js. So you wont need to require it anywhere again.

@hammer
Yep, you were right. I didn’t notice the second parameter in
ScriptingCore::getInstance()->runScript(path, module, cx);