Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremys committed Jun 14, 2011
2 parents 4707fe1 + 9967c36 commit 25b015d
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 4 deletions.
32 changes: 32 additions & 0 deletions doc/api/modules.markdown
Expand Up @@ -330,6 +330,38 @@ Because `module` provides a `filename` property (normally equivalent to
`__filename`), the entry point of the current application can be obtained
by checking `require.main.filename`.

## AMD Compatibility

Node's modules have access to a function named `define`, which may be
used to specify the module's return value. This is not necessary in node
programs, but is present in the node API in order to provide
compatibility with module loaders that use the Asynchronous Module
Definition pattern.

The example module above could be structured like so:

define(function (require, exports, module) {
var PI = Math.PI;

exports.area = function (r) {
return PI * r * r;
};

exports.circumference = function (r) {
return 2 * PI * r;
};
});

* Only the last argument to `define()` matters. Other module loaders
sometimes use a `define(id, [deps], cb)` pattern, but since this is
not relevant in node programs, the other arguments are ignored.
* If the `define` callback returns a value other than `undefined`, then
that value is assigned to `module.exports`.
* **Important**: Despite being called "AMD", the node module loader **is
in fact synchronous**, and using `define()` does not change this fact.
Node executes the callback immediately, so please plan your programs
accordingly.

## Addenda: Package Manager Tips

The semantics of Node's `require()` function were designed to be general
Expand Down
39 changes: 38 additions & 1 deletion lib/module.js
Expand Up @@ -381,6 +381,7 @@ Module.prototype._compile = function(content, filename) {
require.cache = Module._cache;

var dirname = path.dirname(filename);
var define = makeDefine(require, self);

if (Module._contextLoad) {
if (self.id !== '.') {
Expand All @@ -397,6 +398,7 @@ Module.prototype._compile = function(content, filename) {
sandbox.module = self;
sandbox.global = sandbox;
sandbox.root = root;
sandbox.define = define;

return runInNewContext(content, sandbox, filename, true);
}
Expand All @@ -408,6 +410,7 @@ Module.prototype._compile = function(content, filename) {
global.__filename = filename;
global.__dirname = dirname;
global.module = self;
global.define = define;

return runInThisContext(content, filename, true);
}
Expand All @@ -419,10 +422,44 @@ Module.prototype._compile = function(content, filename) {
if (filename === process.argv[1] && global.v8debug) {
global.v8debug.Debug.setBreakPoint(compiledWrapper, 0, 0);
}
var args = [self.exports, require, self, filename, dirname];

var args = [self.exports, require, self, filename, dirname, define];
return compiledWrapper.apply(self.exports, args);
};

// AMD compatibility
function makeDefine(require, module) {
var called = false;
function define() {
if (called) {
throw new Error("define() may only be called once.");
}
called = true;

// only care about the last argument
var cb = arguments[ arguments.length - 1 ];

// set exports immediately:
// define({ foo: "bar" })
if (typeof cb !== 'function') {
module.exports = cb;
return;
}

var ret = cb(require, module.exports, module);

if (ret !== undefined) {
// set exports with return statement
// define(function () { return { foo: "bar" } })
module.exports = ret;
}
}

return define;
}



// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
Expand Down
4 changes: 2 additions & 2 deletions src/node.cc
Expand Up @@ -1287,15 +1287,15 @@ void DisplayExceptionLine (TryCatch &try_catch) {
//
// When reporting errors on the first line of a script, this wrapper
// function is leaked to the user. This HACK is to remove it. The length
// of the wrapper is 62. That wrapper is defined in src/node.js
// of the wrapper is 70. That wrapper is defined in src/node.js
//
// If that wrapper is ever changed, then this number also has to be
// updated. Or - someone could clean this up so that the two peices
// don't need to be changed.
//
// Even better would be to get support into V8 for wrappers that
// shouldn't be reported to users.
int offset = linenum == 1 ? 62 : 0;
int offset = linenum == 1 ? 70 : 0;

fprintf(stderr, "%s\n", sourceline_string + offset);
// Print wavy underline (GetUnderline is deprecated).
Expand Down
2 changes: 1 addition & 1 deletion src/node.js
Expand Up @@ -436,7 +436,7 @@
};

NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'(function (exports, require, module, __filename, __dirname, define) { ',
'\n});'
];

Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/amd-modules/extra-args.js
@@ -0,0 +1,3 @@
define(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, function(r, e, m) {
exports.ok = require("./regular.js").ok;
});
3 changes: 3 additions & 0 deletions test/fixtures/amd-modules/module-exports.js
@@ -0,0 +1,3 @@
define(function(require, exports, module) {
module.exports = { ok: require("./regular.js").ok };
});
1 change: 1 addition & 0 deletions test/fixtures/amd-modules/object.js
@@ -0,0 +1 @@
define({ ok: require("./regular.js").ok });
14 changes: 14 additions & 0 deletions test/fixtures/amd-modules/regular.js
@@ -0,0 +1,14 @@
var R = require;
var E = exports;
var M = module;

define(function(require, exports, module, nothingHere) {
if (R !== require) throw new Error("invalid require in AMD cb");
if (E !== exports) throw new Error("invalid exports in AMD cb");
if (M !== module) throw new Error("invalid module in AMD cb");
if (nothingHere !== undefined) {
throw new Error("unknown args to AMD cb");
}

exports.ok = { ok: true };
});
3 changes: 3 additions & 0 deletions test/fixtures/amd-modules/return.js
@@ -0,0 +1,3 @@
define(function() {
return { ok: require("./regular.js").ok };
});
20 changes: 20 additions & 0 deletions test/simple/test-module-loading.js
Expand Up @@ -187,6 +187,26 @@ try {
assert.equal(require(loadOrder + 'file8').file8, 'file8/index.reg', msg);
assert.equal(require(loadOrder + 'file9').file9, 'file9/index.reg2', msg);


// test the async module definition pattern modules
var amdFolder = '../fixtures/amd-modules';
var amdreg = require(amdFolder + '/regular.js');
assert.deepEqual(amdreg.ok, {ok: true}, 'regular amd module failed');

// make sure they all get the same 'ok' object.
var amdModuleExports = require(amdFolder + '/module-exports.js');
assert.equal(amdModuleExports.ok, amdreg.ok, 'amd module.exports failed');

var amdReturn = require(amdFolder + '/return.js');
assert.equal(amdReturn.ok, amdreg.ok, 'amd return failed');

var amdObj = require(amdFolder + '/object.js');
assert.equal(amdObj.ok, amdreg.ok, 'amd object literal failed');

var amdExtraArgs = require(amdFolder + '/extra-args.js');
assert.equal(amdExtraArgs.ok, amdreg.ok, 'amd extra args failed');


process.addListener('exit', function() {
assert.ok(common.indirectInstanceOf(a.A, Function));
assert.equal('A done', a.A());
Expand Down

0 comments on commit 25b015d

Please sign in to comment.