duktape icon indicating copy to clipboard operation
duktape copied to clipboard

Problem with extras/module-node

Open moar82 opened this issue 5 years ago • 3 comments

Hello, I'm using version duktape 2.3.0 and I have issues to load Node.js modules.

In the following web page: https://duktape.org/guide.html there is a link for a Node.js modules compatible loader. However, the following link is broken "See How to use Node.js-like modules for examples."

I'm running the example in duktape-2.3.0/extras/module-node However, I got undefined in all cases. I'm not sure I understand the test, but I see in test.c file the following instruction:


if (strcmp(module_id, "pig.js") == 0) {
		duk_push_sprintf(ctx, "module.exports = 'you\\'re about to get eaten by %s';",

Which is clearly not working.

make
rm -rf ./prep
python2 ../../tools/configure.py --quiet --output-directory ./prep
genconfig.py          WARNING Recommended config option DUK_USE_FATAL_HANDLER not provided
gcc -std=c99 -Wall -Wextra -o test -I./prep -I. ./prep/duktape.c duk_module_node.c test.c -lm

./test 'assert(typeof require("pig") === "string", "basic require()");'
top after init: 0
Evaling: assert(typeof require("pig") === "string", "basic require()");
resolve_cb: id:'pig', parent-id:'', resolve-to:'pig.js'
load_cb: id:'pig.js', filename:'pig.js'
--> undefined
Done
./test 'assert(require("cow").indexOf("pig") !== -1, "nested require()");'
top after init: 0
Evaling: assert(require("cow").indexOf("pig") !== -1, "nested require()");
resolve_cb: id:'cow', parent-id:'', resolve-to:'cow.js'
load_cb: id:'cow.js', filename:'cow.js'
resolve_cb: id:'pig', parent-id:'cow.js', resolve-to:'pig.js'
load_cb: id:'pig.js', filename:'pig.js'
--> undefined
Done
./test 'var ape1 = require("ape"); var ape2 = require("ape"); assert(ape1 === ape2, "caching");'
top after init: 0
Evaling: var ape1 = require("ape"); var ape2 = require("ape"); assert(ape1 === ape2, "caching");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
--> undefined
Done
./test 'var ape1 = require("ape"); var inCache = "ape.js" in require.cache; delete require.cache["ape.js"]; var ape2 = require("ape"); assert(inCache && ape2 !== ape1, "require.cache");'
top after init: 0
Evaling: var ape1 = require("ape"); var inCache = "ape.js" in require.cache; delete require.cache["ape.js"]; var ape2 = require("ape"); assert(inCache && ape2 !== ape1, "require.cache");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var ape = require("ape"); assert(typeof ape.module.require === "function", "module.require()");'
top after init: 0
Evaling: var ape = require("ape"); assert(typeof ape.module.require === "function", "module.require()");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var ape = require("ape"); assert(ape.module.exports === ape, "module.exports");'
top after init: 0
Evaling: var ape = require("ape"); assert(ape.module.exports === ape, "module.exports");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var ape = require("ape"); assert(ape.module.id === "ape.js" && ape.module.id === ape.module.filename, "module.id");'
top after init: 0
Evaling: var ape = require("ape"); assert(ape.module.id === "ape.js" && ape.module.id === ape.module.filename, "module.id");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var ape = require("ape"); assert(ape.module.filename === "ape.js", "module.filename");'
top after init: 0
Evaling: var ape = require("ape"); assert(ape.module.filename === "ape.js", "module.filename");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var ape = require("ape"); assert(ape.module.loaded === true && ape.wasLoaded === false, "module.loaded");'
top after init: 0
Evaling: var ape = require("ape"); assert(ape.module.loaded === true && ape.wasLoaded === false, "module.loaded");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var ape = require("ape"); assert(ape.__filename === "ape.js", "__filename");'
top after init: 0
Evaling: var ape = require("ape"); assert(ape.__filename === "ape.js", "__filename");
resolve_cb: id:'ape', parent-id:'', resolve-to:'ape.js'
load_cb: id:'ape.js', filename:'ape.js'
--> undefined
Done
./test 'var badger = require("badger"); assert(badger.foo === 123 && badger.bar === 234, "exports.foo assignment");'
top after init: 0
Evaling: var badger = require("badger"); assert(badger.foo === 123 && badger.bar === 234, "exports.foo assignment");
resolve_cb: id:'badger', parent-id:'', resolve-to:'badger.js'
load_cb: id:'badger.js', filename:'badger.js'
--> undefined
Done
./test 'var comment = require("comment"); assert(comment.foo === 123 && comment.bar === 234, "last line with // comment");'
top after init: 0
Evaling: var comment = require("comment"); assert(comment.foo === 123 && comment.bar === 234, "last line with // comment");
resolve_cb: id:'comment', parent-id:'', resolve-to:'comment.js'
load_cb: id:'comment.js', filename:'comment.js'
--> undefined
Done
./test 'var shebang = require("shebang"); assert(shebang.foo === 123 && shebang.bar === 234, "shebang");'
top after init: 0
Evaling: var shebang = require("shebang"); assert(shebang.foo === 123 && shebang.bar === 234, "shebang");
resolve_cb: id:'shebang', parent-id:'', resolve-to:'shebang.js'
load_cb: id:'shebang.js', filename:'shebang.js'
--> undefined
Done

Any help would be appreciated.

Regards

moar82 avatar Jul 17 '19 19:07 moar82

If you're referring to the --> undefined printouts, they are simply the result of eval()ing the input:

  • https://github.com/svaarala/duktape/blob/master/extras/module-node/test.c#L86-L97

In particular, if the script being eval'd ends with assert(...), assert() will return undefined, and thus the script result will be undefined on success. The makefile tests use this assert() technique, throwing an error if some test fails, and that error would then show up in the output: --> TypeError: xxx.

svaarala avatar Jul 19 '19 01:07 svaarala

Sorry, I was expecting a more straightforward test. I apologize my lack of knowledge, but I'm struggling to fix the blanks on the documentation (https://github.com/svaarala/duktape/tree/master/extras/module-node).

Is it too much to ask for a running example? something like runing this script that requires a node.js library?

var http = require('http'); 
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('Hello World!');
}).listen(8080); 

This is my C code:

#include <stdio.h>
#include <string.h>
#include "duktape.h"
#include "duk_module_node.h"

static duk_ret_t cb_resolve_module(duk_context *ctx) {
	/*
     *  Entry stack: [ requested_id parent_id ]
     */

    const char *requested_id = duk_get_string(ctx, 0);
    const char *parent_id = duk_get_string(ctx, 1);  /* calling module */
    const char *resolved_id;

    /* Arrive at the canonical module ID somehow. */

    duk_push_string(ctx, resolved_id);
    return 1;  /*nrets*/

}

static duk_ret_t cb_load_module(duk_context *ctx) {
	const char *filename;
	const char *module_id;

	module_id = duk_require_string(ctx, 0);
	duk_get_prop_string(ctx, 2, "filename");
	filename = duk_require_string(ctx, -1);

	 duk_push_string(ctx, module_id);

	return 1;
}

static duk_ret_t handle_print(duk_context *ctx) {
	printf("%s\n", duk_safe_to_string(ctx, 0));
	return 0;
}

static duk_ret_t handle_assert(duk_context *ctx) {
	if (duk_to_boolean(ctx, 0)) {
		return 0;
	}
	(void) duk_generic_error(ctx, "assertion failed: %s", duk_safe_to_string(ctx, 1));
	return 0;
}


/* We assume a  JS maximum file size of 17kB. */
static void push_file_as_string(duk_context *ctx, const char *filename) {
    FILE *f;
    size_t len;
    char buf[17384];

    f = fopen(filename, "rb");
    if (f) {
        len = fread((void *) buf, 1, sizeof(buf), f);
        fclose(f);
        duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len);
    } else {
        duk_push_undefined(ctx);
    }
}


int main(int argc, char *argv[]) {
	duk_context *ctx;
	//int i;
	//int exitcode = 0;

	ctx = duk_create_heap_default();
	if (!ctx) {
		return 1;
	}

	duk_push_c_function(ctx, handle_print, 1);
	duk_put_global_string(ctx, "print");
	duk_push_c_function(ctx, handle_assert, 2);
	duk_put_global_string(ctx, "assert");

	duk_push_object(ctx);
	duk_push_c_function(ctx, cb_resolve_module, DUK_VARARGS);
	duk_put_prop_string(ctx, -2, "resolve");
	duk_push_c_function(ctx, cb_load_module, DUK_VARARGS);
	duk_put_prop_string(ctx, -2, "load");
	duk_module_node_init(ctx);
	
	/* We push to Duktape heap the JS file*/
	push_file_as_string(ctx, argv[1]);
	if (duk_peval(ctx) != 0) {
		printf("Error: %s\n", duk_safe_to_string(ctx, -1));
	        goto finished;
	    }
 	if (duk_pcall(ctx, 0 /*nargs*/) != 0) {
	        	printf("Error: %s\n", duk_safe_to_string(ctx, -1));
	            } 
	duk_pop(ctx);  /* pop result/error */

	finished:
	duk_destroy_heap(ctx);    

	printf("Done\n");
	
}

Thanks for the support

moar82 avatar Jul 19 '19 02:07 moar82

@moar82 If you’re expecting that require("http") to work automatically with the node module loader, it won’t unless you provide your own implementation of the http module...

This is just a module loader framework providing support for Node.js-style CommonJS modules (i.e. require() itself). You don’t get the Node.js APIs “for free” if that’s what you were expecting.

fatcerberus avatar Aug 17 '19 22:08 fatcerberus