Comparing version 0.6.5 to 0.6.6
@@ -214,2 +214,9 @@ var assert = require("assert"); | ||
var providesExp = /@providesModule[ ]+(\S+)/; | ||
BCp.getProvidedId = function(source) { | ||
var match = providesExp.exec(source); | ||
return match && match[1]; | ||
}; | ||
exports.BuildContext = BuildContext; |
@@ -12,3 +12,3 @@ var assert = require("assert"); | ||
function ModuleReader(context, resolvers, processors, wrapper) { | ||
function ModuleReader(context, resolvers, processors) { | ||
var self = this; | ||
@@ -39,3 +39,3 @@ assert.ok(self instanceof ModuleReader); | ||
if (!context.ignoreDependencies) | ||
procArgs.push(require("./relative").relativizeP); | ||
procArgs.push(require("./relative").getProcessor(self)); | ||
processors = hashCallbacks("processors", procArgs); | ||
@@ -45,2 +45,3 @@ | ||
context: { value: context }, | ||
idToHash: { value: {} }, | ||
resolvers: { value: resolvers }, | ||
@@ -54,3 +55,3 @@ processors: { value: processors }, | ||
ModuleReader.prototype = { | ||
getSourceP: function(id) { | ||
getSourceP: util.cachedMethod(function(id) { | ||
var context = this.context; | ||
@@ -77,16 +78,42 @@ var copy = this.resolvers.slice(0).reverse(); | ||
return tryNextResolverP(); | ||
}, | ||
}), | ||
getCanonicalIdP: util.cachedMethod(function(id) { | ||
var reader = this; | ||
return reader.getSourceP(id).then(function(source) { | ||
return reader.context.getProvidedId(source) || id; | ||
}); | ||
}), | ||
readModuleP: util.cachedMethod(function(id) { | ||
var reader = this; | ||
var hash = createHash("sha1") | ||
.update("module\0") | ||
.update(id + "\0") | ||
.update(reader.salt + "\0"); | ||
return reader.getSourceP(id).then(function(source) { | ||
// If the source contains a @providesModule declaration, treat | ||
// that declaration as canonical. Note that the Module object | ||
// returned by readModuleP might have an .id property whose | ||
// value differs from the original id parameter. | ||
id = reader.context.getProvidedId(source) || id; | ||
return reader.getSourceP(id).then(function(source) { | ||
assert.strictEqual(typeof source, "string"); | ||
hash.update(source.length + "\0" + source); | ||
return reader.buildModuleP(id, hash.digest("hex"), source); | ||
var hash = createHash("sha1") | ||
.update("module\0") | ||
.update(id + "\0") | ||
.update(reader.salt + "\0") | ||
.update(source.length + "\0" + source) | ||
.digest("hex"); | ||
if (reader.idToHash.hasOwnProperty(id)) { | ||
// Ensure that the same module identifier is not | ||
// provided by distinct modules. | ||
assert.strictEqual( | ||
reader.idToHash[id], hash, | ||
"more than one module named " + | ||
JSON.stringify(id)); | ||
} else { | ||
reader.idToHash[id] = hash; | ||
} | ||
return reader.buildModuleP(id, hash, source); | ||
}); | ||
@@ -121,4 +148,21 @@ }), | ||
var reader = this; | ||
return Q.all(ids).then(function(ids) { | ||
return Q.all(ids.map(reader.readModuleP, reader)); | ||
if (ids.length === 0) | ||
return ids; // Shortcut. | ||
var modulePs = ids.map(reader.readModuleP, reader); | ||
return Q.all(modulePs).then(function(modules) { | ||
var seen = {}; | ||
var result = []; | ||
modules.forEach(function(module) { | ||
if (!seen.hasOwnProperty(module.id)) { | ||
seen[module.id] = true; | ||
result.push(module); | ||
} | ||
}); | ||
return result; | ||
}); | ||
}); | ||
@@ -173,4 +217,4 @@ } | ||
resolveId: function(id) { | ||
return path.normalize(path.join(this.id, "..", id)); | ||
return util.absolutize(this.id, id); | ||
} | ||
}; |
var assert = require("assert"); | ||
var Q = require("q"); | ||
var path = require("path"); | ||
var makePromise = require("./util").makePromise; | ||
var util = require("./util"); | ||
var recast = require("recast"); | ||
var n = recast.namedTypes; | ||
exports.relativizeP = function(id, source) { | ||
return makePromise(function(callback) { | ||
recast.runString(source, function(ast, callback) { | ||
callback(new RequireVisitor(id).visit(ast)); | ||
function Relativizer(reader) { | ||
assert.ok(this instanceof Relativizer); | ||
assert.ok(reader === null || | ||
reader instanceof require("./reader").ModuleReader); | ||
Object.defineProperties(this, { | ||
reader: { value: reader } | ||
}); | ||
} | ||
var Rp = Relativizer.prototype; | ||
exports.getProcessor = function(reader) { | ||
var relativizer = new Relativizer(reader); | ||
return function(id, source) { | ||
return relativizer.processSourceP(id, source); | ||
}; | ||
}; | ||
Rp.processSourceP = function(id, source) { | ||
var visitor = new RequireVisitor(this, id); | ||
return util.makePromise(function(finish) { | ||
recast.runString(source, function(ast, reprint) { | ||
ast = visitor.visit(ast); | ||
Q.all(visitor.promises).then(function() { | ||
reprint(ast); | ||
}); | ||
}, { | ||
writeback: function(code) { | ||
callback(null, code); | ||
finish(null, code); | ||
} | ||
@@ -19,7 +44,39 @@ }); | ||
Rp.absolutizeP = function(moduleId, requiredId) { | ||
requiredId = util.absolutize(moduleId, requiredId); | ||
if (this.reader) | ||
return this.reader.getCanonicalIdP(requiredId); | ||
return Q.resolve(requiredId); | ||
}; | ||
Rp.relativizeP = function(moduleId, requiredId) { | ||
return this.absolutizeP( | ||
moduleId, | ||
requiredId | ||
).then(function(absoluteId) { | ||
return util.relativize(moduleId, absoluteId); | ||
}); | ||
}; | ||
var RequireVisitor = recast.Visitor.extend({ | ||
init: function(moduleId) { | ||
init: function(relativizer, moduleId) { | ||
assert.ok(relativizer instanceof Relativizer); | ||
this.relativizer = relativizer; | ||
this.moduleId = moduleId; | ||
this.promises = []; | ||
}, | ||
fixRequireP: function(literal) { | ||
var promise = this.relativizer.relativizeP( | ||
this.moduleId, | ||
literal.value | ||
).then(function(newValue) { | ||
return literal.value = newValue; | ||
}); | ||
this.promises.push(promise); | ||
}, | ||
visitCallExpression: function(exp) { | ||
@@ -35,3 +92,3 @@ var callee = exp.callee; | ||
{ | ||
arg.value = relativize(this.moduleId, arg.value); | ||
this.fixRequireP(arg); | ||
return; | ||
@@ -44,19 +101,1 @@ } | ||
}); | ||
function relativize(moduleId, requiredId) { | ||
if (requiredId.charAt(0) === ".") { | ||
// Keep the required ID relative. | ||
} else { | ||
// Relativize the required ID. | ||
requiredId = path.relative( | ||
path.join(moduleId, ".."), | ||
requiredId | ||
); | ||
} | ||
requiredId = path.normalize(requiredId); | ||
if (requiredId.charAt(0) !== ".") | ||
requiredId = "./" + requiredId; | ||
return requiredId; | ||
} |
@@ -298,1 +298,28 @@ var assert = require("assert"); | ||
}; | ||
function absolutize(moduleId, requiredId) { | ||
if (requiredId.charAt(0) === ".") | ||
requiredId = path.join(moduleId, "..", requiredId); | ||
return path.normalize(requiredId); | ||
} | ||
exports.absolutize = absolutize; | ||
function relativize(moduleId, requiredId) { | ||
requiredId = absolutize(moduleId, requiredId); | ||
if (requiredId.charAt(0) === ".") { | ||
// Keep the required ID relative. | ||
} else { | ||
// Relativize the required ID. | ||
requiredId = path.relative( | ||
path.join(moduleId, ".."), | ||
requiredId | ||
); | ||
} | ||
if (requiredId.charAt(0) !== ".") | ||
requiredId = "./" + requiredId; | ||
return requiredId; | ||
} | ||
exports.relativize = relativize; |
@@ -17,3 +17,3 @@ { | ||
], | ||
"version": "0.6.5", | ||
"version": "0.6.6", | ||
"homepage": "http://github.com/benjamn/commoner", | ||
@@ -20,0 +20,0 @@ "repository": { |
152
test/run.js
@@ -34,4 +34,12 @@ var Watcher = require("../lib/watcher").Watcher; | ||
function getProvidedP(id) { | ||
var context = this; | ||
return context.getProvidedP().then(function(idToPath) { | ||
if (idToPath.hasOwnProperty(id)) | ||
return context.readFileP(idToPath[id]); | ||
}); | ||
} | ||
function getSourceP(id) { | ||
return this.readFileP(id + ".js"); | ||
return this.readModuleP(id); | ||
} | ||
@@ -130,2 +138,74 @@ | ||
exports.testProvidesModule = function(t, assert) { | ||
var code = arguments.callee.toString(); | ||
/** | ||
* Look, Ma! A test function that uses itself as input! | ||
* @providesModule | ||
* @providesModule foo/bar | ||
*/ | ||
assert.strictEqual( | ||
code.split("@provides" + "Module").length, | ||
4); | ||
assert.strictEqual( | ||
debugContext.getProvidedId(code), | ||
"foo/bar"); | ||
assert.strictEqual( | ||
debugContext.getProvidedId( | ||
"no at-providesModule, here"), | ||
null); | ||
/** | ||
* Just to make sure we only pay attention to the first one. | ||
* @providesModule ignored | ||
*/ | ||
function helper(context) { | ||
var reader = new ModuleReader(context, [ | ||
getProvidedP, | ||
getSourceP | ||
], []); | ||
return Q.all([ | ||
Q.all([ | ||
reader.readModuleP("widget/share"), | ||
reader.readModuleP("WidgetShare") | ||
]).spread(function(ws1, ws2) { | ||
assert.strictEqual(ws1.id, ws2.id); | ||
assert.strictEqual(ws1.id, "WidgetShare"); | ||
assert.strictEqual(ws1, ws2); | ||
}), | ||
reader.readMultiP([ | ||
"widget/share", | ||
"WidgetShare" | ||
]).then(function(modules) { | ||
assert.strictEqual(modules.length, 1); | ||
assert.strictEqual(modules[0].id, "WidgetShare"); | ||
}), | ||
reader.readModuleP( | ||
"widget/gallery" | ||
).then(function(gallery) { | ||
return gallery.getRequiredP(); | ||
}).then(function(deps) { | ||
assert.strictEqual(deps.length, 1); | ||
assert.strictEqual(deps[0].id, "WidgetShare"); | ||
}), | ||
Q.all([ | ||
reader.getSourceP("widget/share"), | ||
reader.getSourceP("WidgetShare") | ||
]).spread(function(source1, source2) { | ||
assert.strictEqual(source1, source2); | ||
}) | ||
]); | ||
} | ||
waitForHelpers(t, helper); | ||
}; | ||
exports.testMakePromise = function(t, assert) { | ||
@@ -158,3 +238,4 @@ var error = new Error("test"); | ||
exports.testRelativize = function(t, assert) { | ||
var relativizeP = require("../lib/relative").relativizeP; | ||
var moduleId = "some/deeply/nested/module"; | ||
var processor = require("../lib/relative").getProcessor(null); | ||
@@ -168,4 +249,8 @@ function makeSource(id) { | ||
function helperP(requiredId, expected) { | ||
return relativizeP( | ||
"some/deeply/nested/module", | ||
assert.strictEqual( | ||
util.relativize(moduleId, requiredId), | ||
expected); | ||
return processor( | ||
moduleId, | ||
makeSource(requiredId) | ||
@@ -200,2 +285,61 @@ ).then(function(source) { | ||
exports.testGetCanonicalId = function(t, assert) { | ||
function helperP(context) { | ||
var reader = new ModuleReader(context, [ | ||
getProvidedP, | ||
getSourceP | ||
], []); | ||
return Q.all([ | ||
reader.getCanonicalIdP("widget/share"), | ||
reader.getCanonicalIdP("WidgetShare"), | ||
reader.readModuleP("widget/share").get("id"), | ||
reader.readModuleP("WidgetShare").get("id") | ||
]).spread(function(ws1, ws2, ws3, ws4) { | ||
assert.strictEqual(ws1, "WidgetShare"); | ||
assert.strictEqual(ws2, "WidgetShare"); | ||
assert.strictEqual(ws3, "WidgetShare"); | ||
assert.strictEqual(ws4, "WidgetShare"); | ||
}); | ||
} | ||
waitForHelpers(t, helperP); | ||
}; | ||
exports.testCanonicalRequires = function(t, assert) { | ||
function helperP(context) { | ||
assert.strictEqual(context.ignoreDependencies, false); | ||
var reader = new ModuleReader(context, [ | ||
getProvidedP, | ||
getSourceP | ||
], []); | ||
return reader.readModuleP("widget/follow").then(function(follow) { | ||
assert.strictEqual(follow.source.indexOf("widget/share"), -1); | ||
assert.strictEqual(strCount( | ||
'require("../WidgetShare")', | ||
follow.source | ||
), 4); | ||
assert.strictEqual(strCount( | ||
'require("./gallery")', | ||
follow.source | ||
), 2); | ||
assert.strictEqual(strCount( | ||
'require("../assert")', | ||
follow.source | ||
), 2); | ||
}); | ||
} | ||
waitForHelpers(t, helperP); | ||
}; | ||
function strCount(substring, string) { | ||
return string.split(substring).length - 1; | ||
} | ||
exports.testFlatten = function(t, assert) { | ||
@@ -202,0 +346,0 @@ function check(input, expected) { |
@@ -0,1 +1,20 @@ | ||
// All these ways of requiring WidgetShare should get normalized to the | ||
// same relative identifier: "../WidgetShare". | ||
require("./share"); | ||
require("../widget/share"); | ||
require("WidgetShare"); | ||
require("../WidgetShare"); | ||
// These identifiers will both become "./gallery". | ||
require("../widget/gallery"); | ||
require("./gallery"); | ||
// These both become "../assert". | ||
require("assert"); | ||
require("../assert"); | ||
// These circular references should both become "./follow". | ||
require("./follow"); | ||
require("../widget/follow"); | ||
exports.name = "widget/follow"; |
@@ -0,1 +1,5 @@ | ||
require("../widget/share"); | ||
require("./share"); | ||
require("WidgetShare"); | ||
exports.name = "widget/gallery"; |
67249
1608
30