From 41ad67ad1b5b0507d249532e586d42c28e985c5d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 6 Aug 2013 17:15:18 -0700 Subject: [PATCH 01/64] Build boot.js script using a build script :warning: This includes backward incompatible changes that impact Montage and Mop. Changes to those packages in the corresponding `build` branch must be coordinated and released in a future, backward-incompatible release. Some care will be needed to ensure that `mop` in particular depends on compatible versions of `mr` and `montage`. This introduces a program, `mrs`, that can create stand-alone scripts from modules that only need the ability to produce and consume CommonJS modules with `require` and `exports` or `module.exports`, and depend only on a static working set. The resulting script has very little overhead and is based on the build products of Browserify. `mrs` is sufficient to build `mr/boot.js` (previously `bootstrap.js`)from the CommonJS modules in the `boot` directory. Run `npm run build`to produce a new revision of `boot.js` and `boot/preload-boilerplate.js`. This aleviates the need to perform script injection in the bootstrapping process, at the expense of introducing a build step for the bootstrapper. This change also allows a greater level of code reuse between Mr and Montage in their bootstrapping processes. This also obviates the need to embed UMD boilerplate in these modules, since they will always be used as CommonJS modules, either as sources for the build or as instruments of Node.js. Since `mrs` embeds the entirety of `boot.js`'s dependencies, it is no longer necessary to use a subrepository for these dependencies and no longer necessary to use hard-coded relative locations for these dependencies in `package.json`. These dependencies may now be installed by NPM and the maintainers of Mr and its dependencies no longer need to coordinate or hard-code their dependency trees. This is important because Q will soon be breaking up into smaller modules and packages. The `mini-url` module may now be a CommonJS module shared by Montage and Mr. Within Montage and Mr, it may be required by the name `url`. A mapping in `package.json` directs the module loader to use `mini-url.js` instead. On the Node.js side, where `mini-url.js` would not work, we fall back to Node.js's `url` module, which has an API that is a strict super-set of `mini-url.js`'s. --- adhoc.js | 2 +- bin/{mr => mr.js} | 2 +- bin/mrs.js | 31 + packages/q/q.js => boot.js | 1375 ++++++++++++++ boot/boilerplate.js | 30 + boot/browser.js | 46 + boot/preload-boilerplate.js | 3198 +++++++++++++++++++++++++++++++++ boot/preload-entry.js | 8 + boot/preload.js | 35 + boot/script-injection.js | 14 + boot/script-params.js | 65 + bootstrap-node.js | 89 - bootstrap.js | 262 --- browser.js | 21 +- build.js | 63 + mini-url.js | 28 + node.js | 74 +- package.json | 28 +- packages/q/.gitignore | 9 - packages/q/LICENSE | 19 - packages/q/package.json | 74 - packages/q/queue.js | 35 - packages/q/spec/q-spec.html | 52 - packages/q/spec/q-spec.js | 2462 ------------------------- packages/q/spec/queue-spec.js | 180 -- require.js | 1589 ++++++++-------- spec/node-spec.js | 13 +- spec/production/program.js | 1 - spec/read/package.json | 2 +- spec/require-spec.js | 17 +- spec/run.html | 2 +- 31 files changed, 5790 insertions(+), 4036 deletions(-) rename bin/{mr => mr.js} (62%) create mode 100755 bin/mrs.js rename packages/q/q.js => boot.js (53%) create mode 100644 boot/boilerplate.js create mode 100644 boot/browser.js create mode 100644 boot/preload-boilerplate.js create mode 100644 boot/preload-entry.js create mode 100644 boot/preload.js create mode 100644 boot/script-injection.js create mode 100644 boot/script-params.js delete mode 100644 bootstrap-node.js delete mode 100644 bootstrap.js create mode 100644 build.js create mode 100644 mini-url.js delete mode 100644 packages/q/.gitignore delete mode 100644 packages/q/LICENSE delete mode 100644 packages/q/package.json delete mode 100644 packages/q/queue.js delete mode 100644 packages/q/spec/q-spec.html delete mode 100644 packages/q/spec/q-spec.js delete mode 100644 packages/q/spec/queue-spec.js diff --git a/adhoc.js b/adhoc.js index 2c5200f6..f4511c3f 100644 --- a/adhoc.js +++ b/adhoc.js @@ -1,5 +1,5 @@ -var URL = require("mini-url"); +var URL = require("url"); var QS = require("qs"); var a = document.createElement("a"); diff --git a/bin/mr b/bin/mr.js similarity index 62% rename from bin/mr rename to bin/mr.js index 635bd9d0..9416c8f4 100755 --- a/bin/mr +++ b/bin/mr.js @@ -1,2 +1,2 @@ #!/usr/bin/env node --harmony_weakmaps --harmony_proxies -require("../bootstrap-node"); +require("../node").boot().done(); diff --git a/bin/mrs.js b/bin/mrs.js new file mode 100755 index 00000000..bcc6d701 --- /dev/null +++ b/bin/mrs.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +var optimist = require("optimist"); +var build = require("../build"); + +var argv = optimist + .default("execute", "") + .alias("e", "execute") + .alias("h", "help") + .argv; + +function usage() { + console.log("Usage: mrs [-e ]"); + console.log(""); + console.log(" Creates a - - - - - - - - - - - - - - - - diff --git a/packages/q/spec/q-spec.js b/packages/q/spec/q-spec.js deleted file mode 100644 index 2c31975d..00000000 --- a/packages/q/spec/q-spec.js +++ /dev/null @@ -1,2462 +0,0 @@ -"use strict"; -/*jshint newcap: false*/ -/*global Q: true, describe: false, it: false, expect: false, beforeEach: false, - afterEach: false, require: false, jasmine: false, waitsFor: false, - runs: false */ - -if (typeof Q === "undefined" && typeof require !== "undefined") { - // For Node compatibility. - global.Q = require("../q"); - require("./lib/jasmine-promise"); -} - -var REASON = "this is not an error, but it might show up in the console"; - -// In browsers that support strict mode, it'll be `undefined`; otherwise, the global. -var calledAsFunctionThis = (function () { return this; }()); - -afterEach(function () { - Q.onerror = null; -}); - -describe("computing sum of integers using promises", function() { - it("should compute correct result without blowing stack", function () { - var array = []; - var iters = 1000; - for (var i = 1; i <= iters; i++) { - array.push(i); - } - - var pZero = Q.fulfill(0); - var result = array.reduce(function (promise, nextVal) { - return promise.then(function (currentVal) { - return Q.fulfill(currentVal + nextVal); - }); - }, pZero); - - return result.then(function (value) { - expect(value).toEqual(iters * (iters + 1) / 2); - }); - }); -}); - -describe("Q function", function () { - it("should result in a fulfilled promise when given a value", function () { - expect(Q(5).isFulfilled()).toBe(true); - }); - - it("should be the identity when given promise", function () { - var f = Q.fulfill(5); - var r = Q.reject(new Error("aaargh")); - var p = Q.promise(function () { }); - - expect(Q(f)).toBe(f); - expect(Q(r)).toBe(r); - expect(Q(p)).toBe(p); - }); -}); - -describe("defer and when", function () { - - it("resolve before when", function () { - var turn = 0; - var deferred = Q.defer(); - deferred.resolve(10); - var promise = Q.when(deferred.promise, function (value) { - expect(turn).toEqual(1); - expect(value).toEqual(10); - }); - turn++; - return promise; - }); - - it("reject before when", function () { - var turn = 0; - var deferred = Q.defer(); - deferred.reject(-1); - var promise = Q.when(deferred.promise, function () { - expect(true).toBe(false); - }, function (value) { - expect(turn).toEqual(1); - expect(value).toEqual(-1); - }); - turn++; - return promise; - }); - - it("when before resolve", function () { - var turn = 0; - var deferred = Q.defer(); - var promise = deferred.promise.then(function (value) { - expect(turn).toEqual(2); - expect(value).toEqual(10); - turn++; - }); - Q.nextTick(function () { - expect(turn).toEqual(1); - deferred.resolve(10); - turn++; - }); - turn++; - return promise; - }); - - it("when before reject", function () { - var turn = 0; - var deferred = Q.defer(); - var promise = deferred.promise.then(function () { - expect(true).toBe(false); - }, function (value) { - expect(turn).toEqual(2); - expect(value).toEqual(-1); - turn++; - }); - Q.nextTick(function () { - expect(turn).toEqual(1); - deferred.reject(-1); - turn++; - }); - turn++; - return promise; - }); - - it("resolves multiple observers", function (done) { - var nextTurn = false; - - var resolution = "Taram pam param!"; - var deferred = Q.defer(); - var count = 10; - var i = 0; - - function resolve(value) { - i++; - expect(value).toBe(resolution); - expect(nextTurn).toBe(true); - if (i === count) { - done(); - } - } - - while (++i <= count) { - Q.when(deferred.promise, resolve); - } - - deferred.resolve(resolution); - i = 0; - nextTurn = true; - }); - - it("observers called even after throw", function () { - var threw = false; - var deferred = Q.defer(); - Q.when(deferred.promise, function () { - threw = true; - throw new Error(REASON); - }); - var promise = Q.when(deferred.promise, function (value) { - expect(value).toEqual(10); - }, function () { - expect("not").toEqual("here"); - }); - deferred.resolve(10); - return promise; - }); - - it("returns `undefined` from the deferred's methods", function () { - expect(Q.defer().resolve()).toBe(undefined); - expect(Q.defer().reject()).toBe(undefined); - }); - -}); - -describe("always next tick", function () { - - it("generated by `resolve`", function () { - var turn = 0; - var promise = Q.when(Q.resolve(), function () { - expect(turn).toEqual(1); - }); - turn++; - return promise; - }); - - it("generated by `reject`", function () { - var turn = 0; - var promise = Q.when(Q.reject(), function () { - expect(true).toBe(false); - }, function () { - expect(turn).toEqual(1); - }); - turn++; - return promise; - }); - -}); - -describe("progress", function () { - - it("calls a single progress listener", function () { - var progressed = false; - var deferred = Q.defer(); - - var promise = Q.when( - deferred.promise, - function () { - expect(progressed).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed = true; - } - ); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("calls multiple progress listeners", function () { - var progressed1 = false; - var progressed2 = false; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(progressed1).toBe(true); - expect(progressed2).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed1 = true; - } - ); - Q.when(deferred.promise, null, null, function () { - progressed2 = true; - }); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("calls all progress listeners even if one throws", function () { - var progressed1 = false; - var progressed2 = false; - var progressed3 = false; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(progressed1).toBe(true); - expect(progressed2).toBe(true); - expect(progressed3).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed1 = true; - } - ); - - Q.onerror = function () { }; - - Q.when(deferred.promise, null, null, function () { - progressed2 = true; - throw new Error("just a test, ok if it shows up in the console"); - }); - Q.when(deferred.promise, null, null, function () { - progressed3 = true; - }); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("calls the progress listener even if later rejected", function () { - var progressed = false; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(true).toBe(false); - }, - function () { - expect(progressed).toEqual(true); - }, - function () { - progressed = true; - } - ); - - deferred.notify(); - deferred.reject(); - - return promise; - }); - - it("calls the progress listener with the notify values", function () { - var progressValues = []; - var desiredProgressValues = [{}, {}, "foo", 5]; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - for (var i = 0; i < desiredProgressValues.length; ++i) { - var desired = desiredProgressValues[i]; - var actual = progressValues[i]; - expect(actual).toBe(desired); - } - }, - function () { - expect(true).toBe(false); - }, - function (value) { - progressValues.push(value); - } - ); - - for (var i = 0; i < desiredProgressValues.length; ++i) { - deferred.notify(desiredProgressValues[i]); - } - deferred.resolve(); - - return promise; - }); - - it("does not call the progress listener if notify is called after fulfillment", function () { - var deferred = Q.defer(); - var called = false; - - Q.when(deferred.promise, null, null, function () { - called = true; - }); - - deferred.resolve(); - deferred.notify(); - - return Q.delay(10).then(function () { - expect(called).toBe(false); - }); - }); - - it("does not call the progress listener if notify is called after rejection", function () { - var deferred = Q.defer(); - var called = false; - - Q.when(deferred.promise, null, null, function () { - called = true; - }); - - deferred.reject(); - deferred.notify(); - - return Q.delay(10).then(function () { - expect(called).toBe(false); - }); - }); - - it("should not save and re-emit progress notifications", function () { - var deferred = Q.defer(); - var progressValues = []; - - deferred.notify(1); - - var promise = Q.when( - deferred.promise, - function () { - expect(progressValues).toEqual([2]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - deferred.notify(2); - deferred.resolve(); - - return promise; - }); - - it("should allow attaching progress listeners w/ .progress", function () { - var progressed = false; - var deferred = Q.defer(); - - deferred.promise.progress(function () { - progressed = true; - }); - - deferred.notify(); - deferred.resolve(); - - return deferred.promise; - }); - - it("should allow attaching progress listeners w/ Q.progress", function () { - var progressed = false; - var deferred = Q.defer(); - - Q.progress(deferred.promise, function () { - progressed = true; - }); - - deferred.notify(); - deferred.resolve(); - - return deferred.promise; - }); - - it("should call the progress listener with undefined context", function () { - var progressed = false; - var progressContext = {}; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(progressed).toBe(true); - expect(progressContext).toBe(calledAsFunctionThis); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed = true; - progressContext = this; - } - ); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("should forward only the first notify argument to listeners", function () { - var progressValueArrays = []; - var deferred = Q.defer(); - - var promise = Q.when( - deferred.promise, - function () { - expect(progressValueArrays).toEqual([[1], [2], [4]]); - }, - function () { - expect(true).toBe(false); - }, - function () { - var args = Array.prototype.slice.call(arguments); - progressValueArrays.push(args); - } - ); - - deferred.notify(1); - deferred.notify(2, 3); - deferred.notify(4, 5, 6); - deferred.resolve(); - - return promise; - }); - - it("should work with .then as well", function () { - var progressed = false; - var deferred = Q.defer(); - - var promise = deferred.promise.then( - function () { - expect(progressed).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed = true; - } - ); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("should re-throw all errors thrown by listeners to Q.onerror", function () { - var theError = new Error("boo!"); - - var def = Q.defer(); - def.promise.progress(function () { - throw theError; - }); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(error).toBe(theError); - deferred.resolve(); - }; - Q.delay(100).then(deferred.reject); - - def.notify(); - - return deferred.promise; - }); -}); - -describe("promises for objects", function () { - - describe("get", function () { - - it("fulfills a promise", function () { - var deferred = Q.defer(); - deferred.resolve({a: 1}); - return deferred.promise.get("a") - .then(function (a) { - expect(a).toBe(1); - }); - }); - - it("propagates a rejection", function () { - var exception = new Error("boo!"); - return Q.fcall(function () { - throw exception; - }) - .get("a") - .then(function () { - expect("be").toBe("not to be"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("set", function () { - - it("fulfills a promise", function () { - var object = {}; - return Q.resolve(object) - .set("a", 1) - .then(function (result) { - expect(result).toBe(undefined); - expect(object.a).toBe(1); - }); - }); - - it("propagates a rejection", function () { - var exception = new Error("Gah!"); - return Q.reject(exception) - .set("a", 1) - .then(function () { - expect("frozen over").toBe("quite warm"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("del", function () { - - it("fulfills a promise", function () { - var object = {a: 10}; - return Q.fcall(function () { - return object; - }) - .del("a") - .then(function (result) { - expect("a" in object).toBe(false); - expect(result).toBe(void 0); - }, function () { - expect("up").toBe("down"); - }); - }); - - it("propagates a rejection", function () { - var exception = new Error("hah-hah"); - return Q.fcall(function () { - throw exception; - }) - .del("a") - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("post", function () { - - it("fulfills a promise", function () { - var subject = { - a: function a(value) { - this._a = value; - return 1 + value; - } - }; - return Q.when(Q.post(subject, "a", [1]), function (two) { - expect(subject._a).toBe(1); - expect(two).toBe(2); - }); - }); - - it("works as apply when given no name", function () { - return Q.resolve(function (a, b, c) { - return a + b + c; - }) - .post(undefined, [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - }); - - describe("send", function () { - - it("fulfills a promise", function () { - var foo; - var subject = { - foo: function (_bar) { - return _bar; - }, - bar: function (_foo, _bar) { - foo = _foo; - return this.foo(_bar); - } - }; - return Q.send(subject, "bar", 1, 2) - .then(function (two) { - expect(foo).toEqual(1); - expect(two).toEqual(2); - }); - }); - - it("is rejected for undefined method", function () { - var subject = {}; - return Q.resolve(subject) - .send("foo") - .then(function () { - expect("here").toEqual("not here"); - }, function () { - }); - }); - - it("is rejected for undefined object", function () { - return Q.resolve() - .send("foo") - .then(function () { - expect("here").toEqual("not here"); - }, function () { - }); - }); - - }); - - describe("keys", function () { - - function Klass (a, b) { - this.a = a; - this.b = b; - } - Klass.prototype.notOwn = 1; - - it("fulfills a promise", function () { - return Q.keys(new Klass(10, 20)) - .then(function (keys) { - expect(keys.sort()).toEqual(["a", "b"]); - }); - }); - - }); - -}); - -describe("promises for functions", function () { - - describe("fapply", function () { - it("fulfills a promise using arguments", function () { - return Q.resolve(function (a, b, c) { - return a + b + c; - }) - .fapply([1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - }); - - describe("fcall", function () { - it("fulfills a promise using arguments", function () { - return Q.resolve(function (a, b, c) { - return a + b + c; - }) - .fcall(1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - }); - - describe("fbind", function () { - - it("accepts a promise for a function", function () { - return Q.fbind(Q.resolve(function (high, low) { - return high - low; - })) - (2, 1) - .then(function (difference) { - expect(difference).toEqual(1); - }); - }); - - it("chains partial application on a promise for a function", function () { - return Q.resolve(function (a, b) { - return a * b; - }) - .fbind(2)(3) - .then(function (product) { - expect(product).toEqual(6); - }); - }); - - it("returns a fulfilled promise", function () { - var result = {}; - var bound = Q.fbind(function () { - return result; - }); - return bound() - .then(function (_result) { - expect(_result).toBe(result); - }); - }); - - it("returns a rejected promise from a thrown error", function () { - var exception = new Error("Boo!"); - var bound = Q.fbind(function () { - throw exception; - }); - return bound() - .then(function () { - expect("flying pigs").toBe("swillin' pigs"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("passes arguments through", function () { - var x = {}, y = {}; - var bound = Q.fbind(function (a, b) { - expect(a).toBe(x); - expect(b).toBe(y); - }); - return bound(x, y); - }); - - it("passes and also partially applies arguments", function () { - var x = {}, y = {}; - var bound = Q.fbind(function (a, b) { - expect(a).toBe(x); - expect(b).toBe(y); - }, x); - return bound(y); - }); - - it("doesn't bind `this`", function () { - var theThis = { me: "this" }; - var bound = Q.fbind(function () { - expect(this).toBe(theThis); - }); - - return bound.call(theThis); - }); - - }); - -}); - -describe("inspect", function () { - - it("for a fulfilled promise", function () { - expect(Q.resolve(10).inspect()).toEqual({ - state: "fulfilled", - value: 10 - }); - }); - - it("for a rejected promise", function () { - var error = new Error("In your face."); - var rejected = Q.reject(error); - expect(rejected.inspect()).toEqual({ - state: "rejected", - reason: error - }); - }); - - it("for a pending, unresolved promise", function () { - var pending = Q.defer().promise; - expect(pending.inspect()).toEqual({ state: "pending" }); - }); - - it("for a promise resolved to a rejected promise", function () { - var deferred = Q.defer(); - var error = new Error("Rejected!"); - var rejected = Q.reject(error); - deferred.resolve(rejected); - - expect(deferred.promise.inspect()).toEqual({ - state: "rejected", - reason: error - }); - }); - - it("for a promise resolved to a fulfilled promise", function () { - var deferred = Q.defer(); - var fulfilled = Q.resolve(10); - deferred.resolve(fulfilled); - - expect(deferred.promise.inspect()).toEqual({ - state: "fulfilled", - value: 10 - }); - }); - - it("for a promise resolved to a pending promise", function () { - var a = Q.defer(); - var b = Q.defer(); - a.resolve(b.promise); - - expect(a.promise.inspect()).toEqual({ state: "pending" }); - }); - -}); - -describe("promise states", function () { - - it("of fulfilled value", function () { - expect(Q.isFulfilled(void 0)).toBe(true); - expect(Q.isRejected(false)).toBe(false); - expect(Q.isPending(true)).toBe(false); - }); - - it("of fulfillment", function () { - var promise = Q.resolve(10); - expect(Q.isFulfilled(promise)).toBe(true); - expect(promise.isFulfilled()).toBe(true); - expect(Q.isRejected(promise)).toBe(false); - expect(promise.isRejected()).toBe(false); - expect(Q.isPending(promise)).toBe(false); - expect(promise.isPending()).toBe(false); - }); - - it("of rejection", function () { - var error = new Error("Oh, snap."); - var promise = Q.reject(error); - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(true); - expect(promise.isPending()).toBe(false); - }); - - it("of rejection with a falsy value", function () { - var promise = Q.reject(undefined); - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(true); - expect(promise.isPending()).toBe(false); - }); - - it("of deferred", function () { - var deferred = Q.defer(); - var promise = deferred.promise; - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(false); - expect(promise.isPending()).toBe(true); - }); - - it("of deferred rejection", function () { - var deferred = Q.defer(); - var rejection = Q.reject(new Error("Rejected!")); - deferred.resolve(rejection); - var promise = deferred.promise; - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(true); - expect(promise.isPending()).toBe(false); - }); - - it("of deferred fulfillment", function () { - var deferred = Q.defer(); - deferred.resolve(10); - var promise = deferred.promise; - expect(promise.isFulfilled()).toBe(true); - expect(promise.isRejected()).toBe(false); - expect(promise.isPending()).toBe(false); - }); - - it("of deferred deferred", function () { - var a = Q.defer(); - var b = Q.defer(); - a.resolve(b.promise); - var promise = a.promise; - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(false); - expect(promise.isPending()).toBe(true); - }); - - it("of isFulfilled side effects", function () { - var deferred = Q.defer(); - var finished = false; - - waitsFor(function () { - return finished; - }); - - var parentPromise = deferred.promise; - - var childPromise = parentPromise.then(function () { - expect(parentPromise.isFulfilled()).toBe(true); - expect(childPromise.isFulfilled()).toBe(false); - - return parentPromise.then(function (value) { - finished = true; - return value + 1; - }); - }); - - deferred.resolve(1); - - runs(function () { - expect(childPromise.isPending()).toBe(false); - expect(childPromise.isRejected()).toBe(false); - expect(childPromise.isFulfilled()).toBe(true); - expect(childPromise.inspect().value).toBe(2); - }); - }); - -}); - -describe("propagation", function () { - - it("propagate through then with no callback", function () { - return Q.resolve(10) - .then() - .then(function (ten) { - expect(ten).toBe(10); - }); - }); - - it("propagate through then with modifying callback", function () { - return Q.resolve(10) - .then(function (ten) { - return ten + 10; - }) - .then(function (twen) { - expect(twen).toBe(20); - }); - }); - - it("errback recovers from exception", function () { - var error = new Error("Bah!"); - return Q.reject(error) - .then(null, function (_error) { - expect(_error).toBe(error); - return 10; - }) - .then(function (value) { - expect(value).toBe(10); - }); - }); - - it("rejection propagates through then with no errback", function () { - var error = new Error("Foolish mortals!"); - return Q.reject(error) - .then() - .then(null, function (_error) { - expect(_error).toBe(error); - }); - }); - - it("rejection intercepted and rethrown", function () { - var error = new Error("Foolish mortals!"); - var nextError = new Error("Silly humans!"); - return Q.reject(error) - .fail(function () { - throw nextError; - }) - .then(null, function (_error) { - expect(_error).toBe(nextError); - }); - }); - - it("resolution is forwarded through deferred promise", function () { - var a = Q.defer(); - var b = Q.defer(); - a.resolve(b.promise); - b.resolve(10); - return a.promise.then(function (eh) { - expect(eh).toEqual(10); - }); - }); - - it("should propagate progress by default", function () { - var d = Q.defer(); - - var progressValues = []; - var promise = d.promise - .then() - .then( - function () { - expect(progressValues).toEqual([1]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - d.notify(1); - d.resolve(); - - return promise; - }); - - it("should allow translation of progress in the progressback", function () { - var d = Q.defer(); - - var progressValues = []; - var promise = d.promise - .progress(function (p) { - return p + 5; - }) - .then( - function () { - expect(progressValues).toEqual([10]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - d.notify(5); - d.resolve(); - - return promise; - }); - - - it("should stop progress propagation if an error is thrown", function () { - var def = Q.defer(); - var p2 = def.promise.progress(function () { - throw new Error("boo!"); - }); - - Q.onerror = function () { /* just swallow it for this test */ }; - - var progressValues = []; - var result = p2.then( - function () { - expect(progressValues).toEqual([]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - def.notify(); - def.resolve(); - return result; - }); -}); - -describe("all", function () { - - it("resolves when passed an empty array", function () { - return Q.all([]); - }); - - it("resolves after any constituent promise is rejected", function () { - var toResolve = Q.defer(); // never resolve - var toReject = Q.defer(); - var promises = [toResolve.promise, toReject.promise]; - var promise = Q.all(promises); - - toReject.reject(new Error("Rejected")); - - return Q.delay(250) - .then(function () { - expect(promise.isRejected()).toBe(true); - }) - .timeout(1000); - }); - - it("resolves foreign thenables", function () { - var normal = Q.resolve(1); - var foreign = { then: function (f) { f(2); } }; - - return Q.all([normal, foreign]) - .then(function (result) { - expect(result).toEqual([1, 2]); - }); - }); - - it("resolves when passed an sparse array", function () { - var toResolve = Q.defer(); - var promises = []; - promises[0] = Q.resolve(0); - promises[2] = toResolve.promise; - var promise = Q.all(promises); - - toResolve.resolve(2); - - return promise.then(function (result) { - expect(result).toEqual([0, void 0, 2]); - }); - }); - - it("modifies the input array", function () { - var input = [Q.resolve(0), Q.resolve(1)]; - - return Q.all(input).then(function (result) { - expect(result).toBe(input); - expect(input).toEqual([0, 1]); - }); - }); - -}); - -describe("allSettled", function () { - - it("deals with a mix of non-promises and promises", function () { - return Q.allSettled([1, Q.resolve(2), Q.reject(3)]) - .then(function (snapshots) { - expect(snapshots).toEqual([ - { state: "fulfilled", value: 1 }, - { state: "fulfilled", value: 2 }, - { state: "rejected", reason: 3 } - ]); - }, function () { - expect(true).toBe(false); - }); - }); - - it("is settled after every constituent promise is settled", function () { - var toFulfill = Q.defer(); - var toReject = Q.defer(); - var promises = [toFulfill.promise, toReject.promise]; - var fulfilled; - var rejected; - - Q.fcall(function () { - toReject.reject(); - rejected = true; - }) - .delay(15) - .then(function () { - toFulfill.resolve(); - fulfilled = true; - }); - - return Q.allSettled(promises) - .then(function () { - expect(fulfilled).toBe(true); - expect(rejected).toBe(true); - }); - }); - - it("modifies the input array", function () { - var input = [1, Q.resolve(2), Q.reject(3)]; - - return Q.allSettled(input) - .then(function (snapshots) { - expect(snapshots).toBe(input); - expect(input).toEqual([ - { state: "fulfilled", value: 1 }, - { state: "fulfilled", value: 2 }, - { state: "rejected", reason: 3 } - ]); - }); - }); - -}); - -describe("spread", function () { - - it("spreads values across arguments", function () { - return Q.spread([1, 2, 3], function (a, b) { - expect(b).toBe(2); - }); - }); - - it("spreads promises for arrays across arguments", function () { - return Q.resolve([Q.resolve(10)]) - .spread(function (value) { - expect(value).toEqual(10); - }); - }); - - it("spreads arrays of promises across arguments", function () { - var deferredA = Q.defer(); - var deferredB = Q.defer(); - - var promise = Q.spread([deferredA.promise, deferredB.promise], - function (a, b) { - expect(a).toEqual(10); - expect(b).toEqual(20); - }); - - Q.delay(5).then(function () { - deferredA.resolve(10); - }); - Q.delay(10).then(function () { - deferredB.resolve(20); - }); - - return promise; - }); - - it("calls the errback when given a rejected promise", function () { - var err = new Error(); - return Q.spread([Q.resolve(10), Q.reject(err)], - function () { - expect(true).toBe(false); - }, - function (actual) { - expect(actual).toBe(err); - } - ); - }); - -}); - -describe("fin", function () { - - var exception1 = new Error("boo!"); - var exception2 = new TypeError("evil!"); - - describe("when the promise is fulfilled", function () { - - it("should call the callback", function () { - var called = false; - - return Q.resolve("foo") - .fin(function () { - called = true; - }) - .then(function () { - expect(called).toBe(true); - }); - }); - - it("should fulfill with the original value", function () { - return Q.resolve("foo") - .fin(function () { - return "bar"; - }) - .then(function (result) { - expect(result).toBe("foo"); - }); - }); - - describe("when the callback returns a promise", function () { - - describe("that is fulfilled", function () { - it("should fulfill with the original reason after that promise resolves", function () { - var promise = Q.delay(250); - - return Q.resolve("foo") - .fin(function () { - return promise; - }) - .then(function (result) { - expect(Q.isPending(promise)).toBe(false); - expect(result).toBe("foo"); - }); - }); - }); - - describe("that is rejected", function () { - it("should reject with this new rejection reason", function () { - return Q.resolve("foo") - .fin(function () { - return Q.reject(exception1); - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - }); - }); - }); - - }); - - describe("when the callback throws an exception", function () { - it("should reject with this new exception", function () { - return Q.resolve("foo") - .fin(function () { - throw exception1; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - }); - }); - }); - - }); - - describe("when the promise is rejected", function () { - - it("should call the callback", function () { - var called = false; - - return Q.reject(exception1) - .fin(function () { - called = true; - }) - .then(function () { - expect(called).toBe(true); - }, function () { - expect(called).toBe(true); - }); - }); - - it("should reject with the original reason", function () { - return Q.reject(exception1) - .fin(function () { - return "bar"; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - }); - }); - - describe("when the callback returns a promise", function () { - - describe("that is fulfilled", function () { - it("should reject with the original reason after that promise resolves", function () { - var promise = Q.delay(250); - - return Q.reject(exception1) - .fin(function () { - return promise; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - expect(Q.isPending(promise)).toBe(false); - }); - }); - }); - - describe("that is rejected", function () { - it("should reject with the new reason", function () { - return Q.reject(exception1) - .fin(function () { - return Q.reject(exception2); - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception2); - }); - }); - }); - - }); - - describe("when the callback throws an exception", function () { - it("should reject with this new exception", function () { - return Q.reject(exception1) - .fin(function () { - throw exception2; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception2); - }); - }); - }); - - }); - -}); - -describe("done", function () { - describe("when the promise is fulfilled", function () { - describe("and the callback does not throw", function () { - it("should call the callback and return nothing", function () { - var called = false; - - var promise = Q.resolve(); - - var returnValue = promise.done(function () { - called = true; - }); - - return promise.fail(function () { }).fin(function () { - expect(called).toBe(true); - expect(returnValue).toBe(undefined); - }); - }); - }); - - describe("and the callback throws", function () { - it("should rethrow that error in the next turn and return nothing", function () { - var turn = 0; - Q.nextTick(function () { - ++turn; - }); - - var returnValue = Q.resolve().done( - function () { - throw "foo"; - } - ); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(turn).toBe(1); - expect(error).toBe("foo"); - expect(returnValue).toBe(undefined); - deferred.resolve(); - }; - Q.delay(100).then(deferred.reject); - - return deferred.promise; - }); - }); - }); - - describe("when the promise is rejected", function () { - describe("and the errback handles it", function () { - it("should call the errback and return nothing", function () { - var called = false; - - var promise = Q.reject(new Error()); - - var returnValue = promise.done( - function () { }, - function () { - called = true; - } - ); - - return promise.fail(function () { }).fin(function () { - expect(called).toBe(true); - expect(returnValue).toBe(undefined); - }); - }); - }); - - describe("and the errback throws", function () { - it("should rethrow that error in the next turn and return nothing", function () { - var turn = 0; - Q.nextTick(function () { - ++turn; - }); - - var returnValue = Q.reject("bar").done( - null, - function () { - throw "foo"; - } - ); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(turn).toBe(1); - expect(error).toBe("foo"); - expect(returnValue).toBe(undefined); - deferred.resolve(); - }; - Q.delay(100).then(deferred.reject); - - return deferred.promise; - }); - }); - - describe("and there is no errback", function () { - it("should throw the original error in the next turn", function () { - var turn = 0; - Q.nextTick(function () { - ++turn; - }); - - var returnValue = Q.reject("bar").done(); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(turn).toBe(1); - expect(error).toBe("bar"); - expect(returnValue).toBe(undefined); - deferred.resolve(); - }; - Q.delay(10).then(deferred.reject); - - return deferred.promise; - }); - }); - }); - - it("should attach a progress listener", function () { - var deferred = Q.defer(); - - var spy = jasmine.createSpy(); - deferred.promise.done(null, null, spy); - - deferred.notify(10); - deferred.resolve(); - - return deferred.promise.then(function () { - expect(spy).toHaveBeenCalledWith(10); - }); - }); -}); - -describe("timeout", function () { - it("should do nothing if the promise fulfills quickly", function () { - return Q.delay(10).timeout(200); - }); - - it("should do nothing if the promise rejects quickly", function () { - var goodError = new Error("haha!"); - return Q.delay(10) - .then(function () { - throw goodError; - }) - .timeout(200) - .then(undefined, function (error) { - expect(error).toBe(goodError); - }); - }); - - it("should reject with a timeout error if the promise is too slow", function () { - return Q.delay(100) - .timeout(10) - .then( - function () { - expect(true).toBe(false); - }, - function (error) { - expect(/time/i.test(error.message)).toBe(true); - } - ); - }); - - it("should pass through progress notifications", function () { - var deferred = Q.defer(); - - var progressValsSeen = []; - var promise = Q.timeout(deferred.promise, 300).then(function () { - expect(progressValsSeen).toEqual([1, 2, 3]); - }, undefined, function (progressVal) { - progressValsSeen.push(progressVal); - }); - - Q.delay(5).then(function () { deferred.notify(1); }); - Q.delay(15).then(function () { deferred.notify(2); }); - Q.delay(25).then(function () { deferred.notify(3); }); - Q.delay(35).then(function () { deferred.resolve(); }); - - return promise; - }); - - it("should reject with a custom timeout error if the promise is too slow and msg was provided", function () { - return Q.delay(100) - .timeout(10, "custom") - .then( - function () { - expect(true).toBe(false); - }, - function (error) { - expect(/custom/i.test(error.message)).toBe(true); - } - ); - }); - - -}); - -describe("delay", function () { - it("should delay fulfillment", function () { - var promise = Q.resolve(5).delay(50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise; - }); - - it("should delay rejection", function () { - var promise = Q.reject(5).delay(50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise.then(undefined, function () { }); - }); - - it("should treat a single argument as a time", function () { - var promise = Q.delay(50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise; - }); - - it("should treat two arguments as a value + a time", function () { - var promise = Q.delay("what", 50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise.then(function (value) { - expect(value).toBe("what"); - }); - }); - - it("should delegate to faster passed promises, slowing them down", function () { - var promise1 = Q.delay("what", 30); - var promise2 = Q.delay(promise1, 50); - - setTimeout(function () { - expect(promise1.isPending()).toBe(false); - expect(promise2.isPending()).toBe(true); - }, 40); - - return promise2.then(function (value) { - expect(value).toBe("what"); - }); - }); - - it("should delegate to slower passed promises, staying at their speed", function () { - var promise1 = Q.delay("what", 70); - var promise2 = Q.delay(promise1, 50); - - setTimeout(function () { - expect(promise1.isPending()).toBe(true); - expect(promise2.isPending()).toBe(true); - }, 60); - - return promise2.then(function (value) { - expect(value).toBe("what"); - }); - }); - - it("should pass through progress notifications from passed promises", function () { - var deferred = Q.defer(); - - var progressValsSeen = []; - var promise = Q.delay(deferred.promise, 100).then(function () { - expect(progressValsSeen).toEqual([1, 2, 3]); - }, undefined, function (progressVal) { - progressValsSeen.push(progressVal); - }); - - Q.delay(5).then(function () { deferred.notify(1); }); - Q.delay(15).then(function () { deferred.notify(2); }); - Q.delay(25).then(function () { deferred.notify(3); }); - Q.delay(35).then(function () { deferred.resolve(); }); - - return promise; - }); -}); - -describe("thenResolve", function () { - describe("Resolving with a non-thenable value", function () { - it("returns a promise for that object once the promise is resolved", function () { - var waited = false; - return Q.delay(20) - .then(function () { - waited = true; - }) - .thenResolve("foo") - .then(function (val) { - expect(waited).toBe(true); - expect(val).toBe("foo"); - }); - }); - - describe("based off a rejected promise", function () { - it("does nothing, letting the rejection flow through", function () { - return Q.reject("boo") - .thenResolve("foo") - .then( - function () { - expect(true).toBe(false); - }, - function (reason) { - expect(reason).toBe("boo"); - } - ); - }); - }); - }); - - describe("Resolving with an promise", function () { - it("returns a promise for the result of that promise once the promise is resolved", function () { - var waited = false; - return Q.delay(20) - .then(function () { - waited = true; - }) - .thenResolve(Q.resolve("foo")) - .then(function (val) { - expect(waited).toBe(true); - expect(val).toBe("foo"); - }); - }); - }); -}); - -describe("thenReject", function () { - describe("Rejecting with a reason", function () { - it("returns a promise rejected with that object once the original promise is resolved", function () { - var waited = false; - return Q.delay(20) - .then(function () { - waited = true; - }) - .thenReject("foo") - .then( - function () { - expect(true).toBe(false); - }, - function (reason) { - expect(waited).toBe(true); - expect(reason).toBe("foo"); - } - ); - }); - - describe("based off a rejected promise", function () { - it("does nothing, letting the rejection flow through", function () { - return Q.reject("boo") - .thenResolve("foo") - .then( - function () { - expect(true).toBe(false); - }, - function (reason) { - expect(reason).toBe("boo"); - } - ); - }); - }); - }); -}); - - -describe("thenables", function () { - - it("assimilates a thenable with fulfillment with resolve", function () { - return Q.resolve({ - then: function (resolved) { - resolved(10); - } - }) - .then(function (ten) { - expect(ten).toEqual(10); - }) - .then(function (undefined) { - expect(undefined).toEqual(void 0); - }); - }); - - it("assimilates a thenable with progress and fulfillment (using resolve)", function () { - var progressValueArrays = []; - return Q.resolve({ - then: function (fulfilled, rejected, progressed) { - Q.nextTick(function () { - progressed(1, 2); - progressed(3, 4, 5); - fulfilled(); - }); - } - }) - .progress(function () { - progressValueArrays.push(Array.prototype.slice.call(arguments)); - }) - .then(function () { - expect(progressValueArrays).toEqual([[1], [3]]); - }); - }); - - it("assimilates a thenable with progress and fulfillment (using when)", function () { - var progressValueArrays = []; - return Q.when({ - then: function (fulfilled, rejected, progressed) { - Q.nextTick(function () { - progressed(1, 2); - progressed(3, 4, 5); - fulfilled(); - }); - } - }) - .progress(function () { - progressValueArrays.push(Array.prototype.slice.call(arguments)); - }) - .then(function () { - expect(progressValueArrays).toEqual([[1], [3]]); - }); - }); - - it("flows fulfillment into a promise pipeline", function () { - return Q.resolve({ - then: function (resolved) { - resolved([10]); - } - }) - .get(0) - .then(function (ten) { - expect(ten).toEqual(10); - }); - }); - - it("assimilates an immediately-fulfilled thenable in allSettled", function () { - return Q.allSettled([ - {then: function (win) { - win(10); - }} - ]) - .then(function (snapshots) { - expect(snapshots).toEqual([{ state: "fulfilled", value: 10 }]); - }); - }); - - it("assimilates an eventually-fulfilled thenable in allSettled", function () { - return Q.allSettled([ - {then: function (win) { - setTimeout(function () { - win(10); - }, 100); - }} - ]) - .then(function (snapshots) { - expect(snapshots).toEqual([{ state: "fulfilled", value: 10 }]); - }); - }); - -}); - -describe("node support", function () { - - var exception = new Error("That is not your favorite color."); - - var obj = { - method: function (a, b, c, callback) { - callback(null, a + b + c); - }, - thispChecker: function (callback) { - callback(null, this === obj); - }, - errorCallbacker: function (a, b, c, callback) { - callback(exception); - }, - errorThrower: function () { - throw exception; - } - }; - - describe("nfapply", function () { - - it("fulfills with callback result", function () { - return Q.nfapply(function (a, b, c, callback) { - callback(null, a + b + c); - }, [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("rejects with callback error", function () { - var exception = new Error("That is not your favorite color."); - return Q.nfapply(function (a, b, c, callback) { - callback(exception); - }, [1, 2, 3]) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("nfcall", function () { - it("fulfills with callback result", function () { - return Q.nfcall(function (a, b, c, callback) { - callback(null, a + b + c); - }, 1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("rejects with callback error", function () { - var exception = new Error("That is not your favorite color."); - return Q.nfcall(function (a, b, c, callback) { - callback(exception); - }, 1, 2, 3) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("nfbind", function () { - - it("mixes partial application with complete application", function () { - return Q.nfbind(function (a, b, c, d, callback) { - callback(null, a + b + c + d); - }, 1, 2).call({}, 3, 4) - .then(function (ten) { - expect(ten).toBe(10); - }); - }); - - }); - - describe("nbind", function () { - - it("binds this, and mixes partial application with complete application", function () { - return Q.nbind(function (a, b, c, callback) { - callback(null, this + a + b + c); - }, 1, 2).call(3 /* effectively ignored as fn bound to 1 */, 4, 5) - .then(function (twelve) { - expect(twelve).toBe(12); - }); - }); - - it("second arg binds this", function() { - var expectedThis = { test: null }; - - return Q.nbind(function(callback) { - callback(null, this); - }, expectedThis).call() - .then(function(actualThis) { - expect(actualThis).toEqual(expectedThis); - }); - }); - - }); - describe("npost", function () { - - it("fulfills with callback result", function () { - return Q.npost(obj, "method", [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("gets the correct thisp", function () { - return Q.npost(obj, "thispChecker", []) - .then(function (result) { - expect(result).toBe(true); - }); - }); - - it("rejects with callback error", function () { - return Q.npost(obj, "errorCallbacker", [1, 2, 3]) - .then(function () { - expect("blue").toBe("no, yellow!"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("rejects with thrown error", function () { - return Q.npost(obj, "errorThrower", [1, 2, 3]) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("works on promises for objects with Node methods", function () { - return Q.resolve(obj) - .npost("method", [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - }); - - describe("nsend", function () { - - it("fulfills with callback result", function () { - return Q.nsend(obj, "method", 1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("gets the correct thisp", function () { - return Q.nsend(obj, "thispChecker") - .then(function (result) { - expect(result).toBe(true); - }); - }); - - it("rejects with callback error", function () { - return Q.nsend(obj, "errorCallbacker", 1, 2, 3) - .then(function () { - expect("blue").toBe("no, yellow!"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("rejects with thrown error", function () { - return Q.nsend(obj, "errorThrower", 1, 2, 3) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("works on promises for objects with Node methods", function () { - return Q.resolve(obj) - .nsend("method", 1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - }); - - describe("deferred.makeNodeResolver", function () { - - it("fulfills a promise with a single callback argument", function () { - var deferred = Q.defer(); - var callback = deferred.makeNodeResolver(); - callback(null, 10); - return deferred.promise.then(function (value) { - expect(value).toBe(10); - }); - }); - - it("fulfills a promise with multiple callback arguments", function () { - var deferred = Q.defer(); - var callback = deferred.makeNodeResolver(); - callback(null, 10, 20); - return deferred.promise.then(function (value) { - expect(value).toEqual([10, 20]); - }); - }); - - it("rejects a promise", function () { - var deferred = Q.defer(); - var callback = deferred.makeNodeResolver(); - var exception = new Error("Holy Exception of Anitoch"); - callback(exception); - return deferred.promise.then(function () { - expect(5).toBe(3); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("nodeify", function () { - - it("calls back with a resolution", function () { - var spy = jasmine.createSpy(); - Q.resolve(10).nodeify(spy); - waitsFor(function () { - return spy.argsForCall.length; - }); - runs(function () { - expect(spy.argsForCall).toEqual([[null, 10]]); - }); - }); - - it("calls back with an error", function () { - var spy = jasmine.createSpy(); - Q.reject(10).nodeify(spy); - waitsFor(function () { - return spy.argsForCall.length; - }); - runs(function () { - expect(spy.argsForCall).toEqual([[10]]); - }); - }); - - it("forwards a promise", function () { - return Q.resolve(10).nodeify().then(function (ten) { - expect(ten).toBe(10); - }); - }); - - }); - -}); - -describe("isPromise", function () { - it("returns true if passed a promise", function () { - expect(Q.isPromise(Q.resolve(10))).toBe(true); - }); - - it("returns false if not passed a promise", function () { - expect(Q.isPromise(undefined)).toBe(false); - expect(Q.isPromise(null)).toBe(false); - expect(Q.isPromise(10)).toBe(false); - expect(Q.isPromise("str")).toBe(false); - expect(Q.isPromise("")).toBe(false); - expect(Q.isPromise(true)).toBe(false); - expect(Q.isPromise(false)).toBe(false); - expect(Q.isPromise({})).toBe(false); - expect(Q.isPromise({ - then: function () {} - })).toBe(false); - expect(Q.isPromise(function () {})).toBe(false); - }); -}); - -describe("isPromiseAlike", function () { - it("returns true if passed a promise like object", function () { - expect(Q.isPromiseAlike(Q.resolve(10))).toBe(true); - expect(Q.isPromiseAlike({ - then: function () {} - })).toBe(true); - }); - - it("returns false if not passed a promise like object", function () { - expect(Q.isPromiseAlike(undefined)).toBe(false); - expect(Q.isPromiseAlike(null)).toBe(false); - expect(Q.isPromiseAlike(10)).toBe(false); - expect(Q.isPromiseAlike("str")).toBe(false); - expect(Q.isPromiseAlike("")).toBe(false); - expect(Q.isPromiseAlike(true)).toBe(false); - expect(Q.isPromiseAlike(false)).toBe(false); - expect(Q.isPromiseAlike({})).toBe(false); - expect(Q.isPromiseAlike(function () {})).toBe(false); - }); -}); - -if (typeof require === "function") { - var domain; - try { - domain = require("domain"); - } catch (e) { } - - if (domain) { - var EventEmitter = require("events").EventEmitter; - - describe("node domain support", function () { - var d; - - beforeEach(function () { - d = domain.create(); - }); - afterEach(function() { - d.dispose(); - }); - - it("should work for non-promise async inside a promise handler", - function (done) { - var error = new Error("should be caught by the domain"); - - d.run(function () { - Q.resolve().then(function () { - setTimeout(function () { - throw error; - }, 10); - }); - }); - - var errorTimeout = setTimeout(function () { - done(new Error("Wasn't caught")); - }, 100); - - d.on("error", function (theError) { - expect(theError).toBe(error); - clearTimeout(errorTimeout); - done(); - }); - }); - - it("should transfer errors from `done` into the domain", - function (done) { - var error = new Error("should be caught by the domain"); - - d.run(function () { - Q.reject(error).done(); - }); - - var errorTimeout = setTimeout(function () { - done(new Error("Wasn't caught")); - }, 100); - - d.on("error", function (theError) { - expect(theError).toBe(error); - clearTimeout(errorTimeout); - done(); - }); - }); - - it("should take care of re-used event emitters", function (done) { - // See discussion in https://github.com/kriskowal/q/issues/120 - var error = new Error("should be caught by the domain"); - - var e = new EventEmitter(); - - d.run(function () { - callAsync().done(); - }); - setTimeout(function () { - e.emit("beep"); - }, 100); - - var errorTimeout = setTimeout(function () { - done(new Error("Wasn't caught")); - }, 500); - - d.on("error", function (theError) { - expect(theError).toBe(error); - clearTimeout(errorTimeout); - done(); - }); - - function callAsync() { - var def = Q.defer(); - e.once("beep", function () { - def.reject(error); - }); - return def.promise; - } - }); - }); - } -} - -describe("decorator functions", function () { - describe("promised", function () { - var exception = new Error("That is not the meaning of life."); - it("resolves promised arguments", function () { - var sum = Q.promised(function add(a, b) { - return a + b; - }); - return sum(Q.resolve(4), Q.resolve(5)).then(function (sum) { - expect(sum).toEqual(9); - }); - }); - it("resolves promised `this`", function () { - var inc = Q.promised(function inc(a) { - return this + a; - }); - return inc.call(Q.resolve(4), Q.resolve(5)).then(function (sum) { - expect(sum).toEqual(9); - }); - }); - it("is rejected if an argument is rejected", function () { - var sum = Q.promised(function add(a, b) { - return a + b; - }); - return sum(Q.reject(exception), Q.resolve(4)).then(function () { - expect(4).toEqual(42); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - it("is rejected if `this` is rejected", function () { - var inc = Q.promised(function inc(a) { - return this + a; - }); - return inc.call(Q.reject(exception), Q.resolve(4)).then(function () { - expect(4).toEqual(42); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - }); -}); - -describe("stack trace formatting", function () { - it("doesn't mangle a stack trace that gets handled twice", function () { - var d1 = Q.defer(); - var d2 = Q.defer(); - var captured = []; - d1.promise.done(); - d2.promise.done(); - - Q.onerror = function (err) { - captured.push(err.stack); - }; - - var error = new Error("boom!"); - d1.reject(error); - d2.reject(error); - - return Q.all([d1.promise.fail(function () {}), d2.promise.fail(function () { })]) - .then(function () { - expect(captured[0]).toEqual(captured[1]); - }); - }); -}); - -describe("possible regressions", function () { - - describe("gh-9", function () { - it("treats falsy values as resolved values without error", function () { - expect(Q.isPending(null)).toEqual(false); - expect(Q.isPending(void 0)).toEqual(false); - expect(Q.isPending(false)).toEqual(false); - expect(Q.isPending()).toEqual(false); - }); - }); - - describe("gh-22", function () { - it("ensures that the array prototype is intact", function () { - var keys = []; - for (var key in []) { - keys.push(key); - } - expect(keys.length).toBe(0); - }); - }); - - describe("gh-73", function () { - it("does not choke on non-error rejection reasons", function () { - Q.reject(REASON).done(); - - var deferred = Q.defer(); - - Q.onerror = function (error) { - expect(error).toBe(REASON); - deferred.resolve(); - }; - Q.delay(10).then(deferred.reject); - - return deferred.promise; - }); - }); - - describe("gh-90", function () { - it("does not choke on rejection reasons with an undefined `stack`", function () { - var error = new RangeError(REASON); - error.stack = undefined; - Q.reject(error).done(); - - var deferred = Q.defer(); - - Q.onerror = function (theError) { - expect(theError).toBe(error); - deferred.resolve(); - }; - Q.delay(10).then(deferred.reject); - - return deferred.promise; - }); - }); - - describe("gh-75", function () { - it("does not double-resolve misbehaved promises", function () { - var badPromise = Q.makePromise({ - post: function () { return "hello"; } - }); - - var resolutions = 0; - function onResolution() { - ++resolutions; - } - - return Q.when(badPromise, onResolution, onResolution).then(function () { - expect(resolutions).toBe(1); - }); - }); - }); - -}); - -describe("unhandled rejection reporting", function () { - beforeEach(function () { - Q.resetUnhandledRejections(); - }); - - it("doesn't report a resolve, then reject (gh-252)", function () { - var deferred = Q.defer(); - deferred.resolve(); - deferred.reject(); - - expect(Q.getUnhandledReasons().length).toEqual(0); - }); - - it("doesn't report when you chain off a rejection", function () { - return Q.reject("this will be handled").get("property").fail(function () { - // now it should be handled. - }).fin(function() { - expect(Q.getUnhandledReasons().length).toEqual(0); - }); - }); - - it("reports the most basic case", function () { - Q.reject("a reason"); - - expect(Q.getUnhandledReasons()).toEqual(["a reason"]); - }); - - it("doesn't let you mutate the internal array", function () { - Q.reject("a reason"); - - Q.getUnhandledReasons().length = 0; - expect(Q.getUnhandledReasons()).toEqual(["a reason"]); - }); - - it("resets after calling `Q.resetUnhandledRejections`", function () { - Q.reject("a reason"); - - Q.resetUnhandledRejections(); - expect(Q.getUnhandledReasons()).toEqual([]); - }); - - it("stops tracking after calling `Q.stopUnhandledRejectionTracking`", function () { - Q.reject("a reason"); - - Q.stopUnhandledRejectionTracking(); - - Q.reject("another reason"); - - expect(Q.getUnhandledReasons()).toEqual([]); - }); -}); diff --git a/packages/q/spec/queue-spec.js b/packages/q/spec/queue-spec.js deleted file mode 100644 index 4cec14e2..00000000 --- a/packages/q/spec/queue-spec.js +++ /dev/null @@ -1,180 +0,0 @@ - -var Q = require("../q"); -var Queue = require("../queue"); - -global.Q = Q; -require("./lib/jasmine-promise"); - -describe("queue", function () { - - it("should enqueue then dequeue", function () { - var queue = Queue(); - queue.put(1); - return queue.get().then(function (value) { - expect(value).toBe(1); - }); - }); - - it("should dequeue then enqueue", function () { - var queue = Queue(); - var promise = queue.get().then(function (value) { - expect(value).toBe(1); - }); - queue.put(1); - return promise; - }); - - it("should stream", function () { - var queue = Queue(); - - Q.try(function () { - return Q.delay(20).then(function () { - queue.put(1); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(2); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(3); - }) - }) - .done(); - - return Q.try(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(1); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(2); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(3); - }); - }) - - }); - - it("should be order agnostic", function () { - var queue = Queue(); - - Q.try(function () { - return Q.delay(20).then(function () { - queue.put(1); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(2); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(3); - }) - }) - .done(); - - return Q.all([ - queue.get() - .then(function (value) { - expect(value).toBe(1); - }), - queue.get() - .then(function (value) { - expect(value).toBe(2); - }), - queue.get() - .then(function (value) { - expect(value).toBe(3); - }) - ]); - }); - - it("should close", function () { - - var queue = Queue(); - - Q.try(function () { - return Q.delay(20).then(function () { - queue.put(1); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(2); - }) - }) - .then(function () { - queue.close(); - }) - .done(); - - return Q.try(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(1); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(2); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(false).toBe(true); // should not get here - }); - }) - .catch(function (error) { - expect(error.message).toBe("Can't get value from closed queue"); - return queue.get(); - }) - .catch(function (error) { - expect(error.message).toBe("Can't get value from closed queue"); - }) - .then(function () { - return queue.closed; - }) - .then(function (error) { - expect(error.message).toBe("Can't get value from closed queue"); - }) - }); - - it("should close with alternate error", function () { - - var queue = Queue(); - queue.close(new Error("Alternate reason")); - - return Q.try(function () { - return queue.get(); - }) - .catch(function (error) { - expect(error.message).toBe("Alternate reason"); - return queue.get(); - }) - .catch(function (error) { - expect(error.message).toBe("Alternate reason"); - }) - .then(function () { - return queue.closed; - }) - .then(function (error) { - expect(error.message).toBe("Alternate reason"); - }) - }); - -}); - diff --git a/require.js b/require.js index 1b67e2f8..3c07ce72 100644 --- a/require.js +++ b/require.js @@ -6,916 +6,923 @@ https://github.com/motorola-mobility/montage/blob/master/LICENSE.md */ -/*global bootstrap,define */ -(function (definition) { - - // Boostrapping Browser - if (typeof bootstrap !== "undefined") { - - // Window - if (typeof window !== "undefined") { - bootstrap("require", function (require, exports) { - var Promise = require("promise"); - var URL = require("mini-url"); - definition(exports, Promise, URL); - require("require/browser"); - }); - - // Worker - } else { - bootstrap("require", function (require, exports) { - var Promise = require("promise").Promise; - var URL = require("mini-url"); - definition(exports, Promise, URL); - }); +var Require = exports; +var Q = require("q"); +var URL = require("url"); + +if (!this) { + throw new Error("Require does not work in strict mode."); +} + +var globalEval = eval; // reassigning causes eval to not use lexical scope. + +// Non-CommonJS speced extensions should be marked with an "// EXTENSION" +// comment. + +Require.makeRequire = function (config) { + var require; + + // Configuration defaults: + config = config || {}; + config.location = URL.resolve(config.location || Require.getLocation(), "./"); + config.lib = URL.resolve(config.location, config.lib || "./"); + config.paths = config.paths || [config.lib]; + config.mappings = config.mappings || {}; // EXTENSION + config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; + config.makeLoader = config.makeLoader || Require.makeLoader; + config.load = config.load || config.makeLoader(config); + config.makeCompiler = config.makeCompiler || Require.makeCompiler; + config.compile = config.compile || config.makeCompiler(config); + config.parseDependencies = config.parseDependencies || Require.parseDependencies; + config.read = config.read || Require.read; + + // Modules: { exports, id, location, directory, factory, dependencies, + // dependees, text, type } + var modules = config.modules = config.modules || {}; + + // produces an entry in the module state table, which gets built + // up through loading and execution, ultimately serving as the + // ``module`` free variable inside the corresponding module. + function getModuleDescriptor(id) { + var lookupId = id.toLowerCase(); + if (!has(modules, lookupId)) { + modules[lookupId] = { + id: id, + display: (config.name || config.location) + "#" + id, // EXTENSION + require: require + }; } - - // Node Server - } else if (typeof process !== "undefined") { - // the parens trick the heuristic scanner for static dependencies, so - // they are not pre-loaded by the asynchronous browser loader - var Promise = (require)("q"); - var URL = (require)("url"); - definition(exports, Promise, URL); - (require)("./node"); - } else { - throw new Error("Can't support require on this platform"); + return modules[lookupId]; } -})(function (Require, Promise, URL) { - - if (!this) { - throw new Error("Require does not work in strict mode."); + // for preloading modules by their id and exports, useful to + // prevent wasteful multiple instantiation if a module was loaded + // in the bootstrapping process and can be trivially injected into + // the system. + function inject(id, exports) { + var module = getModuleDescriptor(id); + module.exports = exports; + module.location = URL.resolve(config.location, id); + module.directory = URL.resolve(module.location, "./"); + module.injected = true; + delete module.redirect; + delete module.mappingRedirect; } - var globalEval = eval; // reassigning causes eval to not use lexical scope. - - // Non-CommonJS speced extensions should be marked with an "// EXTENSION" - // comment. - - Require.makeRequire = function (config) { - var require; - - // Configuration defaults: - config = config || {}; - config.location = URL.resolve(config.location || Require.getLocation(), "./"); - config.lib = URL.resolve(config.location, config.lib || "./"); - config.paths = config.paths || [config.lib]; - config.mappings = config.mappings || {}; // EXTENSION - config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; - config.makeLoader = config.makeLoader || Require.makeLoader; - config.load = config.load || config.makeLoader(config); - config.makeCompiler = config.makeCompiler || Require.makeCompiler; - config.compile = config.compile || config.makeCompiler(config); - config.parseDependencies = config.parseDependencies || Require.parseDependencies; - config.read = config.read || Require.read; - - // Modules: { exports, id, location, directory, factory, dependencies, - // dependees, text, type } - var modules = config.modules = config.modules || {}; - - // produces an entry in the module state table, which gets built - // up through loading and execution, ultimately serving as the - // ``module`` free variable inside the corresponding module. - function getModuleDescriptor(id) { - var lookupId = id.toLowerCase(); - if (!has(modules, lookupId)) { - modules[lookupId] = { - id: id, - display: (config.name || config.location) + "#" + id, // EXTENSION - require: require - }; + // Ensures a module definition is loaded, compiled, analyzed + var load = memoize(function (topId, viaId) { + var module = getModuleDescriptor(topId); + return Q.fcall(function () { + // if not already loaded, already instantiated, or + // configured as a redirection to another module + if ( + module.factory === void 0 && + module.exports === void 0 && + module.redirect === void 0 + ) { + return Q.fcall(config.load, topId, module); + } + }) + .then(function () { + // compile and analyze dependencies + config.compile(module); + var dependencies = + module.dependencies = + module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); } - return modules[lookupId]; + }); + }); + + // Load a module definition, and the definitions of its transitive + // dependencies + function deepLoad(topId, viaId, loading) { + var module = getModuleDescriptor(topId); + // this is a memo of modules already being loaded so we don’t + // data-lock on a cycle of dependencies. + loading = loading || {}; + // has this all happened before? will it happen again? + if (has(loading, topId)) { + return; // break the cycle of violence. } - - // for preloading modules by their id and exports, useful to - // prevent wasteful multiple instantiation if a module was loaded - // in the bootstrapping process and can be trivially injected into - // the system. - function inject(id, exports) { - var module = getModuleDescriptor(id); - module.exports = exports; - module.location = URL.resolve(config.location, id); - module.directory = URL.resolve(module.location, "./"); - module.injected = true; - delete module.redirect; - delete module.mappingRedirect; - } - - // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { - var module = getModuleDescriptor(topId); - return Promise.fcall(function () { - // if not already loaded, already instantiated, or - // configured as a redirection to another module - if ( - module.factory === void 0 && - module.exports === void 0 && - module.redirect === void 0 - ) { - return Promise.fcall(config.load, topId, module); - } - }) - .then(function () { - // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } - }); + loading[topId] = true; // this has happened before + return load(topId, viaId) + .then(function () { + // load the transitive dependencies using the magic of + // recursion. + return Q.all(module.dependencies.map(function (depId) { + depId = resolve(depId, topId); + // create dependees set, purely for debug purposes + var module = getModuleDescriptor(depId); + var dependees = module.dependees = module.dependees || {}; + dependees[topId] = true; + return deepLoad(depId, topId, loading); + })); + }, function (error) { + module.error = error; }); + } - // Load a module definition, and the definitions of its transitive - // dependencies - function deepLoad(topId, viaId, loading) { - var module = getModuleDescriptor(topId); - // this is a memo of modules already being loaded so we don’t - // data-lock on a cycle of dependencies. - loading = loading || {}; - // has this all happened before? will it happen again? - if (has(loading, topId)) { - return; // break the cycle of violence. - } - loading[topId] = true; // this has happened before - return load(topId, viaId) - .then(function () { - // load the transitive dependencies using the magic of - // recursion. - return Promise.all(module.dependencies.map(function (depId) { - depId = resolve(depId, topId); - // create dependees set, purely for debug purposes - var module = getModuleDescriptor(depId); - var dependees = module.dependees = module.dependees || {}; - dependees[topId] = true; - return deepLoad(depId, topId, loading); - })); - }, function (error) { - module.error = error; - }); + function lookup(topId, viaId) { + topId = resolve(topId, viaId); + var module = getModuleDescriptor(topId); + + // check for consistent case convention + if (module.id !== topId) { + throw new Error( + "Can't require module " + JSON.stringify(module.id) + + " by alternate spelling " + JSON.stringify(topId) + ); } - // Initializes a module by executing the factory function with a new - // module "exports" object. - function getExports(topId, viaId) { - var module = getModuleDescriptor(topId); + // handle redirects + if (module.redirect !== void 0) { + return lookup(module.redirect, topId); + } - // check for consistent case convention - if (module.id !== topId) { - throw new Error( - "Can't require module " + JSON.stringify(module.id) + - " by alternate spelling " + JSON.stringify(topId) - ); - } + // handle cross-package linkage + if (module.mappingRedirect !== void 0) { + return module.mappingRequire.lookup(module.mappingRedirect, ""); + } - // check for load error - if (module.error) { - var error = new Error( - "Can't require module " + JSON.stringify(module.id) + - " via " + JSON.stringify(viaId) + - " because " + module.error.message - ); - error.cause = module.error; - throw error; - } + return module; + } - // handle redirects - if (module.redirect !== void 0) { - return getExports(module.redirect, viaId); - } + // Initializes a module by executing the factory function with a new + // module "exports" object. + function getExports(topId, viaId) { + var module = getModuleDescriptor(topId); - // handle cross-package linkage - if (module.mappingRedirect !== void 0) { - return module.mappingRequire(module.mappingRedirect, viaId); - } + // check for consistent case convention + if (module.id !== topId) { + throw new Error( + "Can't require module " + JSON.stringify(module.id) + + " by alternate spelling " + JSON.stringify(topId) + ); + } - // do not reinitialize modules - if (module.exports !== void 0) { - return module.exports; - } + // check for load error + if (module.error) { + var error = module.error; + error.message = ( + "Can't require module " + JSON.stringify(module.id) + + " via " + JSON.stringify(viaId) + + " because " + error.message + ); + throw error; + } - // do not initialize modules that do not define a factory function - if (module.factory === void 0) { - throw new Error( - "Can't require module " + JSON.stringify(topId) + - " via " + JSON.stringify(viaId) - ); - } + // handle redirects + if (module.redirect !== void 0) { + return getExports(module.redirect, viaId); + } + + // handle cross-package linkage + if (module.mappingRedirect !== void 0) { + return module.mappingRequire(module.mappingRedirect, viaId); + } - module.directory = URL.resolve(module.location, "./"); // EXTENSION - module.exports = {}; + // do not reinitialize modules + if (module.exports !== void 0) { + return module.exports; + } - // Execute the factory function: - var returnValue = module.factory.call( - // in the context of the module: - void 0, // this (defaults to global) - makeRequire(topId), // require - module.exports, // exports - module // module + // do not initialize modules that do not define a factory function + if (module.factory === void 0) { + throw new Error( + "Can't require module " + JSON.stringify(topId) + + " via " + JSON.stringify(viaId) + " " + JSON.stringify(module) ); + } - // EXTENSION - if (returnValue !== void 0) { - module.exports = returnValue; - } + module.directory = URL.resolve(module.location, "./"); // EXTENSION + module.exports = {}; - return module.exports; + // Execute the factory function: + var returnValue = module.factory.call( + // in the context of the module: + void 0, // this (defaults to global) + makeRequire(topId), // require + module.exports, // exports + module // module + ); + + // EXTENSION + if (returnValue !== void 0) { + module.exports = returnValue; } - // Finds the internal identifier for a module in a subpackage - // The `seen` object is a memo of the packages we have seen to avoid - // infinite recursion of cyclic package dependencies. It also causes - // the function to return null instead of throwing an exception. I’m - // guessing that throwing exceptions *and* being recursive would be - // too much performance evil for one function. - function identify(id2, require2, seen) { - var location = config.location; - if (require2.location === location) { - return id2; - } + return module.exports; + } - var internal = !!seen; - seen = seen || {}; - if (has(seen, location)) { - return null; // break the cycle of violence. - } - seen[location] = true; - /*jshint -W089 */ - for (var name in config.mappings) { - var mapping = config.mappings[name]; - location = mapping.location; - if (!config.hasPackage(location)) { - continue; - } - var candidate = config.getPackage(location); - var id1 = candidate.identify(id2, require2, seen); - if (id1 === null) { - continue; - } else if (id1 === "") { - return name; - } else { - return name + "/" + id1; - } - } - if (internal) { - return null; + // Finds the internal identifier for a module in a subpackage + // The `seen` object is a memo of the packages we have seen to avoid + // infinite recursion of cyclic package dependencies. It also causes + // the function to return null instead of throwing an exception. I’m + // guessing that throwing exceptions *and* being recursive would be + // too much performance evil for one function. + function identify(id2, require2, seen) { + var location = config.location; + if (require2.location === location) { + return id2; + } + + var internal = !!seen; + seen = seen || {}; + if (has(seen, location)) { + return null; // break the cycle of violence. + } + seen[location] = true; + /*jshint -W089 */ + for (var name in config.mappings) { + var mapping = config.mappings[name]; + location = mapping.location; + if (!config.hasPackage(location)) { + continue; + } + var candidate = config.getPackage(location); + var id1 = candidate.identify(id2, require2, seen); + if (id1 === null) { + continue; + } else if (id1 === "") { + return name; } else { - throw new Error( - "Can't identify " + id2 + " from " + require2.location - ); + return name + "/" + id1; } - /*jshint +W089 */ } + if (internal) { + return null; + } else { + throw new Error( + "Can't identify " + id2 + " from " + require2.location + ); + } + /*jshint +W089 */ + } - // Creates a unique require function for each module that encapsulates - // that module's id for resolving relative module IDs against. - function makeRequire(viaId) { - - // Main synchronously executing "require()" function - var require = function(id) { - var topId = resolve(id, viaId); - return getExports(topId, viaId); - }; + // Creates a unique require function for each module that encapsulates + // that module's id for resolving relative module IDs against. + function makeRequire(viaId) { - // Asynchronous "require.async()" which ensures async executation - // (even with synchronous loaders) - require.async = function(id) { - var topId = resolve(id, viaId); - var module = getModuleDescriptor(id); - return deepLoad(topId, viaId) - .then(function () { - return require(topId); - }); - }; + // Main synchronously executing "require()" function + var require = function(id) { + var topId = resolve(id, viaId); + return getExports(topId, viaId); + }; - require.resolve = function (id) { - return normalizeId(resolve(id, viaId)); - }; + // Asynchronous "require.async()" which ensures async executation + // (even with synchronous loaders) + require.async = function(id) { + var topId = resolve(id, viaId); + var module = getModuleDescriptor(id); + return deepLoad(topId, viaId) + .then(function () { + return require(topId); + }); + }; - require.getModule = getModuleDescriptor; // XXX deprecated, use: - require.getModuleDescriptor = getModuleDescriptor; - require.load = load; - require.deepLoad = deepLoad; + require.resolve = function (id) { + return normalizeId(resolve(id, viaId)); + }; - require.loadPackage = function (dependency, givenConfig) { - if (givenConfig) { // explicit configuration, fresh environment - return Require.loadPackage(dependency, givenConfig); - } else { // inherited environment - return config.loadPackage(dependency, config); - } - }; + require.getModule = getModuleDescriptor; // XXX deprecated, use: + require.getModuleDescriptor = getModuleDescriptor; + require.lookup = lookup; + require.load = load; + require.deepLoad = deepLoad; - require.hasPackage = function (dependency) { - return config.hasPackage(dependency); - }; + require.loadPackage = function (dependency, givenConfig) { + if (givenConfig) { // explicit configuration, fresh environment + return Require.loadPackage(dependency, givenConfig); + } else { // inherited environment + return config.loadPackage(dependency, config); + } + }; - require.getPackage = function (dependency) { - return config.getPackage(dependency); - }; + require.hasPackage = function (dependency) { + return config.hasPackage(dependency); + }; - require.isMainPackage = function () { - return require.location === config.mainPackageLocation; - }; + require.getPackage = function (dependency) { + return config.getPackage(dependency); + }; - require.injectPackageDescription = function (location, description) { - Require.injectPackageDescription(location, description, config); - }; + require.isMainPackage = function () { + return require.location === config.mainPackageLocation; + }; - require.injectPackageDescriptionLocation = function (location, descriptionLocation) { - Require.injectPackageDescriptionLocation(location, descriptionLocation, config); - }; + require.injectPackageDescription = function (location, description) { + Require.injectPackageDescription(location, description, config); + }; - require.injectMapping = function (dependency, name) { - dependency = normalizeDependency(dependency, config, name); - name = name || dependency.name; - config.mappings[name] = dependency; - }; + require.injectPackageDescriptionLocation = function (location, descriptionLocation) { + Require.injectPackageDescriptionLocation(location, descriptionLocation, config); + }; - require.injectDependency = function (name) { - require.injectMapping({name: name}, name); - }; + require.injectMapping = function (dependency, name) { + dependency = normalizeDependency(dependency, config, name); + name = name || dependency.name; + config.mappings[name] = dependency; + }; - require.identify = identify; - require.inject = inject; + require.injectDependency = function (name) { + require.injectMapping({name: name}, name); + }; - config.exposedConfigs.forEach(function(name) { - require[name] = config[name]; - }); + require.identify = identify; + require.inject = inject; - require.config = config; + config.exposedConfigs.forEach(function(name) { + require[name] = config[name]; + }); - require.read = config.read; + require.config = config; - return require; - } + require.read = config.read; - require = makeRequire(""); return require; - }; - - Require.injectPackageDescription = function (location, description, config) { - var descriptions = - config.descriptions = - config.descriptions || {}; - descriptions[location] = Promise.resolve(description); - }; + } - Require.injectPackageDescriptionLocation = function (location, descriptionLocation, config) { + require = makeRequire(""); + return require; +}; + +Require.injectPackageDescription = function (location, description, config) { + var descriptions = + config.descriptions = + config.descriptions || {}; + descriptions[location] = Q.resolve(description); +}; + +Require.injectPackageDescriptionLocation = function (location, descriptionLocation, config) { + var descriptionLocations = + config.descriptionLocations = + config.descriptionLocations || {}; + descriptionLocations[location] = descriptionLocation; +}; + +Require.loadPackageDescription = function (dependency, config) { + var location = dependency.location; + var descriptions = + config.descriptions = + config.descriptions || {}; + if (descriptions[location] === void 0) { var descriptionLocations = config.descriptionLocations = config.descriptionLocations || {}; - descriptionLocations[location] = descriptionLocation; - }; - - Require.loadPackageDescription = function (dependency, config) { - var location = dependency.location; - var descriptions = - config.descriptions = - config.descriptions || {}; - if (descriptions[location] === void 0) { - var descriptionLocations = - config.descriptionLocations = - config.descriptionLocations || {}; - var descriptionLocation; - if (descriptionLocations[location]) { - descriptionLocation = descriptionLocations[location]; - } else { - descriptionLocation = URL.resolve(location, "package.json"); - } - descriptions[location] = (config.read || Require.read)(descriptionLocation) - .then(function (json) { - try { - return JSON.parse(json); - } catch (error) { - error.message = error.message + " in " + JSON.stringify(descriptionLocation); - throw error; - } - }); + var descriptionLocation; + if (descriptionLocations[location]) { + descriptionLocation = descriptionLocations[location]; + } else { + descriptionLocation = URL.resolve(location, "package.json"); } - return descriptions[location]; - }; + descriptions[location] = (config.read || Require.read)(descriptionLocation) + .then(function (json) { + try { + return JSON.parse(json); + } catch (error) { + error.message = error.message + " in " + JSON.stringify(descriptionLocation); + throw error; + } + }); + } + return descriptions[location]; +}; - Require.loadPackage = function (dependency, config) { +Require.loadPackage = function (dependency, config) { + dependency = normalizeDependency(dependency, config); + if (!dependency.location) { + throw new Error("Can't find dependency: " + JSON.stringify(dependency)); + } + var location = dependency.location; + config = Object.create(config || null); + var loadingPackages = config.loadingPackages = config.loadingPackages || {}; + var loadedPackages = config.packages = {}; + var registry = config.registry = config.registry || Object.create(null); + config.mainPackageLocation = location; + + config.hasPackage = function (dependency) { dependency = normalizeDependency(dependency, config); if (!dependency.location) { - throw new Error("Can't find dependency: " + JSON.stringify(dependency)); + return false; } var location = dependency.location; - config = Object.create(config || null); - var loadingPackages = config.loadingPackages = config.loadingPackages || {}; - var loadedPackages = config.packages = {}; - var registry = config.registry = config.registry || Object.create(null); - config.mainPackageLocation = location; - - config.hasPackage = function (dependency) { - dependency = normalizeDependency(dependency, config); - if (!dependency.location) { - return false; - } - var location = dependency.location; - return !!loadedPackages[location]; - }; - - config.getPackage = function (dependency) { - dependency = normalizeDependency(dependency, config); - if (!dependency.location) { - throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); - } - var location = dependency.location; - if (!loadedPackages[location]) { - if (loadingPackages[location]) { - throw new Error( - "Dependency has not finished loading: " + JSON.stringify(dependency) - ); - } else { - throw new Error( - "Dependency was not loaded: " + JSON.stringify(dependency) - ); - } - } - return loadedPackages[location]; - }; - - config.loadPackage = function (dependency, viaConfig) { - dependency = normalizeDependency(dependency, viaConfig); - if (!dependency.location) { - throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); - } - var location = dependency.location; - if (!loadingPackages[location]) { - loadingPackages[location] = Require.loadPackageDescription(dependency, config) - .then(function (packageDescription) { - var subconfig = configurePackage( - location, - packageDescription, - config - ); - var pkg = Require.makeRequire(subconfig); - loadedPackages[location] = pkg; - return pkg; - }); - } - return loadingPackages[location]; - }; - - var pkg = config.loadPackage(dependency); - pkg.location = location; - pkg.async = function (id, callback) { - return pkg.then(function (require) { - return require.async(id, callback); - }); - }; - - return pkg; + return !!loadedPackages[location]; }; - function normalizeDependency(dependency, config, name) { - config = config || {}; - if (typeof dependency === "string") { - dependency = { - location: dependency - }; - } - if (dependency.main) { - dependency.location = config.mainPackageLocation; - } - // if the named dependency has already been found at another - // location, refer to the same eventual instance - if ( - dependency.name && - config.registry && - config.registry[dependency.name] - ) { - dependency.location = config.registry[dependency.name]; - } - // default location - if (!dependency.location && config.packagesDirectory && dependency.name) { - dependency.location = URL.resolve( - config.packagesDirectory, - dependency.name + "/" - ); - } + config.getPackage = function (dependency) { + dependency = normalizeDependency(dependency, config); if (!dependency.location) { - return dependency; // partially completed + throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } - // make sure the dependency location has a trailing slash so that - // relative urls will resolve properly - if (!/\/$/.test(dependency.location)) { - dependency.location += "/"; - } - // resolve the location relative to the current package - if (!Require.isAbsolute(dependency.location)) { - if (!config.location) { + var location = dependency.location; + if (!loadedPackages[location]) { + if (loadingPackages[location]) { throw new Error( - "Dependency locations must be fully qualified: " + - JSON.stringify(dependency) + "Dependency has not finished loading: " + JSON.stringify(dependency) + ); + } else { + throw new Error( + "Dependency was not loaded: " + JSON.stringify(dependency) ); } - dependency.location = URL.resolve( - config.location, - dependency.location - ); - } - // register the package name so the location can be reused - if (dependency.name) { - config.registry[dependency.name] = dependency.location; } - return dependency; - } - - function configurePackage(location, description, parent) { + return loadedPackages[location]; + }; - if (!/\/$/.test(location)) { - location += "/"; + config.loadPackage = function (dependency, viaConfig) { + dependency = normalizeDependency(dependency, viaConfig); + if (!dependency.location) { + throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } - - var config = Object.create(parent); - config.name = description.name; - config.location = location || Require.getLocation(); - config.packageDescription = description; - config.useScriptInjection = description.useScriptInjection; - - if (description.production !== void 0) { - config.production = description.production; + var location = dependency.location; + if (!loadingPackages[location]) { + loadingPackages[location] = Require.loadPackageDescription(dependency, config) + .then(function (packageDescription) { + var subconfig = configurePackage( + location, + packageDescription, + config + ); + var pkg = Require.makeRequire(subconfig); + loadedPackages[location] = pkg; + return pkg; + }); + loadingPackages[location].done(); } + return loadingPackages[location]; + }; - // explicitly mask definitions and modules, which must - // not apply to child packages - var modules = config.modules = config.modules || {}; + var pkg = config.loadPackage(dependency); + pkg.location = location; + pkg.async = function (id, callback) { + return pkg.then(function (require) { + return require.async(id, callback); + }); + }; + + return pkg; +}; - var registry = config.registry; - if (config.name !== void 0 && !registry[config.name]) { - registry[config.name] = config.location; +function normalizeDependency(dependency, config, name) { + config = config || {}; + if (typeof dependency === "string") { + dependency = { + location: dependency + }; + } + if (dependency.main) { + dependency.location = config.mainPackageLocation; + } + // if the named dependency has already been found at another + // location, refer to the same eventual instance + if ( + dependency.name && + config.registry && + config.registry[dependency.name] + ) { + dependency.location = config.registry[dependency.name]; + } + // default location + if (!dependency.location && config.packagesDirectory && dependency.name) { + dependency.location = URL.resolve( + config.packagesDirectory, + dependency.name + "/" + ); + } + if (!dependency.location) { + return dependency; // partially completed + } + // make sure the dependency location has a trailing slash so that + // relative urls will resolve properly + if (!/\/$/.test(dependency.location)) { + dependency.location += "/"; + } + // resolve the location relative to the current package + if (!Require.isAbsolute(dependency.location)) { + if (!config.location) { + throw new Error( + "Dependency locations must be fully qualified: " + + JSON.stringify(dependency) + ); } + dependency.location = URL.resolve( + config.location, + dependency.location + ); + } + // register the package name so the location can be reused + if (dependency.name) { + config.registry[dependency.name] = dependency.location; + } + return dependency; +} - // overlay - var overlay = description.overlay || {}; - var layer; - (config.overlays || Require.overlays).forEach(function (engine) { - /*jshint -W089 */ - if (overlay[engine]) { - var layer = overlay[engine]; - for (var name in layer) { - description[name] = layer[name]; - } - } - /*jshint +W089 */ - }); - delete description.overlay; - - // directories - description.directories = description.directories || {}; - description.directories.lib = - description.directories.lib === void 0 ? "./" : description.directories.lib; - var lib = description.directories.lib; - // lib - config.lib = URL.resolve(location, "./" + lib); - var packagesDirectory = description.directories.packages || "node_modules"; - packagesDirectory = URL.resolve(location, packagesDirectory + "/"); - config.packagesDirectory = packagesDirectory; - - // The default "main" module of a package has the same name as the - // package. - if (description.main !== void 0) { - - // main, injects a definition for the main module, with - // only its path. makeRequire goes through special effort - // in deepLoad to re-initialize this definition with the - // loaded definition from the given path. - modules[""] = { - id: "", - redirect: normalizeId(description.main), - location: config.location - }; +function configurePackage(location, description, parent) { - if (description.name !== modules[""].redirect) { - modules[description.name] = { - id: description.name, - redirect: "", - location: URL.resolve(location, description.name) - }; - } + if (!/\/$/.test(location)) { + location += "/"; + } - } + var config = Object.create(parent); + config.name = description.name; + config.location = location || Require.getLocation(); + config.packageDescription = description; + config.useScriptInjection = description.useScriptInjection; - //Deal with redirects - var redirects = description.redirects; - if (redirects !== void 0) { - Object.keys(redirects).forEach(function (name) { - modules[name] = { - id: name, - redirect: redirects[name], - location: URL.resolve(location, name) - }; - }); - } + if (description.production !== void 0) { + config.production = description.production; + } - // mappings, link this package to other packages. - var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production ? description.devDependencies : null] - .forEach(function (dependencies) { - if (!dependencies) { - return; - } - Object.keys(dependencies).forEach(function (name) { - if (!mappings[name]) { - // dependencies are equivalent to name and version mappings, - // though the version predicate string is presently ignored - // (TODO) - mappings[name] = { - name: name, - version: dependencies[name] - }; - } - }); - }); - // mappings - Object.keys(mappings).forEach(function (name) { - var mapping = mappings[name] = normalizeDependency( - mappings[name], - config, - name - ); - }); - config.mappings = mappings; + // explicitly mask definitions and modules, which must + // not apply to child packages + var modules = config.modules = config.modules || {}; - return config; + var registry = config.registry; + if (config.name !== void 0 && !registry[config.name]) { + registry[config.name] = config.location; } - // Helper functions: + // overlay + var overlay = description.overlay || {}; - function has(object, property) { - return Object.prototype.hasOwnProperty.call(object, property); + // but first, convert "browser" field, as pioneered by Browserify, to an + // overlay + if (typeof description.browser === "string") { + overlay.browser = { + redirects: {"": description.browser} + }; + } else if (typeof description.browser === "object") { + overlay.browser = { + redirects: description.browser + }; } - // Resolves CommonJS module IDs (not paths) - Require.resolve = resolve; - function resolve(id, baseId) { - id = String(id); - var source = id.split("/"); - var target = []; - if (source.length && source[0] === "." || source[0] === "..") { - var parts = baseId.split("/"); - parts.pop(); - source.unshift.apply(source, parts); - } - for (var i = 0, ii = source.length; i < ii; i++) { - /*jshint -W035 */ - var part = source[i]; - if (part === "" || part === ".") { - } else if (part === "..") { - if (target.length) { - target.pop(); - } - } else { - target.push(part); + // overlay continued... + var layer; + (config.overlays || Require.overlays).forEach(function (engine) { + /*jshint -W089 */ + if (overlay[engine]) { + var layer = overlay[engine]; + for (var name in layer) { + description[name] = layer[name]; } - /*jshint +W035 */ } - return target.join("/"); - } + /*jshint +W089 */ + }); + delete description.overlay; + + // directories + description.directories = description.directories || {}; + description.directories.lib = + description.directories.lib === void 0 ? "./" : description.directories.lib; + var lib = description.directories.lib; + // lib + config.lib = URL.resolve(location, "./" + lib); + var packagesDirectory = description.directories.packages || "node_modules"; + packagesDirectory = URL.resolve(location, packagesDirectory + "/"); + config.packagesDirectory = packagesDirectory; + + // The default "main" module of a package has the same name as the + // package. + if (description.main !== void 0) { + + // main, injects a definition for the main module, with + // only its path. makeRequire goes through special effort + // in deepLoad to re-initialize this definition with the + // loaded definition from the given path. + modules[""] = { + id: "", + redirect: normalizeId(description.main), + location: config.location + }; - Require.base = function (location) { - // matches Unix basename - return String(location) - .replace(/(.+?)\/+$/, "$1") - .match(/([^\/]+$|^\/$|^$)/)[1]; - }; + if (description.name !== modules[""].redirect) { + modules[description.name] = { + id: description.name, + redirect: "", + location: URL.resolve(location, description.name || "") + }; + } - // Tests whether the location or URL is a absolute. - Require.isAbsolute = function(location) { - return (/^[\w\-]+:/).test(location); - }; + } - // Extracts dependencies by parsing code and looking for "require" (currently using a simple regexp) - Require.parseDependencies = function(factory) { - var o = {}; - String(factory).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { - o[id] = true; + //Deal with redirects + var redirects = description.redirects; + if (redirects !== void 0) { + Object.keys(redirects).forEach(function (name) { + modules[name] = { + id: name, + redirect: redirects[name], + location: URL.resolve(location, name) + }; }); - return Object.keys(o); - }; - - // Built-in compiler/preprocessor "middleware": + } - Require.DependenciesCompiler = function(config, compile) { - return function(module) { - if (!module.dependencies && module.text !== void 0) { - module.dependencies = config.parseDependencies(module.text); + // mappings, link this package to other packages. + var mappings = description.mappings || {}; + // dependencies, devDependencies if not in production + [description.dependencies, !config.production? description.devDependencies : null] + .forEach(function (dependencies) { + if (!dependencies) { + return; + } + Object.keys(dependencies).forEach(function (name) { + if (!mappings[name]) { + // dependencies are equivalent to name and version mappings, + // though the version predicate string is presently ignored + // (TODO) + mappings[name] = { + name: name, + version: dependencies[name] + }; } - compile(module); - if (module && !module.dependencies) { - if (module.text || module.factory) { - module.dependencies = Require.parseDependencies(module.text || module.factory); - } else { - module.dependencies = []; - } + }); + }); + // mappings + Object.keys(mappings).forEach(function (name) { + var mapping = mappings[name] = normalizeDependency( + mappings[name], + config, + name + ); + }); + config.mappings = mappings; + + return config; +} + +// Helper functions: + +function has(object, property) { + return Object.prototype.hasOwnProperty.call(object, property); +} + +// Resolves CommonJS module IDs (not paths) +Require.resolve = resolve; +function resolve(id, baseId) { + id = String(id); + var source = id.split("/"); + var target = []; + if (source.length && source[0] === "." || source[0] === "..") { + var parts = baseId.split("/"); + parts.pop(); + source.unshift.apply(source, parts); + } + for (var i = 0, ii = source.length; i < ii; i++) { + /*jshint -W035 */ + var part = source[i]; + if (part === "" || part === ".") { + } else if (part === "..") { + if (target.length) { + target.pop(); } - return module; - }; - }; - - // Support she-bang for shell scripts by commenting it out (it is never - // valid JavaScript syntax anyway) - Require.ShebangCompiler = function(config, compile) { - return function (module) { - if (module.text) { - module.text = module.text.replace(/^#!/, "//#!"); + } else { + target.push(part); + } + /*jshint +W035 */ + } + return target.join("/"); +} + +Require.base = function (location) { + // matches Unix basename + return String(location) + .replace(/(.+?)\/+$/, "$1") + .match(/([^\/]+$|^\/$|^$)/)[1]; +}; + +// Tests whether the location or URL is a absolute. +Require.isAbsolute = function(location) { + return (/^[\w\-]+:/).test(location); +}; + +// Extracts dependencies by parsing code and looking for "require" (currently using a simple regexp) +Require.parseDependencies = function(factory) { + var o = {}; + String(factory).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { + o[id] = true; + }); + return Object.keys(o); +}; + +// Built-in compiler/preprocessor "middleware": + +Require.DependenciesCompiler = function(config, compile) { + return function(module) { + if (!module.dependencies && module.text !== void 0) { + module.dependencies = config.parseDependencies(module.text); + } + compile(module); + if (module && !module.dependencies) { + if (module.text || module.factory) { + module.dependencies = Require.parseDependencies(module.text || module.factory); + } else { + module.dependencies = []; } - compile(module); - }; + } + return module; + }; +}; + +// Support she-bang for shell scripts by commenting it out (it is never +// valid JavaScript syntax anyway) +Require.ShebangCompiler = function(config, compile) { + return function (module) { + if (module.text) { + module.text = module.text.replace(/^#!/, "//#!"); + } + compile(module); }; +}; - Require.LintCompiler = function(config, compile) { - return function(module) { - try { - compile(module); - } catch (error) { - if (config.lint) { - Promise.nextTick(function () { - config.lint(module); - }); - } - throw error; +Require.LintCompiler = function(config, compile) { + return function(module) { + try { + compile(module); + } catch (error) { + if (config.lint) { + // TODO: use ASAP + Q.nextTick(function () { + config.lint(module); + }); } - }; + throw error; + } }; - - Require.exposedConfigs = [ - "paths", - "mappings", - "location", - "packageDescription", - "packages", - "modules" - ]; - - Require.makeCompiler = function(config) { - return Require.JsonCompiler( +}; + +Require.exposedConfigs = [ + "paths", + "mappings", + "location", + "packageDescription", + "packages", + "modules" +]; + +Require.makeCompiler = function(config) { + return Require.JsonCompiler( + config, + Require.ShebangCompiler( config, - Require.ShebangCompiler( + Require.DependenciesCompiler( config, - Require.DependenciesCompiler( + Require.LintCompiler( config, - Require.LintCompiler( - config, - Require.Compiler(config) - ) + Require.Compiler(config) ) ) - ); - }; - - Require.JsonCompiler = function (config, compile) { - return function (module) { - var json = (module.location || "").match(/\.json$/); - if (json) { - module.exports = JSON.parse(module.text); - return module; - } else { - return compile(module); - } - }; + ) + ); +}; + +Require.JsonCompiler = function (config, compile) { + return function (module) { + var json = (module.location || "").match(/\.json$/); + if (json) { + module.exports = JSON.parse(module.text); + return module; + } else { + return compile(module); + } }; +}; - // Built-in loader "middleware": +// Built-in loader "middleware": - // Using mappings hash to load modules that match a mapping. - Require.MappingsLoader = function(config, load) { - config.mappings = config.mappings || {}; - config.name = config.name; +// Using mappings hash to load modules that match a mapping. +Require.MappingsLoader = function(config, load) { + config.mappings = config.mappings || {}; + config.name = config.name; - // finds a mapping to follow, if any - return function (id, module) { - var mappings = config.mappings; - var prefixes = Object.keys(mappings); - var length = prefixes.length; + // finds a mapping to follow, if any + return function (id, module) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + var length = prefixes.length; - if (Require.isAbsolute(id)) { - return load(id, module); - } - // TODO: remove this when all code has been migrated off of the autonomous name-space problem + if (Require.isAbsolute(id)) { + return load(id, module); + } + // TODO: remove this when all code has been migrated off of the autonomous name-space problem + if ( + config.name !== void 0 && + id.indexOf(config.name) === 0 && + id.charAt(config.name.length) === "/" + ) { + console.warn("Package reflexive module ignored:", id); + } + var i, prefix; + for (i = 0; i < length; i++) { + prefix = prefixes[i]; if ( - config.name !== void 0 && - id.indexOf(config.name) === 0 && - id.charAt(config.name.length) === "/" + id === prefix || + id.indexOf(prefix) === 0 && + id.charAt(prefix.length) === "/" ) { - console.warn("Package reflexive module ignored:", id); - } - var i, prefix; - for (i = 0; i < length; i++) { - prefix = prefixes[i]; - if ( - id === prefix || - id.indexOf(prefix) === 0 && - id.charAt(prefix.length) === "/" - ) { - /*jshint -W083 */ - var mapping = mappings[prefix]; - var rest = id.slice(prefix.length + 1); - return config.loadPackage(mapping, config) - .then(function (mappingRequire) { - /*jshint +W083 */ - module.mappingRedirect = rest; - module.mappingRequire = mappingRequire; - return mappingRequire.deepLoad(rest, config.location); - }); - } + /*jshint -W083 */ + var mapping = mappings[prefix]; + var rest = id.slice(prefix.length + 1); + return config.loadPackage(mapping, config) + .then(function (mappingRequire) { + /*jshint +W083 */ + module.mappingRedirect = rest; + module.mappingRequire = mappingRequire; + return mappingRequire.deepLoad(rest, config.location); + }); } - return load(id, module); - }; + } + return load(id, module); }; +}; - Require.ExtensionsLoader = function(config, load) { - var extensions = config.extensions || ["js"]; - var loadWithExtension = extensions.reduceRight(function (next, extension) { - return function (id, module) { - return load(id + "." + extension, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " with extensions " + - JSON.stringify(extensions) + " in package at " + - JSON.stringify(config.location) - ); - }); +Require.ExtensionsLoader = function(config, load) { + var extensions = config.extensions || ["js"]; + var loadWithExtension = extensions.reduceRight(function (next, extension) { return function (id, module) { - if (Require.base(id).indexOf(".") !== -1) { - // already has an extension - return load(id, module); - } else { - return loadWithExtension(id, module); - } + return load(id + "." + extension, module) + .fail(function (error) { + if (/^Can't find /.test(error.message)) { + return next(id, module); + } else { + throw error; + } + }); }; + }, function (id, module) { + throw new Error( + "Can't find " + JSON.stringify(id) + " with extensions " + + JSON.stringify(extensions) + " in package at " + + JSON.stringify(config.location) + ); + }); + return function (id, module) { + if (Require.base(id).indexOf(".") !== -1) { + // already has an extension + return load(id, module); + } else { + return loadWithExtension(id, module); + } }; +}; - // Attempts to load using multiple base paths (or one absolute path) with a - // single loader. - Require.PathsLoader = function(config, load) { - var loadFromPaths = config.paths.reduceRight(function (next, path) { - return function (id, module) { - var newId = URL.resolve(path, id); - return load(newId, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " from paths " + - JSON.stringify(config.paths) + " in package at " + - JSON.stringify(config.location) - ); - }); - return function(id, module) { - if (Require.isAbsolute(id)) { - // already fully qualified - return load(id, module); - } else { - return loadFromPaths(id, module); - } +// Attempts to load using multiple base paths (or one absolute path) with a +// single loader. +Require.PathsLoader = function(config, load) { + var loadFromPaths = config.paths.reduceRight(function (next, path) { + return function (id, module) { + var newId = URL.resolve(path, id); + return load(newId, module) + .fail(function (error) { + if (/^Can't find /.test(error.message)) { + return next(id, module); + } else { + throw error; + } + }); }; + }, function (id, module) { + throw new Error( + "Can't find " + JSON.stringify(id) + " from paths " + + JSON.stringify(config.paths) + " in package at " + + JSON.stringify(config.location) + ); + }); + return function(id, module) { + if (Require.isAbsolute(id)) { + // already fully qualified + return load(id, module); + } else { + return loadFromPaths(id, module); + } }; +}; - Require.MemoizedLoader = function (config, load) { - var cache = config.cache = config.cache || {}; - return memoize(load, cache); - }; +Require.MemoizedLoader = function (config, load) { + var cache = config.cache = config.cache || {}; + return memoize(load, cache); +}; - var normalizeId = function (id) { - var match = /^(.*)\.js$/.exec(id); - if (match) { - id = match[1]; +var normalizeId = function (id) { + var match = /^(.*)\.js$/.exec(id); + if (match) { + id = match[1]; + } + return id; +}; + +var memoize = function (callback, cache) { + cache = cache || {}; + return function (key, arg) { + if (!has(cache, key)) { + cache[key] = Q.fcall(callback, key, arg); } - return id; - }; - - var memoize = function (callback, cache) { - cache = cache || {}; - return function (key, arg) { - if (!has(cache, key)) { - cache[key] = Promise.fcall(callback, key, arg); - } - return cache[key]; - }; + return cache[key]; }; +}; -}); diff --git a/spec/node-spec.js b/spec/node-spec.js index 4b172209..33337697 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -1,12 +1,17 @@ -require("./lib/jasmine-promise"); -var PATH = require("path"); -var Require = require("../bootstrap-node"); +require("./lib/jasmine-promise"); +var URL = require("url"); +var Require = require("../node"); // Use async spec to cause Jasmine to wait until the real specs have been loaded describe("Mr on node", function () { + it("must test on node", function () { + expect(typeof __dirname).toBe("string"); + }); it("loads", function () { - return Require.loadPackage(PATH.join(__dirname, "..")) + var location = Require.directoryPathToLocation(__dirname); + location = URL.resolve(location, "../"); + return Require.loadPackage(location) .then(function (mr) { return mr.async("spec/require-spec"); }) diff --git a/spec/production/program.js b/spec/production/program.js index 023b71ac..7c24e9fd 100644 --- a/spec/production/program.js +++ b/spec/production/program.js @@ -3,7 +3,6 @@ module.exports = require.async("dev-dependency") .then(function (a) { throw "should not be able to require dev-dependency in production mode"; }, function (error) { - console.log(error.message); test.assert( /Can\'t require module "dev-dependency" via "program"/.test(error.message), "cannot require dev-dependency in production mode" diff --git a/spec/read/package.json b/spec/read/package.json index 93f632ab..d356a520 100644 --- a/spec/read/package.json +++ b/spec/read/package.json @@ -1,5 +1,5 @@ { "mappings": { - "q": "../../packages/q" + "q": "../../node_modules/q" } } diff --git a/spec/require-spec.js b/spec/require-spec.js index 988b3802..5637a089 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -74,22 +74,19 @@ describe("Require", function () { var done; var message; - console.log(test + ":", "START"); + //console.log(test + ":", "START"); - return require.loadPackage( - module.directory + test + "/", - {} - ) + return require.loadPackage(module.directory + test + "/", {}) .then(function (pkg) { pkg.inject("test", { print: function (_message, level) { - console.log(test + ":", _message); + //console.log(test + ":", _message); if (_message === "DONE") { message = _message; } }, assert: function (guard, message) { - console.log(test + ":", guard ? "PASS" : "FAIL", message); + //console.log(test + ":", guard ? "PASS" : "FAIL", message); expect(!!guard).toBe(true); } }); @@ -97,11 +94,9 @@ describe("Require", function () { return pkg.async("program"); }) .then(function () { - }, function (reason, error) { - spec.fail(error || reason); - }) - .fin(function () { expect(message).toBe("DONE"); + }, function (reason) { + spec.fail(reason); }); }); diff --git a/spec/run.html b/spec/run.html index 3abc3889..7168d63e 100644 --- a/spec/run.html +++ b/spec/run.html @@ -8,7 +8,7 @@ - + From aae7a8d99e5da6ad223cbedddef8e584fc9d8699 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Aug 2013 09:45:32 -0700 Subject: [PATCH 02/64] Add support for compilers --- boot.js | 40 ++++++++++++++++++++++++---------- boot/preload-boilerplate.js | 40 ++++++++++++++++++++++++---------- require.js | 38 +++++++++++++++++++++++--------- spec/compiler/hello.text | 1 + spec/compiler/package.json | 7 ++++++ spec/compiler/program.js | 6 +++++ spec/compiler/text-compiler.js | 3 +++ spec/require-spec.js | 3 ++- 8 files changed, 105 insertions(+), 33 deletions(-) create mode 100644 spec/compiler/hello.text create mode 100644 spec/compiler/package.json create mode 100644 spec/compiler/program.js create mode 100644 spec/compiler/text-compiler.js diff --git a/boot.js b/boot.js index 3e6f0121..d41d7ad1 100644 --- a/boot.js +++ b/boot.js @@ -149,7 +149,7 @@ Require.read = function (url) { request.onload = request.load = onload; request.onerror = request.error = onerror; } catch (exception) { - response.reject(exception.message, exception); + response.reject(exception); } request.send(); @@ -476,6 +476,7 @@ Require.makeRequire = function (config) { config.compile = config.compile || config.makeCompiler(config); config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; + config.compilers = config.compilers || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -511,7 +512,7 @@ Require.makeRequire = function (config) { } // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { + var load = memoize(function (topId, viaId, loading) { var module = getModuleDescriptor(topId); return Q.fcall(function () { // if not already loaded, already instantiated, or @@ -526,15 +527,23 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); + var extension = Require.extension(topId); + if (config.compilers[extension]) { + var compilerId = config.compilers[extension]; + return deepLoad(compilerId, "", loading) + .then(function () { + var compile = require(compilerId); + compile(module); + }); + } else { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } } }); }); @@ -555,6 +564,7 @@ Require.makeRequire = function (config) { .then(function () { // load the transitive dependencies using the magic of // recursion. + var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { depId = resolve(depId, topId); // create dependees set, purely for debug purposes @@ -974,6 +984,7 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; + config.compilers = description.compilers; if (description.production !== void 0) { config.production = description.production; @@ -1137,6 +1148,13 @@ Require.base = function (location) { .match(/([^\/]+$|^\/$|^$)/)[1]; }; +Require.extension = function (location) { + var match = /\.([^\/]+)$/.exec(location); + if (match) { + return match[1]; + } +}; + // Tests whether the location or URL is a absolute. Require.isAbsolute = function(location) { return (/^[\w\-]+:/).test(location); diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index c911e504..86d11f6c 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -202,7 +202,7 @@ Require.read = function (url) { request.onload = request.load = onload; request.onerror = request.error = onerror; } catch (exception) { - response.reject(exception.message, exception); + response.reject(exception); } request.send(); @@ -548,6 +548,7 @@ Require.makeRequire = function (config) { config.compile = config.compile || config.makeCompiler(config); config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; + config.compilers = config.compilers || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -583,7 +584,7 @@ Require.makeRequire = function (config) { } // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { + var load = memoize(function (topId, viaId, loading) { var module = getModuleDescriptor(topId); return Q.fcall(function () { // if not already loaded, already instantiated, or @@ -598,15 +599,23 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); + var extension = Require.extension(topId); + if (config.compilers[extension]) { + var compilerId = config.compilers[extension]; + return deepLoad(compilerId, "", loading) + .then(function () { + var compile = require(compilerId); + compile(module); + }); + } else { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } } }); }); @@ -627,6 +636,7 @@ Require.makeRequire = function (config) { .then(function () { // load the transitive dependencies using the magic of // recursion. + var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { depId = resolve(depId, topId); // create dependees set, purely for debug purposes @@ -1046,6 +1056,7 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; + config.compilers = description.compilers; if (description.production !== void 0) { config.production = description.production; @@ -1209,6 +1220,13 @@ Require.base = function (location) { .match(/([^\/]+$|^\/$|^$)/)[1]; }; +Require.extension = function (location) { + var match = /\.([^\/]+)$/.exec(location); + if (match) { + return match[1]; + } +}; + // Tests whether the location or URL is a absolute. Require.isAbsolute = function(location) { return (/^[\w\-]+:/).test(location); diff --git a/require.js b/require.js index 3c07ce72..fdcf2067 100644 --- a/require.js +++ b/require.js @@ -35,6 +35,7 @@ Require.makeRequire = function (config) { config.compile = config.compile || config.makeCompiler(config); config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; + config.compilers = config.compilers || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -70,7 +71,7 @@ Require.makeRequire = function (config) { } // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { + var load = memoize(function (topId, viaId, loading) { var module = getModuleDescriptor(topId); return Q.fcall(function () { // if not already loaded, already instantiated, or @@ -85,15 +86,23 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); + var extension = Require.extension(topId); + if (config.compilers[extension]) { + var compilerId = config.compilers[extension]; + return deepLoad(compilerId, "", loading) + .then(function () { + var compile = require(compilerId); + compile(module); + }); + } else { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } } }); }); @@ -114,6 +123,7 @@ Require.makeRequire = function (config) { .then(function () { // load the transitive dependencies using the magic of // recursion. + var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { depId = resolve(depId, topId); // create dependees set, purely for debug purposes @@ -533,6 +543,7 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; + config.compilers = description.compilers; if (description.production !== void 0) { config.production = description.production; @@ -696,6 +707,13 @@ Require.base = function (location) { .match(/([^\/]+$|^\/$|^$)/)[1]; }; +Require.extension = function (location) { + var match = /\.([^\/]+)$/.exec(location); + if (match) { + return match[1]; + } +}; + // Tests whether the location or URL is a absolute. Require.isAbsolute = function(location) { return (/^[\w\-]+:/).test(location); diff --git a/spec/compiler/hello.text b/spec/compiler/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/compiler/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/compiler/package.json b/spec/compiler/package.json new file mode 100644 index 00000000..dde692b1 --- /dev/null +++ b/spec/compiler/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "compilers": { + "text": "text-compiler" + } +} diff --git a/spec/compiler/program.js b/spec/compiler/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/compiler/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + diff --git a/spec/compiler/text-compiler.js b/spec/compiler/text-compiler.js new file mode 100644 index 00000000..802085e0 --- /dev/null +++ b/spec/compiler/text-compiler.js @@ -0,0 +1,3 @@ +module.exports = function compile(module) { + module.exports = module.text; +}; diff --git a/spec/require-spec.js b/spec/require-spec.js index 5637a089..151223e2 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -61,7 +61,8 @@ describe("Require", function () { "inject-mapping", {name: "script-injection-dep", node: false}, {name: "script-injection", node: false}, - "read" + "read", + "compiler" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { From 36e6b6e77176e0fcff8382a4f80992cea33bd4d4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Aug 2013 10:13:08 -0700 Subject: [PATCH 03/64] Add support for translators --- boot.js | 33 ++++++++++++++++++++++-------- boot/preload-boilerplate.js | 33 ++++++++++++++++++++++-------- require.js | 33 ++++++++++++++++++++++-------- spec/require-spec.js | 3 ++- spec/translator/hello.text | 1 + spec/translator/package.json | 7 +++++++ spec/translator/program.js | 6 ++++++ spec/translator/text-translator.js | 3 +++ 8 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 spec/translator/hello.text create mode 100644 spec/translator/package.json create mode 100644 spec/translator/program.js create mode 100644 spec/translator/text-translator.js diff --git a/boot.js b/boot.js index d41d7ad1..096dad26 100644 --- a/boot.js +++ b/boot.js @@ -477,6 +477,7 @@ Require.makeRequire = function (config) { config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; config.compilers = config.compilers || {}; + config.translators = config.translators || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -536,14 +537,29 @@ Require.makeRequire = function (config) { compile(module); }); } else { - config.compile(module); - var dependencies = module.dependencies = module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } + return Q.fcall(function () { + if (config.translators[extension]) { + var translatorId = config.translators[extension]; + // TODO try to load translator related modules in a + // parallel module system so that they do not get + // bundled + return deepLoad(translatorId, "", loading) + .then(function () { + var translate = require(translatorId); + module.text = translate(module.text, module); + }); + } + }) + .then(function () { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } + }); } }); }); @@ -985,6 +1001,7 @@ function configurePackage(location, description, parent) { config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; config.compilers = description.compilers; + config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 86d11f6c..8b19a03e 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -549,6 +549,7 @@ Require.makeRequire = function (config) { config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; config.compilers = config.compilers || {}; + config.translators = config.translators || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -608,14 +609,29 @@ Require.makeRequire = function (config) { compile(module); }); } else { - config.compile(module); - var dependencies = module.dependencies = module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } + return Q.fcall(function () { + if (config.translators[extension]) { + var translatorId = config.translators[extension]; + // TODO try to load translator related modules in a + // parallel module system so that they do not get + // bundled + return deepLoad(translatorId, "", loading) + .then(function () { + var translate = require(translatorId); + module.text = translate(module.text, module); + }); + } + }) + .then(function () { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } + }); } }); }); @@ -1057,6 +1073,7 @@ function configurePackage(location, description, parent) { config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; config.compilers = description.compilers; + config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; diff --git a/require.js b/require.js index fdcf2067..eea90951 100644 --- a/require.js +++ b/require.js @@ -36,6 +36,7 @@ Require.makeRequire = function (config) { config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; config.compilers = config.compilers || {}; + config.translators = config.translators || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -95,14 +96,29 @@ Require.makeRequire = function (config) { compile(module); }); } else { - config.compile(module); - var dependencies = module.dependencies = module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } + return Q.fcall(function () { + if (config.translators[extension]) { + var translatorId = config.translators[extension]; + // TODO try to load translator related modules in a + // parallel module system so that they do not get + // bundled + return deepLoad(translatorId, "", loading) + .then(function () { + var translate = require(translatorId); + module.text = translate(module.text, module); + }); + } + }) + .then(function () { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } + }); } }); }); @@ -544,6 +560,7 @@ function configurePackage(location, description, parent) { config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; config.compilers = description.compilers; + config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; diff --git a/spec/require-spec.js b/spec/require-spec.js index 151223e2..e592c49f 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -62,7 +62,8 @@ describe("Require", function () { {name: "script-injection-dep", node: false}, {name: "script-injection", node: false}, "read", - "compiler" + "compiler", + "translator" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { diff --git a/spec/translator/hello.text b/spec/translator/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/translator/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/translator/package.json b/spec/translator/package.json new file mode 100644 index 00000000..2f577d14 --- /dev/null +++ b/spec/translator/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "translators": { + "text": "text-translator" + } +} diff --git a/spec/translator/program.js b/spec/translator/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/translator/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + diff --git a/spec/translator/text-translator.js b/spec/translator/text-translator.js new file mode 100644 index 00000000..efcaef9d --- /dev/null +++ b/spec/translator/text-translator.js @@ -0,0 +1,3 @@ +module.exports = function (text) { + return "module.exports = " + JSON.stringify(text) + ";"; +}; From 14ab32e49f8937e6a9f3c6c52694c36c2bb512d0 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Aug 2013 11:17:49 -0700 Subject: [PATCH 04/64] Inherit translators and compilers from dependencies --- boot.js | 63 ++++++++++++++++--- boot/preload-boilerplate.js | 63 ++++++++++++++++--- require.js | 63 ++++++++++++++++--- spec/compiler-package/hello.text | 1 + .../node_modules/mr-text/package.json | 7 +++ .../node_modules/mr-text/text-compiler.js | 3 + spec/compiler-package/package.json | 7 +++ spec/compiler-package/program.js | 6 ++ spec/identify/package.json | 4 +- spec/require-spec.js | 4 +- spec/translator-package/hello.text | 1 + .../node_modules/mr-text/mr-text.js | 3 + .../node_modules/mr-text/package.json | 8 +++ spec/translator-package/package.json | 7 +++ spec/translator-package/program.js | 6 ++ 15 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 spec/compiler-package/hello.text create mode 100644 spec/compiler-package/node_modules/mr-text/package.json create mode 100644 spec/compiler-package/node_modules/mr-text/text-compiler.js create mode 100644 spec/compiler-package/package.json create mode 100644 spec/compiler-package/program.js create mode 100644 spec/translator-package/hello.text create mode 100644 spec/translator-package/node_modules/mr-text/mr-text.js create mode 100644 spec/translator-package/node_modules/mr-text/package.json create mode 100644 spec/translator-package/package.json create mode 100644 spec/translator-package/program.js diff --git a/boot.js b/boot.js index 096dad26..912c9ba2 100644 --- a/boot.js +++ b/boot.js @@ -491,6 +491,7 @@ Require.makeRequire = function (config) { if (!has(modules, lookupId)) { modules[lookupId] = { id: id, + extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION require: require }; @@ -528,9 +529,8 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - var extension = Require.extension(topId); - if (config.compilers[extension]) { - var compilerId = config.compilers[extension]; + if (config.compilers[module.extension]) { + var compilerId = config.compilers[module.extension]; return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); @@ -538,8 +538,8 @@ Require.makeRequire = function (config) { }); } else { return Q.fcall(function () { - if (config.translators[extension]) { - var translatorId = config.translators[extension]; + if (config.translators[module.extension]) { + var translatorId = config.translators[module.extension]; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled @@ -901,12 +901,19 @@ Require.loadPackage = function (dependency, config) { return loadedPackages[location]; }; - config.loadPackage = function (dependency, viaConfig) { + config.loadPackage = function (dependency, viaConfig, loading) { dependency = normalizeDependency(dependency, viaConfig); if (!dependency.location) { throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } var location = dependency.location; + + loading = loading || {}; + if (loading[location]) { + return Q(); + } + loading[location] = true; + if (!loadingPackages[location]) { loadingPackages[location] = Require.loadPackageDescription(dependency, config) .then(function (packageDescription) { @@ -917,7 +924,14 @@ Require.loadPackage = function (dependency, config) { ); var pkg = Require.makeRequire(subconfig); loadedPackages[location] = pkg; - return pkg; + return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { + var dependency = subconfig.mappings[prefix]; + return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + })) + .then(function () { + postConfigurePackage(subconfig); + }) + .thenResolve(pkg); }); loadingPackages[location].done(); } @@ -1094,8 +1108,8 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production? description.devDependencies : null] + // dependencies, devDependencies if not in production, if not installed by NPM + [description.dependencies, description._id || description.production ? null : description.devDependencies] .forEach(function (dependencies) { if (!dependencies) { return; @@ -1125,6 +1139,37 @@ function configurePackage(location, description, parent) { return config; } +function postConfigurePackage(config) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + prefixes.forEach(function (prefix) { + + var dependency = mappings[prefix]; + if (!config.hasPackage(dependency)) { + return; + } + var package = config.getPackage(dependency); + var extensions; + + // reference translators + var myTranslators = config.translators = config.translators || {}; + var theirTranslators = package.config.translators; + extensions = Object.keys(theirTranslators); + extensions.forEach(function (extension) { + myTranslators[extension] = prefix + "/" + theirTranslators[extension]; + }); + + // reference compilers + var myCompilers = config.compilers = config.compilers || {}; + var theirCompilers = package.config.compilers; + extensions = Object.keys(theirCompilers); + extensions.forEach(function (extension) { + myCompilers[extension] = prefix + "/" + theirCompilers[extension]; + }); + + }); +} + // Helper functions: function has(object, property) { diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 8b19a03e..bc22b9a8 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -563,6 +563,7 @@ Require.makeRequire = function (config) { if (!has(modules, lookupId)) { modules[lookupId] = { id: id, + extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION require: require }; @@ -600,9 +601,8 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - var extension = Require.extension(topId); - if (config.compilers[extension]) { - var compilerId = config.compilers[extension]; + if (config.compilers[module.extension]) { + var compilerId = config.compilers[module.extension]; return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); @@ -610,8 +610,8 @@ Require.makeRequire = function (config) { }); } else { return Q.fcall(function () { - if (config.translators[extension]) { - var translatorId = config.translators[extension]; + if (config.translators[module.extension]) { + var translatorId = config.translators[module.extension]; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled @@ -973,12 +973,19 @@ Require.loadPackage = function (dependency, config) { return loadedPackages[location]; }; - config.loadPackage = function (dependency, viaConfig) { + config.loadPackage = function (dependency, viaConfig, loading) { dependency = normalizeDependency(dependency, viaConfig); if (!dependency.location) { throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } var location = dependency.location; + + loading = loading || {}; + if (loading[location]) { + return Q(); + } + loading[location] = true; + if (!loadingPackages[location]) { loadingPackages[location] = Require.loadPackageDescription(dependency, config) .then(function (packageDescription) { @@ -989,7 +996,14 @@ Require.loadPackage = function (dependency, config) { ); var pkg = Require.makeRequire(subconfig); loadedPackages[location] = pkg; - return pkg; + return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { + var dependency = subconfig.mappings[prefix]; + return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + })) + .then(function () { + postConfigurePackage(subconfig); + }) + .thenResolve(pkg); }); loadingPackages[location].done(); } @@ -1166,8 +1180,8 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production? description.devDependencies : null] + // dependencies, devDependencies if not in production, if not installed by NPM + [description.dependencies, description._id || description.production ? null : description.devDependencies] .forEach(function (dependencies) { if (!dependencies) { return; @@ -1197,6 +1211,37 @@ function configurePackage(location, description, parent) { return config; } +function postConfigurePackage(config) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + prefixes.forEach(function (prefix) { + + var dependency = mappings[prefix]; + if (!config.hasPackage(dependency)) { + return; + } + var package = config.getPackage(dependency); + var extensions; + + // reference translators + var myTranslators = config.translators = config.translators || {}; + var theirTranslators = package.config.translators; + extensions = Object.keys(theirTranslators); + extensions.forEach(function (extension) { + myTranslators[extension] = prefix + "/" + theirTranslators[extension]; + }); + + // reference compilers + var myCompilers = config.compilers = config.compilers || {}; + var theirCompilers = package.config.compilers; + extensions = Object.keys(theirCompilers); + extensions.forEach(function (extension) { + myCompilers[extension] = prefix + "/" + theirCompilers[extension]; + }); + + }); +} + // Helper functions: function has(object, property) { diff --git a/require.js b/require.js index eea90951..a045c6dc 100644 --- a/require.js +++ b/require.js @@ -50,6 +50,7 @@ Require.makeRequire = function (config) { if (!has(modules, lookupId)) { modules[lookupId] = { id: id, + extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION require: require }; @@ -87,9 +88,8 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - var extension = Require.extension(topId); - if (config.compilers[extension]) { - var compilerId = config.compilers[extension]; + if (config.compilers[module.extension]) { + var compilerId = config.compilers[module.extension]; return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); @@ -97,8 +97,8 @@ Require.makeRequire = function (config) { }); } else { return Q.fcall(function () { - if (config.translators[extension]) { - var translatorId = config.translators[extension]; + if (config.translators[module.extension]) { + var translatorId = config.translators[module.extension]; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled @@ -460,12 +460,19 @@ Require.loadPackage = function (dependency, config) { return loadedPackages[location]; }; - config.loadPackage = function (dependency, viaConfig) { + config.loadPackage = function (dependency, viaConfig, loading) { dependency = normalizeDependency(dependency, viaConfig); if (!dependency.location) { throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } var location = dependency.location; + + loading = loading || {}; + if (loading[location]) { + return Q(); + } + loading[location] = true; + if (!loadingPackages[location]) { loadingPackages[location] = Require.loadPackageDescription(dependency, config) .then(function (packageDescription) { @@ -476,7 +483,14 @@ Require.loadPackage = function (dependency, config) { ); var pkg = Require.makeRequire(subconfig); loadedPackages[location] = pkg; - return pkg; + return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { + var dependency = subconfig.mappings[prefix]; + return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + })) + .then(function () { + postConfigurePackage(subconfig); + }) + .thenResolve(pkg); }); loadingPackages[location].done(); } @@ -653,8 +667,8 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production? description.devDependencies : null] + // dependencies, devDependencies if not in production, if not installed by NPM + [description.dependencies, description._id || description.production ? null : description.devDependencies] .forEach(function (dependencies) { if (!dependencies) { return; @@ -684,6 +698,37 @@ function configurePackage(location, description, parent) { return config; } +function postConfigurePackage(config) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + prefixes.forEach(function (prefix) { + + var dependency = mappings[prefix]; + if (!config.hasPackage(dependency)) { + return; + } + var package = config.getPackage(dependency); + var extensions; + + // reference translators + var myTranslators = config.translators = config.translators || {}; + var theirTranslators = package.config.translators; + extensions = Object.keys(theirTranslators); + extensions.forEach(function (extension) { + myTranslators[extension] = prefix + "/" + theirTranslators[extension]; + }); + + // reference compilers + var myCompilers = config.compilers = config.compilers || {}; + var theirCompilers = package.config.compilers; + extensions = Object.keys(theirCompilers); + extensions.forEach(function (extension) { + myCompilers[extension] = prefix + "/" + theirCompilers[extension]; + }); + + }); +} + // Helper functions: function has(object, property) { diff --git a/spec/compiler-package/hello.text b/spec/compiler-package/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/compiler-package/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/compiler-package/node_modules/mr-text/package.json b/spec/compiler-package/node_modules/mr-text/package.json new file mode 100644 index 00000000..cae5888f --- /dev/null +++ b/spec/compiler-package/node_modules/mr-text/package.json @@ -0,0 +1,7 @@ +{ + "name": "mr-text", + "version": "*", + "compilers": { + "text": "text-compiler" + } +} diff --git a/spec/compiler-package/node_modules/mr-text/text-compiler.js b/spec/compiler-package/node_modules/mr-text/text-compiler.js new file mode 100644 index 00000000..802085e0 --- /dev/null +++ b/spec/compiler-package/node_modules/mr-text/text-compiler.js @@ -0,0 +1,3 @@ +module.exports = function compile(module) { + module.exports = module.text; +}; diff --git a/spec/compiler-package/package.json b/spec/compiler-package/package.json new file mode 100644 index 00000000..6bef9c7e --- /dev/null +++ b/spec/compiler-package/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "dependencies": { + "mr-text": "*" + } +} diff --git a/spec/compiler-package/program.js b/spec/compiler-package/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/compiler-package/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + diff --git a/spec/identify/package.json b/spec/identify/package.json index 4f497ea6..e0f0b820 100644 --- a/spec/identify/package.json +++ b/spec/identify/package.json @@ -2,8 +2,6 @@ "name": "identify", "dependencies": { "cyclic": "*", - "a": "*", - "x": "*", - "y": "*" + "x": "*" } } diff --git a/spec/require-spec.js b/spec/require-spec.js index e592c49f..bcef2a26 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -63,7 +63,9 @@ describe("Require", function () { {name: "script-injection", node: false}, "read", "compiler", - "translator" + "translator", + "compiler-package", + "translator-package" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { diff --git a/spec/translator-package/hello.text b/spec/translator-package/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/translator-package/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/translator-package/node_modules/mr-text/mr-text.js b/spec/translator-package/node_modules/mr-text/mr-text.js new file mode 100644 index 00000000..efcaef9d --- /dev/null +++ b/spec/translator-package/node_modules/mr-text/mr-text.js @@ -0,0 +1,3 @@ +module.exports = function (text) { + return "module.exports = " + JSON.stringify(text) + ";"; +}; diff --git a/spec/translator-package/node_modules/mr-text/package.json b/spec/translator-package/node_modules/mr-text/package.json new file mode 100644 index 00000000..1d5c94d4 --- /dev/null +++ b/spec/translator-package/node_modules/mr-text/package.json @@ -0,0 +1,8 @@ +{ + "name": "mr-text", + "version": "", + "main": "mr-text", + "translators": { + "text": "mr-text" + } +} diff --git a/spec/translator-package/package.json b/spec/translator-package/package.json new file mode 100644 index 00000000..6bef9c7e --- /dev/null +++ b/spec/translator-package/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "dependencies": { + "mr-text": "*" + } +} diff --git a/spec/translator-package/program.js b/spec/translator-package/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/translator-package/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + From 73ee93d27954e370335b4dea7932f37fd3389211 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 28 Aug 2013 18:28:27 -0700 Subject: [PATCH 05/64] Add support for redirect patterns Inherited by dependee packages. --- boot.js | 73 +++++++++++++++---- boot/preload-boilerplate.js | 73 +++++++++++++++---- browser.js | 14 +--- node.js | 18 +---- require.js | 59 ++++++++++++++- spec/redirect-patterns/foo-bar.js | 1 + .../node_modules/mr-foo/package.json | 7 ++ spec/redirect-patterns/package.json | 7 ++ spec/redirect-patterns/program.js | 4 + spec/require-spec.js | 3 +- 10 files changed, 198 insertions(+), 61 deletions(-) create mode 100644 spec/redirect-patterns/foo-bar.js create mode 100644 spec/redirect-patterns/node_modules/mr-foo/package.json create mode 100644 spec/redirect-patterns/package.json create mode 100644 spec/redirect-patterns/program.js diff --git a/boot.js b/boot.js index 912c9ba2..c0a5fd05 100644 --- a/boot.js +++ b/boot.js @@ -314,19 +314,7 @@ Require.makeLoader = function (config) { } else { Loader = Require.XhrLoader; } - return Require.MappingsLoader( - config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Loader(config) - ) - ) - ) - ); + return Require.makeCommonLoader(config, Loader(config)); }; module.exports = Require; @@ -478,6 +466,7 @@ Require.makeRequire = function (config) { config.read = config.read || Require.read; config.compilers = config.compilers || {}; config.translators = config.translators || {}; + config.redirectTable = config.redirectTable || []; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -929,7 +918,7 @@ Require.loadPackage = function (dependency, config) { return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); })) .then(function () { - postConfigurePackage(subconfig); + postConfigurePackage(subconfig, packageDescription); }) .thenResolve(pkg); }); @@ -1139,9 +1128,10 @@ function configurePackage(location, description, parent) { return config; } -function postConfigurePackage(config) { +function postConfigurePackage(config, description) { var mappings = config.mappings; var prefixes = Object.keys(mappings); + var redirectTable = config.redirectTable = config.redirectTable || []; prefixes.forEach(function (prefix) { var dependency = mappings[prefix]; @@ -1167,7 +1157,25 @@ function postConfigurePackage(config) { myCompilers[extension] = prefix + "/" + theirCompilers[extension]; }); + // copy redirect patterns + redirectTable.push.apply( + redirectTable, + package.config.redirectTable + ); + }); + + if (description["redirect-patterns"]) { + var describedPatterns = description["redirect-patterns"]; + for (var pattern in describedPatterns) { + if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + redirectTable.push([ + new RegExp(pattern), + describedPatterns[pattern] + ]); + } + } + } } // Helper functions: @@ -1316,6 +1324,25 @@ Require.JsonCompiler = function (config, compile) { // Built-in loader "middleware": +Require.makeCommonLoader = function (config, load) { + return Require.MappingsLoader( + config, + Require.RedirectPatternsLoader( + config, + Require.ExtensionsLoader( + config, + Require.PathsLoader( + config, + Require.MemoizedLoader( + config, + load + ) + ) + ) + ) + ); +}; + // Using mappings hash to load modules that match a mapping. Require.MappingsLoader = function(config, load) { config.mappings = config.mappings || {}; @@ -1429,6 +1456,22 @@ Require.MemoizedLoader = function (config, load) { return memoize(load, cache); }; +Require.RedirectPatternsLoader = function (config, load) { + return function (id, module) { + var table = config.redirectTable || []; + for (var i = 0; i < table.length; i++) { + var expression = table[i][0]; + var match = expression.exec(id); + if (match) { + var replacement = table[i][1]; + module.redirect = id.replace(expression, replacement); + return; + } + } + return load(id, module); + }; +}; + var normalizeId = function (id) { var match = /^(.*)\.js$/.exec(id); if (match) { diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index bc22b9a8..32e4f0dc 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -367,19 +367,7 @@ Require.makeLoader = function (config) { } else { Loader = Require.XhrLoader; } - return Require.MappingsLoader( - config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Loader(config) - ) - ) - ) - ); + return Require.makeCommonLoader(config, Loader(config)); }; module.exports = Require; @@ -550,6 +538,7 @@ Require.makeRequire = function (config) { config.read = config.read || Require.read; config.compilers = config.compilers || {}; config.translators = config.translators || {}; + config.redirectTable = config.redirectTable || []; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -1001,7 +990,7 @@ Require.loadPackage = function (dependency, config) { return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); })) .then(function () { - postConfigurePackage(subconfig); + postConfigurePackage(subconfig, packageDescription); }) .thenResolve(pkg); }); @@ -1211,9 +1200,10 @@ function configurePackage(location, description, parent) { return config; } -function postConfigurePackage(config) { +function postConfigurePackage(config, description) { var mappings = config.mappings; var prefixes = Object.keys(mappings); + var redirectTable = config.redirectTable = config.redirectTable || []; prefixes.forEach(function (prefix) { var dependency = mappings[prefix]; @@ -1239,7 +1229,25 @@ function postConfigurePackage(config) { myCompilers[extension] = prefix + "/" + theirCompilers[extension]; }); + // copy redirect patterns + redirectTable.push.apply( + redirectTable, + package.config.redirectTable + ); + }); + + if (description["redirect-patterns"]) { + var describedPatterns = description["redirect-patterns"]; + for (var pattern in describedPatterns) { + if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + redirectTable.push([ + new RegExp(pattern), + describedPatterns[pattern] + ]); + } + } + } } // Helper functions: @@ -1388,6 +1396,25 @@ Require.JsonCompiler = function (config, compile) { // Built-in loader "middleware": +Require.makeCommonLoader = function (config, load) { + return Require.MappingsLoader( + config, + Require.RedirectPatternsLoader( + config, + Require.ExtensionsLoader( + config, + Require.PathsLoader( + config, + Require.MemoizedLoader( + config, + load + ) + ) + ) + ) + ); +}; + // Using mappings hash to load modules that match a mapping. Require.MappingsLoader = function(config, load) { config.mappings = config.mappings || {}; @@ -1501,6 +1528,22 @@ Require.MemoizedLoader = function (config, load) { return memoize(load, cache); }; +Require.RedirectPatternsLoader = function (config, load) { + return function (id, module) { + var table = config.redirectTable || []; + for (var i = 0; i < table.length; i++) { + var expression = table[i][0]; + var match = expression.exec(id); + if (match) { + var replacement = table[i][1]; + module.redirect = id.replace(expression, replacement); + return; + } + } + return load(id, module); + }; +}; + var normalizeId = function (id) { var match = /^(.*)\.js$/.exec(id); if (match) { diff --git a/browser.js b/browser.js index 518d09c9..a4f12246 100644 --- a/browser.js +++ b/browser.js @@ -229,19 +229,7 @@ Require.makeLoader = function (config) { } else { Loader = Require.XhrLoader; } - return Require.MappingsLoader( - config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Loader(config) - ) - ) - ) - ); + return Require.makeCommonLoader(config, Loader(config)); }; module.exports = Require; diff --git a/node.js b/node.js index 5e66e088..b60019be 100644 --- a/node.js +++ b/node.js @@ -139,22 +139,10 @@ Require.NodeLoader = function NodeLoader(config, load) { }; Require.makeLoader = function makeLoader(config) { - return Require.MappingsLoader( + return Require.makeCommonLoader(config, Require.Loader( config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Require.Loader( - config, - Require.NodeLoader(config) - ) - ) - ) - ) - ); + Require.NodeLoader(config) + )); }; Require.findPackagePath = function findPackagePath(directory) { diff --git a/require.js b/require.js index a045c6dc..b4df5e15 100644 --- a/require.js +++ b/require.js @@ -37,6 +37,7 @@ Require.makeRequire = function (config) { config.read = config.read || Require.read; config.compilers = config.compilers || {}; config.translators = config.translators || {}; + config.redirectTable = config.redirectTable || []; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -488,7 +489,7 @@ Require.loadPackage = function (dependency, config) { return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); })) .then(function () { - postConfigurePackage(subconfig); + postConfigurePackage(subconfig, packageDescription); }) .thenResolve(pkg); }); @@ -698,9 +699,10 @@ function configurePackage(location, description, parent) { return config; } -function postConfigurePackage(config) { +function postConfigurePackage(config, description) { var mappings = config.mappings; var prefixes = Object.keys(mappings); + var redirectTable = config.redirectTable = config.redirectTable || []; prefixes.forEach(function (prefix) { var dependency = mappings[prefix]; @@ -726,7 +728,25 @@ function postConfigurePackage(config) { myCompilers[extension] = prefix + "/" + theirCompilers[extension]; }); + // copy redirect patterns + redirectTable.push.apply( + redirectTable, + package.config.redirectTable + ); + }); + + if (description["redirect-patterns"]) { + var describedPatterns = description["redirect-patterns"]; + for (var pattern in describedPatterns) { + if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + redirectTable.push([ + new RegExp(pattern), + describedPatterns[pattern] + ]); + } + } + } } // Helper functions: @@ -875,6 +895,25 @@ Require.JsonCompiler = function (config, compile) { // Built-in loader "middleware": +Require.makeCommonLoader = function (config, load) { + return Require.MappingsLoader( + config, + Require.RedirectPatternsLoader( + config, + Require.ExtensionsLoader( + config, + Require.PathsLoader( + config, + Require.MemoizedLoader( + config, + load + ) + ) + ) + ) + ); +}; + // Using mappings hash to load modules that match a mapping. Require.MappingsLoader = function(config, load) { config.mappings = config.mappings || {}; @@ -988,6 +1027,22 @@ Require.MemoizedLoader = function (config, load) { return memoize(load, cache); }; +Require.RedirectPatternsLoader = function (config, load) { + return function (id, module) { + var table = config.redirectTable || []; + for (var i = 0; i < table.length; i++) { + var expression = table[i][0]; + var match = expression.exec(id); + if (match) { + var replacement = table[i][1]; + module.redirect = id.replace(expression, replacement); + return; + } + } + return load(id, module); + }; +}; + var normalizeId = function (id) { var match = /^(.*)\.js$/.exec(id); if (match) { diff --git a/spec/redirect-patterns/foo-bar.js b/spec/redirect-patterns/foo-bar.js new file mode 100644 index 00000000..34cd7b97 --- /dev/null +++ b/spec/redirect-patterns/foo-bar.js @@ -0,0 +1 @@ +module.exports = "Hello, Foo!"; diff --git a/spec/redirect-patterns/node_modules/mr-foo/package.json b/spec/redirect-patterns/node_modules/mr-foo/package.json new file mode 100644 index 00000000..420c041e --- /dev/null +++ b/spec/redirect-patterns/node_modules/mr-foo/package.json @@ -0,0 +1,7 @@ +{ + "name": "mr-foo", + "version": "*", + "redirect-patterns": { + "^(.*)\\.foo$": "foo-$1" + } +} diff --git a/spec/redirect-patterns/package.json b/spec/redirect-patterns/package.json new file mode 100644 index 00000000..9936de83 --- /dev/null +++ b/spec/redirect-patterns/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "dependencies": { + "mr-foo": "*" + } +} diff --git a/spec/redirect-patterns/program.js b/spec/redirect-patterns/program.js new file mode 100644 index 00000000..bce64e9a --- /dev/null +++ b/spec/redirect-patterns/program.js @@ -0,0 +1,4 @@ +var test = require("test"); +test.assert(require.lookup("bar.foo").id === "foo-bar"); +test.assert(require("bar.foo") === "Hello, Foo!"); +test.print("DONE", "info"); diff --git a/spec/require-spec.js b/spec/require-spec.js index bcef2a26..bae704d8 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -65,7 +65,8 @@ describe("Require", function () { "compiler", "translator", "compiler-package", - "translator-package" + "translator-package", + "redirect-patterns" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { From 4102bd7627b57d13a48b8a7e4a676079511bc412 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 1 Sep 2013 13:50:12 -0700 Subject: [PATCH 06/64] Fix main linkage for relative identifiers Previously, a package with a "main" property that started with "./" would be linked incorrectly. --- boot.js | 2 +- require.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boot.js b/boot.js index c0a5fd05..bf8fe86d 100644 --- a/boot.js +++ b/boot.js @@ -1069,7 +1069,7 @@ function configurePackage(location, description, parent) { // loaded definition from the given path. modules[""] = { id: "", - redirect: normalizeId(description.main), + redirect: normalizeId(resolve(description.main, "")), location: config.location }; diff --git a/require.js b/require.js index b4df5e15..40a69da0 100644 --- a/require.js +++ b/require.js @@ -640,7 +640,7 @@ function configurePackage(location, description, parent) { // loaded definition from the given path. modules[""] = { id: "", - redirect: normalizeId(description.main), + redirect: normalizeId(resolve(description.main, "")), location: config.location }; From 48118c03563769b3dc76b265b87d632119e0dca0 Mon Sep 17 00:00:00 2001 From: Stuart Knightley Date: Tue, 10 Sep 2013 17:27:38 -0700 Subject: [PATCH 07/64] Add tests for main property of package.json and fix for Node --- boot.js | 2 +- boot/preload-boilerplate.js | 4 ++-- require.js | 2 +- spec/main/node_modules/dot-slash/a.js | 1 + spec/main/node_modules/dot-slash/package.json | 3 +++ spec/main/node_modules/js-ext/a.js | 1 + spec/main/node_modules/js-ext/package.json | 3 +++ spec/main/node_modules/no-ext/a.js | 1 + spec/main/node_modules/no-ext/package.json | 3 +++ spec/main/package.json | 7 +++++++ spec/main/program.js | 8 ++++++++ spec/require-spec.js | 3 ++- 12 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 spec/main/node_modules/dot-slash/a.js create mode 100644 spec/main/node_modules/dot-slash/package.json create mode 100644 spec/main/node_modules/js-ext/a.js create mode 100644 spec/main/node_modules/js-ext/package.json create mode 100644 spec/main/node_modules/no-ext/a.js create mode 100644 spec/main/node_modules/no-ext/package.json create mode 100644 spec/main/package.json create mode 100644 spec/main/program.js diff --git a/boot.js b/boot.js index bf8fe86d..285ac75d 100644 --- a/boot.js +++ b/boot.js @@ -1073,7 +1073,7 @@ function configurePackage(location, description, parent) { location: config.location }; - if (description.name !== modules[""].redirect) { + if (description.name && description.name !== modules[""].redirect) { modules[description.name] = { id: description.name, redirect: "", diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 32e4f0dc..2a48a0ec 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -1141,11 +1141,11 @@ function configurePackage(location, description, parent) { // loaded definition from the given path. modules[""] = { id: "", - redirect: normalizeId(description.main), + redirect: normalizeId(resolve(description.main, "")), location: config.location }; - if (description.name !== modules[""].redirect) { + if (description.name && description.name !== modules[""].redirect) { modules[description.name] = { id: description.name, redirect: "", diff --git a/require.js b/require.js index 40a69da0..9add2799 100644 --- a/require.js +++ b/require.js @@ -644,7 +644,7 @@ function configurePackage(location, description, parent) { location: config.location }; - if (description.name !== modules[""].redirect) { + if (description.name && description.name !== modules[""].redirect) { modules[description.name] = { id: description.name, redirect: "", diff --git a/spec/main/node_modules/dot-slash/a.js b/spec/main/node_modules/dot-slash/a.js new file mode 100644 index 00000000..4387befd --- /dev/null +++ b/spec/main/node_modules/dot-slash/a.js @@ -0,0 +1 @@ +module.exports = 10; diff --git a/spec/main/node_modules/dot-slash/package.json b/spec/main/node_modules/dot-slash/package.json new file mode 100644 index 00000000..35e7b12f --- /dev/null +++ b/spec/main/node_modules/dot-slash/package.json @@ -0,0 +1,3 @@ +{ + "main": "./a.js" +} diff --git a/spec/main/node_modules/js-ext/a.js b/spec/main/node_modules/js-ext/a.js new file mode 100644 index 00000000..9320b557 --- /dev/null +++ b/spec/main/node_modules/js-ext/a.js @@ -0,0 +1 @@ +module.exports = 20; diff --git a/spec/main/node_modules/js-ext/package.json b/spec/main/node_modules/js-ext/package.json new file mode 100644 index 00000000..d888d3e1 --- /dev/null +++ b/spec/main/node_modules/js-ext/package.json @@ -0,0 +1,3 @@ +{ + "main": "a.js" +} diff --git a/spec/main/node_modules/no-ext/a.js b/spec/main/node_modules/no-ext/a.js new file mode 100644 index 00000000..dc1b1836 --- /dev/null +++ b/spec/main/node_modules/no-ext/a.js @@ -0,0 +1 @@ +module.exports = 30; diff --git a/spec/main/node_modules/no-ext/package.json b/spec/main/node_modules/no-ext/package.json new file mode 100644 index 00000000..7b4272a6 --- /dev/null +++ b/spec/main/node_modules/no-ext/package.json @@ -0,0 +1,3 @@ +{ + "main": "a" +} diff --git a/spec/main/package.json b/spec/main/package.json new file mode 100644 index 00000000..cb2f1cae --- /dev/null +++ b/spec/main/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "dot-slash": "*", + "js-ext": "*", + "no-ext": "*" + } +} diff --git a/spec/main/program.js b/spec/main/program.js new file mode 100644 index 00000000..317448c0 --- /dev/null +++ b/spec/main/program.js @@ -0,0 +1,8 @@ +var test = require('test'); + +debugger; +test.assert(require('dot-slash') === 10, 'main with "./"'); +test.assert(require('js-ext') === 20, 'main with ".js" extension'); +test.assert(require('no-ext') === 30, 'main with no extension'); + +test.print('DONE', 'info'); diff --git a/spec/require-spec.js b/spec/require-spec.js index bae704d8..772a8502 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -66,7 +66,8 @@ describe("Require", function () { "translator", "compiler-package", "translator-package", - "redirect-patterns" + "redirect-patterns", + "main" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { From aedbd9b8be5d22bc11d705f5cc675fab7deae92a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:23:53 -0700 Subject: [PATCH 08/64] Anonymous require in boilerplate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of allocating a require function for every call, only create the closure if it’s needed. --- boot.js | 2 +- boot/boilerplate.js | 2 +- boot/preload-boilerplate.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/boot.js b/boot.js index 285ac75d..b328b008 100644 --- a/boot.js +++ b/boot.js @@ -14,7 +14,7 @@ var module = this; if (!module.exports) { module.exports = {}; - function require(id) { + var require = function (id) { var index = module.dependencies[id]; var dependency = modules[index]; if (!dependency) diff --git a/boot/boilerplate.js b/boot/boilerplate.js index eb74c50b..472d9b41 100644 --- a/boot/boilerplate.js +++ b/boot/boilerplate.js @@ -14,7 +14,7 @@ var module = this; if (!module.exports) { module.exports = {}; - function require(id) { + var require = function (id) { var index = module.dependencies[id]; var dependency = modules[index]; if (!dependency) diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 2a48a0ec..3f44e30c 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -14,7 +14,7 @@ var module = this; if (!module.exports) { module.exports = {}; - function require(id) { + var require = function (id) { var index = module.dependencies[id]; var dependency = modules[index]; if (!dependency) From d79038460bbf9cf71bf8754a54a872c52c5e275e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:26:46 -0700 Subject: [PATCH 09/64] Support Node.js __filename __dirname If you can't beat 'em, join 'em. Also, some Node.js/npm packages depend on this and we would like them to work without modification, even if they're using bad nonstandard variable names that shouldn't have been made in the first place. --- boot.js | 6 ++++-- boot/preload-boilerplate.js | 6 ++++-- browser.js | 2 +- node.js | 2 +- require.js | 4 +++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/boot.js b/boot.js index b328b008..5e437903 100644 --- a/boot.js +++ b/boot.js @@ -169,7 +169,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { var __FILE__String = "__FILE__", DoubleUnderscoreString = "__", globalEvalConstantA = "(function ", - globalEvalConstantB = "(require, exports, module) {", + globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; Require.Compiler = function (config) { @@ -664,7 +664,9 @@ Require.makeRequire = function (config) { void 0, // this (defaults to global) makeRequire(topId), // require module.exports, // exports - module // module + module, // module + module.location, // __filename + module.directory // __dirname ); // EXTENSION diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 3f44e30c..1ced3618 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -222,7 +222,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { var __FILE__String = "__FILE__", DoubleUnderscoreString = "__", globalEvalConstantA = "(function ", - globalEvalConstantB = "(require, exports, module) {", + globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; Require.Compiler = function (config) { @@ -736,7 +736,9 @@ Require.makeRequire = function (config) { void 0, // this (defaults to global) makeRequire(topId), // require module.exports, // exports - module // module + module, // module + module.location, // __filename + module.directory // __dirname ); // EXTENSION diff --git a/browser.js b/browser.js index a4f12246..7b0dc0d6 100644 --- a/browser.js +++ b/browser.js @@ -84,7 +84,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { var __FILE__String = "__FILE__", DoubleUnderscoreString = "__", globalEvalConstantA = "(function ", - globalEvalConstantB = "(require, exports, module) {", + globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; Require.Compiler = function (config) { diff --git a/node.js b/node.js index b60019be..2ac23151 100644 --- a/node.js +++ b/node.js @@ -77,7 +77,7 @@ Require.read = function read(location) { // Can be overriden by the platform to make the engine aware of the source path. Uses sourceURL hack by default. Require.Compiler = function Compiler(config) { config.scope = config.scope || {}; - var names = ["require", "exports", "module"]; + var names = ["require", "exports", "module", "__filename", "__dirname"]; var scopeNames = Object.keys(config.scope); names.push.apply(names, scopeNames); return function (module) { diff --git a/require.js b/require.js index 9add2799..32dc2cb9 100644 --- a/require.js +++ b/require.js @@ -235,7 +235,9 @@ Require.makeRequire = function (config) { void 0, // this (defaults to global) makeRequire(topId), // require module.exports, // exports - module // module + module, // module + module.location, // __filename + module.directory // __dirname ); // EXTENSION From 4608598ee541fc4cd5f69a39e6607119350481fd Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:28:47 -0700 Subject: [PATCH 10/64] Delete module text after compile Since it just makes the application heavier at run time. I did have delusions about retaining it and scraping the text for debug purposes, but those illusions can be restored some other day. --- boot.js | 3 +++ boot/preload-boilerplate.js | 3 +++ browser.js | 3 +++ 3 files changed, 9 insertions(+) diff --git a/boot.js b/boot.js index 5e437903..8344c4e9 100644 --- a/boot.js +++ b/boot.js @@ -194,6 +194,9 @@ Require.Compiler = function (config) { try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); + if (!config.saveText) { + delete module.text; // save some space + } } catch (exception) { exception.message = exception.message + " in " + module.location; throw exception; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 1ced3618..f8cf0103 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -247,6 +247,9 @@ Require.Compiler = function (config) { try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); + if (!config.saveText) { + delete module.text; // save some space + } } catch (exception) { exception.message = exception.message + " in " + module.location; throw exception; diff --git a/browser.js b/browser.js index 7b0dc0d6..8fd65f11 100644 --- a/browser.js +++ b/browser.js @@ -109,6 +109,9 @@ Require.Compiler = function (config) { try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); + if (!config.saveText) { + delete module.text; // save some space + } } catch (exception) { exception.message = exception.message + " in " + module.location; throw exception; From 35360a3a682cdd3b050745a02151bc02c630b27b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:31:31 -0700 Subject: [PATCH 11/64] Use module.require that resolves relative ids Previously, each module descriptor shared the root package object. This made identity checks on the package object possible for test for package equivalence, but `require.location` is a better practice anyway. The advantage of this new approach is that `module.require` is the same as `require` and can be used to resolve module ids releative to the module. --- boot.js | 4 ++-- boot/preload-boilerplate.js | 4 ++-- require.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/boot.js b/boot.js index 8344c4e9..021876cb 100644 --- a/boot.js +++ b/boot.js @@ -485,7 +485,7 @@ Require.makeRequire = function (config) { id: id, extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION - require: require + require: makeRequire(id) }; } return modules[lookupId]; @@ -665,7 +665,7 @@ Require.makeRequire = function (config) { var returnValue = module.factory.call( // in the context of the module: void 0, // this (defaults to global) - makeRequire(topId), // require + module.require, // require module.exports, // exports module, // module module.location, // __filename diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index f8cf0103..6c8ab4ad 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -557,7 +557,7 @@ Require.makeRequire = function (config) { id: id, extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION - require: require + require: makeRequire(id) }; } return modules[lookupId]; @@ -737,7 +737,7 @@ Require.makeRequire = function (config) { var returnValue = module.factory.call( // in the context of the module: void 0, // this (defaults to global) - makeRequire(topId), // require + module.require, // require module.exports, // exports module, // module module.location, // __filename diff --git a/require.js b/require.js index 32dc2cb9..064b3b17 100644 --- a/require.js +++ b/require.js @@ -53,7 +53,7 @@ Require.makeRequire = function (config) { id: id, extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION - require: require + require: makeRequire(id) }; } return modules[lookupId]; @@ -233,7 +233,7 @@ Require.makeRequire = function (config) { var returnValue = module.factory.call( // in the context of the module: void 0, // this (defaults to global) - makeRequire(topId), // require + module.require, // require module.exports, // exports module, // module module.location, // __filename From 94d2df0d27c685288eb7d466545c72582db2b8ba Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:34:09 -0700 Subject: [PATCH 12/64] Add package to require errors --- boot.js | 1 + boot/preload-boilerplate.js | 1 + require.js | 1 + spec/not-found/package.json | 2 +- spec/not-found/program.js | 2 +- 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/boot.js b/boot.js index 021876cb..e00dcb68 100644 --- a/boot.js +++ b/boot.js @@ -630,6 +630,7 @@ Require.makeRequire = function (config) { error.message = ( "Can't require module " + JSON.stringify(module.id) + " via " + JSON.stringify(viaId) + + " in " + JSON.stringify(config.name || config.location) + " because " + error.message ); throw error; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 6c8ab4ad..c43bc8ae 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -702,6 +702,7 @@ Require.makeRequire = function (config) { error.message = ( "Can't require module " + JSON.stringify(module.id) + " via " + JSON.stringify(viaId) + + " in " + JSON.stringify(config.name || config.location) + " because " + error.message ); throw error; diff --git a/require.js b/require.js index 064b3b17..53d40efb 100644 --- a/require.js +++ b/require.js @@ -198,6 +198,7 @@ Require.makeRequire = function (config) { error.message = ( "Can't require module " + JSON.stringify(module.id) + " via " + JSON.stringify(viaId) + + " in " + JSON.stringify(config.name || config.location) + " because " + error.message ); throw error; diff --git a/spec/not-found/package.json b/spec/not-found/package.json index 0967ef42..95bea58b 100644 --- a/spec/not-found/package.json +++ b/spec/not-found/package.json @@ -1 +1 @@ -{} +{"name": "not-found-spec"} diff --git a/spec/not-found/program.js b/spec/not-found/program.js index b7f0940f..41453627 100644 --- a/spec/not-found/program.js +++ b/spec/not-found/program.js @@ -35,6 +35,6 @@ try { require("a"); } catch (exception) { test.print(exception.message); - test.assert(/Can't require module "a" via "program" because Can't XHR /.test(exception.message)); + test.assert(/Can't require module "a" via "program" in "not-found-spec" because Can't XHR /.test(exception.message)); } test.print('DONE', 'info'); From b0ad1f08e76d3e5bc66bdb23e884a98927acf9ba Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:34:46 -0700 Subject: [PATCH 13/64] Fix overlays + compilers and redirects These features previously did not compose. Now, compilers and redirects are captured from the configuration after overlays have been applied. --- boot.js | 6 ++++-- boot/preload-boilerplate.js | 6 ++++-- require.js | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/boot.js b/boot.js index e00dcb68..df459cb9 100644 --- a/boot.js +++ b/boot.js @@ -1009,8 +1009,6 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; - config.compilers = description.compilers; - config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; @@ -1131,6 +1129,10 @@ function configurePackage(location, description, parent) { }); config.mappings = mappings; + // compilers, translators, redirect patterns + config.compilers = description.compilers; + config.translators = description.translators; + return config; } diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index c43bc8ae..9c5b6333 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -1081,8 +1081,6 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; - config.compilers = description.compilers; - config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; @@ -1203,6 +1201,10 @@ function configurePackage(location, description, parent) { }); config.mappings = mappings; + // compilers, translators, redirect patterns + config.compilers = description.compilers; + config.translators = description.translators; + return config; } diff --git a/require.js b/require.js index 53d40efb..0bdea67c 100644 --- a/require.js +++ b/require.js @@ -577,8 +577,6 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; - config.compilers = description.compilers; - config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; @@ -699,6 +697,10 @@ function configurePackage(location, description, parent) { }); config.mappings = mappings; + // compilers, translators, redirect patterns + config.compilers = description.compilers; + config.translators = description.translators; + return config; } From db829eddda09faf87d810c340ab62ab9d76c8d4a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:36:37 -0700 Subject: [PATCH 14/64] Fix for setimmediate package The setimmediate package has a setImmediate.js main module, so we have to take care not to throw a warning about inconsistent module identifiers. --- boot.js | 5 ++++- boot/preload-boilerplate.js | 5 ++++- require.js | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/boot.js b/boot.js index df459cb9..65f5eca6 100644 --- a/boot.js +++ b/boot.js @@ -1077,7 +1077,10 @@ function configurePackage(location, description, parent) { location: config.location }; - if (description.name && description.name !== modules[""].redirect) { + if ( + description.name && + description.name.toLowerCase() !== modules[""].redirect.toLowerCase() + ) { modules[description.name] = { id: description.name, redirect: "", diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 9c5b6333..352a485e 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -1149,7 +1149,10 @@ function configurePackage(location, description, parent) { location: config.location }; - if (description.name && description.name !== modules[""].redirect) { + if ( + description.name && + description.name.toLowerCase() !== modules[""].redirect.toLowerCase() + ) { modules[description.name] = { id: description.name, redirect: "", diff --git a/require.js b/require.js index 0bdea67c..2ecfcae0 100644 --- a/require.js +++ b/require.js @@ -645,7 +645,10 @@ function configurePackage(location, description, parent) { location: config.location }; - if (description.name && description.name !== modules[""].redirect) { + if ( + description.name && + description.name.toLowerCase() !== modules[""].redirect.toLowerCase() + ) { modules[description.name] = { id: description.name, redirect: "", From 6a1d9dad20f905579bd34221aea31499e07549f6 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:38:28 -0700 Subject: [PATCH 15/64] Normalize redirects This has the happy side-benefit of increasing support for packages designed for Browserify, since the `browser` field gets converted to one or more `redirects`. --- boot.js | 6 +++--- boot/preload-boilerplate.js | 6 +++--- require.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/boot.js b/boot.js index 65f5eca6..70921b21 100644 --- a/boot.js +++ b/boot.js @@ -394,8 +394,8 @@ function getParams(scriptName) { }],[{},function (require, exports, module){ -// mr mini-url.js -// -------------- +// mr mini-url +// ----------- var head = document.querySelector("head"), @@ -1096,7 +1096,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: redirects[name], + redirect: normalizeId(resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 352a485e..c117c22f 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -447,8 +447,8 @@ function getParams(scriptName) { }],[{},function (require, exports, module){ -// mr mini-url.js -// -------------- +// mr mini-url +// ----------- var head = document.querySelector("head"), @@ -1168,7 +1168,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: redirects[name], + redirect: normalizeId(resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); diff --git a/require.js b/require.js index 2ecfcae0..df5f5330 100644 --- a/require.js +++ b/require.js @@ -664,7 +664,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: redirects[name], + redirect: normalizeId(resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); From 4f91611cb4fa0b66bf84a7453f0142e80fb30459 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:49:19 -0700 Subject: [PATCH 16/64] Remove support for directories configuration Existing packages in Node.js/npm ecosystem use a "directories" property to indicate that modules are in the "lib" directory, but Node.js does not read this configuration, so the user still has to include "lib" in module identifiers. This is not according to spec, but the behavior of ignoring the "directories" property has become a de-facto spec and these packages will not load with Mr if we persist in reading `directories.lib`. Also, there's no point in fighting the `node_modules` dependency packages directory configuration any longer. I have lost that fight. --- boot.js | 14 ++------------ boot/preload-boilerplate.js | 14 ++------------ require.js | 14 ++------------ spec/load-package-name/{ => node_modules}/a/a.js | 0 .../{ => node_modules}/a/package.json | 0 spec/load-package-name/package.json | 3 --- spec/main/program.js | 1 - .../{ => node_modules}/bar/package.json | 0 spec/named-mappings/{ => node_modules}/foo/foo.js | 0 .../{ => node_modules}/foo/package.json | 0 spec/named-mappings/package.json | 3 --- .../{ => node_modules}/bar/package.json | 0 spec/named-packages/{ => node_modules}/foo/foo.js | 0 .../{ => node_modules}/foo/package.json | 0 spec/named-packages/package.json | 3 --- .../child-package/child-module.js | 0 .../{ => node_modules}/child-package/package.json | 0 spec/named-parent-package/package.json | 3 --- .../{ => node_modules}/foo/barz.js | 0 .../{ => node_modules}/foo/package.json | 0 spec/redirects-package/package.json | 3 --- 21 files changed, 6 insertions(+), 52 deletions(-) rename spec/load-package-name/{ => node_modules}/a/a.js (100%) rename spec/load-package-name/{ => node_modules}/a/package.json (100%) rename spec/named-mappings/{ => node_modules}/bar/package.json (100%) rename spec/named-mappings/{ => node_modules}/foo/foo.js (100%) rename spec/named-mappings/{ => node_modules}/foo/package.json (100%) rename spec/named-packages/{ => node_modules}/bar/package.json (100%) rename spec/named-packages/{ => node_modules}/foo/foo.js (100%) rename spec/named-packages/{ => node_modules}/foo/package.json (100%) rename spec/named-parent-package/{ => node_modules}/child-package/child-module.js (100%) rename spec/named-parent-package/{ => node_modules}/child-package/package.json (100%) rename spec/redirects-package/{ => node_modules}/foo/barz.js (100%) rename spec/redirects-package/{ => node_modules}/foo/package.json (100%) diff --git a/boot.js b/boot.js index 70921b21..a8a3bcc3 100644 --- a/boot.js +++ b/boot.js @@ -457,8 +457,7 @@ Require.makeRequire = function (config) { // Configuration defaults: config = config || {}; config.location = URL.resolve(config.location || Require.getLocation(), "./"); - config.lib = URL.resolve(config.location, config.lib || "./"); - config.paths = config.paths || [config.lib]; + config.paths = config.paths || [config.location]; config.mappings = config.mappings || {}; // EXTENSION config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; config.makeLoader = config.makeLoader || Require.makeLoader; @@ -1052,16 +1051,7 @@ function configurePackage(location, description, parent) { }); delete description.overlay; - // directories - description.directories = description.directories || {}; - description.directories.lib = - description.directories.lib === void 0 ? "./" : description.directories.lib; - var lib = description.directories.lib; - // lib - config.lib = URL.resolve(location, "./" + lib); - var packagesDirectory = description.directories.packages || "node_modules"; - packagesDirectory = URL.resolve(location, packagesDirectory + "/"); - config.packagesDirectory = packagesDirectory; + config.packagesDirectory = URL.resolve(location, "node_modules/"); // The default "main" module of a package has the same name as the // package. diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index c117c22f..0f88b464 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -529,8 +529,7 @@ Require.makeRequire = function (config) { // Configuration defaults: config = config || {}; config.location = URL.resolve(config.location || Require.getLocation(), "./"); - config.lib = URL.resolve(config.location, config.lib || "./"); - config.paths = config.paths || [config.lib]; + config.paths = config.paths || [config.location]; config.mappings = config.mappings || {}; // EXTENSION config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; config.makeLoader = config.makeLoader || Require.makeLoader; @@ -1124,16 +1123,7 @@ function configurePackage(location, description, parent) { }); delete description.overlay; - // directories - description.directories = description.directories || {}; - description.directories.lib = - description.directories.lib === void 0 ? "./" : description.directories.lib; - var lib = description.directories.lib; - // lib - config.lib = URL.resolve(location, "./" + lib); - var packagesDirectory = description.directories.packages || "node_modules"; - packagesDirectory = URL.resolve(location, packagesDirectory + "/"); - config.packagesDirectory = packagesDirectory; + config.packagesDirectory = URL.resolve(location, "node_modules/"); // The default "main" module of a package has the same name as the // package. diff --git a/require.js b/require.js index df5f5330..1d5f7ab2 100644 --- a/require.js +++ b/require.js @@ -25,8 +25,7 @@ Require.makeRequire = function (config) { // Configuration defaults: config = config || {}; config.location = URL.resolve(config.location || Require.getLocation(), "./"); - config.lib = URL.resolve(config.location, config.lib || "./"); - config.paths = config.paths || [config.lib]; + config.paths = config.paths || [config.location]; config.mappings = config.mappings || {}; // EXTENSION config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; config.makeLoader = config.makeLoader || Require.makeLoader; @@ -620,16 +619,7 @@ function configurePackage(location, description, parent) { }); delete description.overlay; - // directories - description.directories = description.directories || {}; - description.directories.lib = - description.directories.lib === void 0 ? "./" : description.directories.lib; - var lib = description.directories.lib; - // lib - config.lib = URL.resolve(location, "./" + lib); - var packagesDirectory = description.directories.packages || "node_modules"; - packagesDirectory = URL.resolve(location, packagesDirectory + "/"); - config.packagesDirectory = packagesDirectory; + config.packagesDirectory = URL.resolve(location, "node_modules/"); // The default "main" module of a package has the same name as the // package. diff --git a/spec/load-package-name/a/a.js b/spec/load-package-name/node_modules/a/a.js similarity index 100% rename from spec/load-package-name/a/a.js rename to spec/load-package-name/node_modules/a/a.js diff --git a/spec/load-package-name/a/package.json b/spec/load-package-name/node_modules/a/package.json similarity index 100% rename from spec/load-package-name/a/package.json rename to spec/load-package-name/node_modules/a/package.json diff --git a/spec/load-package-name/package.json b/spec/load-package-name/package.json index ef05b20e..2c63c085 100644 --- a/spec/load-package-name/package.json +++ b/spec/load-package-name/package.json @@ -1,5 +1,2 @@ { - "directories": { - "packages": "." - } } diff --git a/spec/main/program.js b/spec/main/program.js index 317448c0..339ee91d 100644 --- a/spec/main/program.js +++ b/spec/main/program.js @@ -1,6 +1,5 @@ var test = require('test'); -debugger; test.assert(require('dot-slash') === 10, 'main with "./"'); test.assert(require('js-ext') === 20, 'main with ".js" extension'); test.assert(require('no-ext') === 30, 'main with no extension'); diff --git a/spec/named-mappings/bar/package.json b/spec/named-mappings/node_modules/bar/package.json similarity index 100% rename from spec/named-mappings/bar/package.json rename to spec/named-mappings/node_modules/bar/package.json diff --git a/spec/named-mappings/foo/foo.js b/spec/named-mappings/node_modules/foo/foo.js similarity index 100% rename from spec/named-mappings/foo/foo.js rename to spec/named-mappings/node_modules/foo/foo.js diff --git a/spec/named-mappings/foo/package.json b/spec/named-mappings/node_modules/foo/package.json similarity index 100% rename from spec/named-mappings/foo/package.json rename to spec/named-mappings/node_modules/foo/package.json diff --git a/spec/named-mappings/package.json b/spec/named-mappings/package.json index c89dc09d..a3e46491 100644 --- a/spec/named-mappings/package.json +++ b/spec/named-mappings/package.json @@ -2,8 +2,5 @@ "mappings": { "bar": {"name": "bar"}, "foo": {"name": "foo"} - }, - "directories": { - "packages": "." } } diff --git a/spec/named-packages/bar/package.json b/spec/named-packages/node_modules/bar/package.json similarity index 100% rename from spec/named-packages/bar/package.json rename to spec/named-packages/node_modules/bar/package.json diff --git a/spec/named-packages/foo/foo.js b/spec/named-packages/node_modules/foo/foo.js similarity index 100% rename from spec/named-packages/foo/foo.js rename to spec/named-packages/node_modules/foo/foo.js diff --git a/spec/named-packages/foo/package.json b/spec/named-packages/node_modules/foo/package.json similarity index 100% rename from spec/named-packages/foo/package.json rename to spec/named-packages/node_modules/foo/package.json diff --git a/spec/named-packages/package.json b/spec/named-packages/package.json index 5e57aa04..3c665340 100644 --- a/spec/named-packages/package.json +++ b/spec/named-packages/package.json @@ -1,7 +1,4 @@ { - "directories": { - "packages": "." - }, "dependencies": { "foo": "*", "bar": "*" diff --git a/spec/named-parent-package/child-package/child-module.js b/spec/named-parent-package/node_modules/child-package/child-module.js similarity index 100% rename from spec/named-parent-package/child-package/child-module.js rename to spec/named-parent-package/node_modules/child-package/child-module.js diff --git a/spec/named-parent-package/child-package/package.json b/spec/named-parent-package/node_modules/child-package/package.json similarity index 100% rename from spec/named-parent-package/child-package/package.json rename to spec/named-parent-package/node_modules/child-package/package.json diff --git a/spec/named-parent-package/package.json b/spec/named-parent-package/package.json index 2a5c4e65..0dc1664a 100644 --- a/spec/named-parent-package/package.json +++ b/spec/named-parent-package/package.json @@ -4,8 +4,5 @@ "main": "program", "dependencies": { "child-package": "0.0.0" - }, - "directories": { - "packages": "." } } diff --git a/spec/redirects-package/foo/barz.js b/spec/redirects-package/node_modules/foo/barz.js similarity index 100% rename from spec/redirects-package/foo/barz.js rename to spec/redirects-package/node_modules/foo/barz.js diff --git a/spec/redirects-package/foo/package.json b/spec/redirects-package/node_modules/foo/package.json similarity index 100% rename from spec/redirects-package/foo/package.json rename to spec/redirects-package/node_modules/foo/package.json diff --git a/spec/redirects-package/package.json b/spec/redirects-package/package.json index 1dfcfc88..aa31120c 100644 --- a/spec/redirects-package/package.json +++ b/spec/redirects-package/package.json @@ -2,8 +2,5 @@ "name": "redirects-package", "dependencies": { "foo": "*" - }, - "directories": { - "packages": "." } } From fba1c6a051301a452a0e90175a6316e3cf1e696f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 6 Aug 2013 17:15:18 -0700 Subject: [PATCH 17/64] Build boot.js script using a build script :warning: This includes backward incompatible changes that impact Montage and Mop. Changes to those packages in the corresponding `build` branch must be coordinated and released in a future, backward-incompatible release. Some care will be needed to ensure that `mop` in particular depends on compatible versions of `mr` and `montage`. This introduces a program, `mrs`, that can create stand-alone scripts from modules that only need the ability to produce and consume CommonJS modules with `require` and `exports` or `module.exports`, and depend only on a static working set. The resulting script has very little overhead and is based on the build products of Browserify. `mrs` is sufficient to build `mr/boot.js` (previously `bootstrap.js`)from the CommonJS modules in the `boot` directory. Run `npm run build`to produce a new revision of `boot.js` and `boot/preload-boilerplate.js`. This aleviates the need to perform script injection in the bootstrapping process, at the expense of introducing a build step for the bootstrapper. This change also allows a greater level of code reuse between Mr and Montage in their bootstrapping processes. This also obviates the need to embed UMD boilerplate in these modules, since they will always be used as CommonJS modules, either as sources for the build or as instruments of Node.js. Since `mrs` embeds the entirety of `boot.js`'s dependencies, it is no longer necessary to use a subrepository for these dependencies and no longer necessary to use hard-coded relative locations for these dependencies in `package.json`. These dependencies may now be installed by NPM and the maintainers of Mr and its dependencies no longer need to coordinate or hard-code their dependency trees. This is important because Q will soon be breaking up into smaller modules and packages. The `mini-url` module may now be a CommonJS module shared by Montage and Mr. Within Montage and Mr, it may be required by the name `url`. A mapping in `package.json` directs the module loader to use `mini-url.js` instead. On the Node.js side, where `mini-url.js` would not work, we fall back to Node.js's `url` module, which has an API that is a strict super-set of `mini-url.js`'s. Invert package nesting in browser boot: Previously, the root package was the module loader itself. Now, to match what is likely in practice, the application loads first, then the module loader, then promises. --- adhoc.js | 2 +- bin/{mr => mr.js} | 2 +- bin/mrs.js | 31 + boot.js | 3117 ++++++++++++++++++++++++++++++++ boot/README.md | 109 ++ boot/boilerplate.js | 30 + boot/browser.js | 45 + boot/preload-boilerplate.js | 3189 +++++++++++++++++++++++++++++++++ boot/preload-entry.js | 8 + boot/preload.js | 35 + boot/script-injection.js | 14 + boot/script-params.js | 65 + bootstrap-node.js | 89 - bootstrap.js | 262 --- browser.js | 65 +- build.js | 63 + mini-url.js | 28 + node.js | 74 +- package.json | 26 +- packages/q/.gitignore | 10 - packages/q/LICENSE | 19 - packages/q/package.json | 74 - packages/q/q.js | 1937 -------------------- packages/q/queue.js | 35 - packages/q/spec/q-spec.html | 52 - packages/q/spec/q-spec.js | 2490 ------------------------- packages/q/spec/queue-spec.js | 180 -- require.js | 1577 ++++++++-------- spec/node-spec.js | 13 +- spec/production/program.js | 1 - spec/read/package.json | 2 +- spec/require-spec.js | 17 +- spec/run.html | 2 +- spec/sandbox/package.json | 2 +- 34 files changed, 7644 insertions(+), 6021 deletions(-) rename bin/{mr => mr.js} (62%) create mode 100755 bin/mrs.js create mode 100644 boot.js create mode 100644 boot/README.md create mode 100644 boot/boilerplate.js create mode 100644 boot/browser.js create mode 100644 boot/preload-boilerplate.js create mode 100644 boot/preload-entry.js create mode 100644 boot/preload.js create mode 100644 boot/script-injection.js create mode 100644 boot/script-params.js delete mode 100644 bootstrap-node.js delete mode 100644 bootstrap.js create mode 100644 build.js create mode 100644 mini-url.js delete mode 100644 packages/q/.gitignore delete mode 100644 packages/q/LICENSE delete mode 100644 packages/q/package.json delete mode 100644 packages/q/q.js delete mode 100644 packages/q/queue.js delete mode 100644 packages/q/spec/q-spec.html delete mode 100644 packages/q/spec/q-spec.js delete mode 100644 packages/q/spec/queue-spec.js diff --git a/adhoc.js b/adhoc.js index 2c5200f6..f4511c3f 100644 --- a/adhoc.js +++ b/adhoc.js @@ -1,5 +1,5 @@ -var URL = require("mini-url"); +var URL = require("url"); var QS = require("qs"); var a = document.createElement("a"); diff --git a/bin/mr b/bin/mr.js similarity index 62% rename from bin/mr rename to bin/mr.js index 635bd9d0..9416c8f4 100755 --- a/bin/mr +++ b/bin/mr.js @@ -1,2 +1,2 @@ #!/usr/bin/env node --harmony_weakmaps --harmony_proxies -require("../bootstrap-node"); +require("../node").boot().done(); diff --git a/bin/mrs.js b/bin/mrs.js new file mode 100755 index 00000000..bcc6d701 --- /dev/null +++ b/bin/mrs.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +var optimist = require("optimist"); +var build = require("../build"); + +var argv = optimist + .default("execute", "") + .alias("e", "execute") + .alias("h", "help") + .argv; + +function usage() { + console.log("Usage: mrs [-e ]"); + console.log(""); + console.log(" Creates a - - - - - - - - - - - - - - - - diff --git a/packages/q/spec/q-spec.js b/packages/q/spec/q-spec.js deleted file mode 100644 index 450685c8..00000000 --- a/packages/q/spec/q-spec.js +++ /dev/null @@ -1,2490 +0,0 @@ -"use strict"; -/*jshint newcap: false*/ -/*global Q: true, describe: false, it: false, expect: false, beforeEach: false, - afterEach: false, require: false, jasmine: false, waitsFor: false, - runs: false */ - -if (typeof Q === "undefined" && typeof require !== "undefined") { - // For Node compatibility. - global.Q = require("../q"); - require("./lib/jasmine-promise"); -} - -var REASON = "this is not an error, but it might show up in the console"; - -// In browsers that support strict mode, it'll be `undefined`; otherwise, the global. -var calledAsFunctionThis = (function () { return this; }()); - -afterEach(function () { - Q.onerror = null; -}); - -describe("computing sum of integers using promises", function() { - it("should compute correct result without blowing stack", function () { - var array = []; - var iters = 1000; - for (var i = 1; i <= iters; i++) { - array.push(i); - } - - var pZero = Q.fulfill(0); - var result = array.reduce(function (promise, nextVal) { - return promise.then(function (currentVal) { - return Q.fulfill(currentVal + nextVal); - }); - }, pZero); - - return result.then(function (value) { - expect(value).toEqual(iters * (iters + 1) / 2); - }); - }); -}); - -describe("Q function", function () { - it("should result in a fulfilled promise when given a value", function () { - expect(Q(5).isFulfilled()).toBe(true); - }); - - it("should be the identity when given promise", function () { - var f = Q.fulfill(5); - var r = Q.reject(new Error("aaargh")); - var p = Q.promise(function () { }); - - expect(Q(f)).toBe(f); - expect(Q(r)).toBe(r); - expect(Q(p)).toBe(p); - }); -}); - -describe("defer and when", function () { - - it("resolve before when", function () { - var turn = 0; - var deferred = Q.defer(); - deferred.resolve(10); - var promise = Q.when(deferred.promise, function (value) { - expect(turn).toEqual(1); - expect(value).toEqual(10); - }); - turn++; - return promise; - }); - - it("reject before when", function () { - var turn = 0; - var deferred = Q.defer(); - deferred.reject(-1); - var promise = Q.when(deferred.promise, function () { - expect(true).toBe(false); - }, function (value) { - expect(turn).toEqual(1); - expect(value).toEqual(-1); - }); - turn++; - return promise; - }); - - it("when before resolve", function () { - var turn = 0; - var deferred = Q.defer(); - var promise = deferred.promise.then(function (value) { - expect(turn).toEqual(2); - expect(value).toEqual(10); - turn++; - }); - Q.nextTick(function () { - expect(turn).toEqual(1); - deferred.resolve(10); - turn++; - }); - turn++; - return promise; - }); - - it("when before reject", function () { - var turn = 0; - var deferred = Q.defer(); - var promise = deferred.promise.then(function () { - expect(true).toBe(false); - }, function (value) { - expect(turn).toEqual(2); - expect(value).toEqual(-1); - turn++; - }); - Q.nextTick(function () { - expect(turn).toEqual(1); - deferred.reject(-1); - turn++; - }); - turn++; - return promise; - }); - - it("resolves multiple observers", function (done) { - var nextTurn = false; - - var resolution = "Taram pam param!"; - var deferred = Q.defer(); - var count = 10; - var i = 0; - - function resolve(value) { - i++; - expect(value).toBe(resolution); - expect(nextTurn).toBe(true); - if (i === count) { - done(); - } - } - - while (++i <= count) { - Q.when(deferred.promise, resolve); - } - - deferred.resolve(resolution); - i = 0; - nextTurn = true; - }); - - it("observers called even after throw", function () { - var threw = false; - var deferred = Q.defer(); - Q.when(deferred.promise, function () { - threw = true; - throw new Error(REASON); - }); - var promise = Q.when(deferred.promise, function (value) { - expect(value).toEqual(10); - }, function () { - expect("not").toEqual("here"); - }); - deferred.resolve(10); - return promise; - }); - - it("returns `undefined` from the deferred's methods", function () { - expect(Q.defer().resolve()).toBe(undefined); - expect(Q.defer().reject()).toBe(undefined); - }); - -}); - -describe("always next tick", function () { - - it("generated by `resolve`", function () { - var turn = 0; - var promise = Q.when(Q(), function () { - expect(turn).toEqual(1); - }); - turn++; - return promise; - }); - - it("generated by `reject`", function () { - var turn = 0; - var promise = Q.when(Q.reject(), function () { - expect(true).toBe(false); - }, function () { - expect(turn).toEqual(1); - }); - turn++; - return promise; - }); - -}); - -describe("progress", function () { - - it("calls a single progress listener", function () { - var progressed = false; - var deferred = Q.defer(); - - var promise = Q.when( - deferred.promise, - function () { - expect(progressed).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed = true; - } - ); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("calls multiple progress listeners", function () { - var progressed1 = false; - var progressed2 = false; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(progressed1).toBe(true); - expect(progressed2).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed1 = true; - } - ); - Q.when(deferred.promise, null, null, function () { - progressed2 = true; - }); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("calls all progress listeners even if one throws", function () { - var progressed1 = false; - var progressed2 = false; - var progressed3 = false; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(progressed1).toBe(true); - expect(progressed2).toBe(true); - expect(progressed3).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed1 = true; - } - ); - - Q.onerror = function () { }; - - Q.when(deferred.promise, null, null, function () { - progressed2 = true; - throw new Error("just a test, ok if it shows up in the console"); - }); - Q.when(deferred.promise, null, null, function () { - progressed3 = true; - }); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("calls the progress listener even if later rejected", function () { - var progressed = false; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(true).toBe(false); - }, - function () { - expect(progressed).toEqual(true); - }, - function () { - progressed = true; - } - ); - - deferred.notify(); - deferred.reject(); - - return promise; - }); - - it("calls the progress listener with the notify values", function () { - var progressValues = []; - var desiredProgressValues = [{}, {}, "foo", 5]; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - for (var i = 0; i < desiredProgressValues.length; ++i) { - var desired = desiredProgressValues[i]; - var actual = progressValues[i]; - expect(actual).toBe(desired); - } - }, - function () { - expect(true).toBe(false); - }, - function (value) { - progressValues.push(value); - } - ); - - for (var i = 0; i < desiredProgressValues.length; ++i) { - deferred.notify(desiredProgressValues[i]); - } - deferred.resolve(); - - return promise; - }); - - it("does not call the progress listener if notify is called after fulfillment", function () { - var deferred = Q.defer(); - var called = false; - - Q.when(deferred.promise, null, null, function () { - called = true; - }); - - deferred.resolve(); - deferred.notify(); - - return Q.delay(10).then(function () { - expect(called).toBe(false); - }); - }); - - it("does not call the progress listener if notify is called after rejection", function () { - var deferred = Q.defer(); - var called = false; - - Q.when(deferred.promise, null, null, function () { - called = true; - }); - - deferred.reject(); - deferred.notify(); - - return Q.delay(10).then(function () { - expect(called).toBe(false); - }); - }); - - it("should not save and re-emit progress notifications", function () { - var deferred = Q.defer(); - var progressValues = []; - - deferred.notify(1); - - var promise = Q.when( - deferred.promise, - function () { - expect(progressValues).toEqual([2]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - deferred.notify(2); - deferred.resolve(); - - return promise; - }); - - it("should allow attaching progress listeners w/ .progress", function () { - var progressed = false; - var deferred = Q.defer(); - - deferred.promise.progress(function () { - progressed = true; - }); - - deferred.notify(); - deferred.resolve(); - - return deferred.promise; - }); - - it("should allow attaching progress listeners w/ Q.progress", function () { - var progressed = false; - var deferred = Q.defer(); - - Q.progress(deferred.promise, function () { - progressed = true; - }); - - deferred.notify(); - deferred.resolve(); - - return deferred.promise; - }); - - it("should call the progress listener with undefined context", function () { - var progressed = false; - var progressContext = {}; - var deferred = Q.defer(); - var promise = Q.when( - deferred.promise, - function () { - expect(progressed).toBe(true); - expect(progressContext).toBe(calledAsFunctionThis); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed = true; - progressContext = this; - } - ); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("should forward only the first notify argument to listeners", function () { - var progressValueArrays = []; - var deferred = Q.defer(); - - var promise = Q.when( - deferred.promise, - function () { - expect(progressValueArrays).toEqual([[1], [2], [4]]); - }, - function () { - expect(true).toBe(false); - }, - function () { - var args = Array.prototype.slice.call(arguments); - progressValueArrays.push(args); - } - ); - - deferred.notify(1); - deferred.notify(2, 3); - deferred.notify(4, 5, 6); - deferred.resolve(); - - return promise; - }); - - it("should work with .then as well", function () { - var progressed = false; - var deferred = Q.defer(); - - var promise = deferred.promise.then( - function () { - expect(progressed).toBe(true); - }, - function () { - expect(true).toBe(false); - }, - function () { - progressed = true; - } - ); - - deferred.notify(); - deferred.resolve(); - - return promise; - }); - - it("should re-throw all errors thrown by listeners to Q.onerror", function () { - var theError = new Error("boo!"); - - var def = Q.defer(); - def.promise.progress(function () { - throw theError; - }); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(error).toBe(theError); - deferred.resolve(); - }; - Q.delay(100).then(deferred.reject); - - def.notify(); - - return deferred.promise; - }); -}); - -describe("promises for objects", function () { - - describe("get", function () { - - it("fulfills a promise", function () { - var deferred = Q.defer(); - deferred.resolve({a: 1}); - return deferred.promise.get("a") - .then(function (a) { - expect(a).toBe(1); - }); - }); - - it("propagates a rejection", function () { - var exception = new Error("boo!"); - return Q.fcall(function () { - throw exception; - }) - .get("a") - .then(function () { - expect("be").toBe("not to be"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("set", function () { - - it("fulfills a promise", function () { - var object = {}; - return Q(object) - .set("a", 1) - .then(function (result) { - expect(result).toBe(undefined); - expect(object.a).toBe(1); - }); - }); - - it("propagates a rejection", function () { - var exception = new Error("Gah!"); - return Q.reject(exception) - .set("a", 1) - .then(function () { - expect("frozen over").toBe("quite warm"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("del", function () { - - it("fulfills a promise", function () { - var object = {a: 10}; - return Q.fcall(function () { - return object; - }) - .del("a") - .then(function (result) { - expect("a" in object).toBe(false); - expect(result).toBe(void 0); - }, function () { - expect("up").toBe("down"); - }); - }); - - it("propagates a rejection", function () { - var exception = new Error("hah-hah"); - return Q.fcall(function () { - throw exception; - }) - .del("a") - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("post", function () { - - it("fulfills a promise", function () { - var subject = { - a: function a(value) { - this._a = value; - return 1 + value; - } - }; - return Q.when(Q.post(subject, "a", [1]), function (two) { - expect(subject._a).toBe(1); - expect(two).toBe(2); - }); - }); - - it("works as apply when given no name", function () { - return Q(function (a, b, c) { - return a + b + c; - }) - .post(undefined, [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - }); - - describe("send", function () { - - it("fulfills a promise", function () { - var foo; - var subject = { - foo: function (_bar) { - return _bar; - }, - bar: function (_foo, _bar) { - foo = _foo; - return this.foo(_bar); - } - }; - return Q.send(subject, "bar", 1, 2) - .then(function (two) { - expect(foo).toEqual(1); - expect(two).toEqual(2); - }); - }); - - it("is rejected for undefined method", function () { - var subject = {}; - return Q(subject) - .send("foo") - .then(function () { - expect("here").toEqual("not here"); - }, function () { - }); - }); - - it("is rejected for undefined object", function () { - return Q() - .send("foo") - .then(function () { - expect("here").toEqual("not here"); - }, function () { - }); - }); - - }); - - describe("keys", function () { - - function Klass (a, b) { - this.a = a; - this.b = b; - } - Klass.prototype.notOwn = 1; - - it("fulfills a promise", function () { - return Q.keys(new Klass(10, 20)) - .then(function (keys) { - expect(keys.sort()).toEqual(["a", "b"]); - }); - }); - - }); - -}); - -describe("promises for functions", function () { - - describe("fapply", function () { - it("fulfills a promise using arguments", function () { - return Q(function (a, b, c) { - return a + b + c; - }) - .fapply([1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - }); - - describe("fcall", function () { - it("fulfills a promise using arguments", function () { - return Q(function (a, b, c) { - return a + b + c; - }) - .fcall(1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - }); - - describe("fbind", function () { - - it("accepts a promise for a function", function () { - return Q.fbind(Q(function (high, low) { - return high - low; - })) - (2, 1) - .then(function (difference) { - expect(difference).toEqual(1); - }); - }); - - it("chains partial application on a promise for a function", function () { - return Q(function (a, b) { - return a * b; - }) - .fbind(2)(3) - .then(function (product) { - expect(product).toEqual(6); - }); - }); - - it("returns a fulfilled promise", function () { - var result = {}; - var bound = Q.fbind(function () { - return result; - }); - return bound() - .then(function (_result) { - expect(_result).toBe(result); - }); - }); - - it("returns a rejected promise from a thrown error", function () { - var exception = new Error("Boo!"); - var bound = Q.fbind(function () { - throw exception; - }); - return bound() - .then(function () { - expect("flying pigs").toBe("swillin' pigs"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("passes arguments through", function () { - var x = {}, y = {}; - var bound = Q.fbind(function (a, b) { - expect(a).toBe(x); - expect(b).toBe(y); - }); - return bound(x, y); - }); - - it("passes and also partially applies arguments", function () { - var x = {}, y = {}; - var bound = Q.fbind(function (a, b) { - expect(a).toBe(x); - expect(b).toBe(y); - }, x); - return bound(y); - }); - - it("doesn't bind `this`", function () { - var theThis = { me: "this" }; - var bound = Q.fbind(function () { - expect(this).toBe(theThis); - }); - - return bound.call(theThis); - }); - - }); - -}); - -describe("inspect", function () { - - it("for a fulfilled promise", function () { - expect(Q(10).inspect()).toEqual({ - state: "fulfilled", - value: 10 - }); - }); - - it("for a rejected promise", function () { - var error = new Error("In your face."); - var rejected = Q.reject(error); - expect(rejected.inspect()).toEqual({ - state: "rejected", - reason: error - }); - }); - - it("for a pending, unresolved promise", function () { - var pending = Q.defer().promise; - expect(pending.inspect()).toEqual({ state: "pending" }); - }); - - it("for a promise resolved to a rejected promise", function () { - var deferred = Q.defer(); - var error = new Error("Rejected!"); - var rejected = Q.reject(error); - deferred.resolve(rejected); - - expect(deferred.promise.inspect()).toEqual({ - state: "rejected", - reason: error - }); - }); - - it("for a promise resolved to a fulfilled promise", function () { - var deferred = Q.defer(); - var fulfilled = Q(10); - deferred.resolve(fulfilled); - - expect(deferred.promise.inspect()).toEqual({ - state: "fulfilled", - value: 10 - }); - }); - - it("for a promise resolved to a pending promise", function () { - var a = Q.defer(); - var b = Q.defer(); - a.resolve(b.promise); - - expect(a.promise.inspect()).toEqual({ state: "pending" }); - }); - -}); - -describe("promise states", function () { - - it("of fulfilled value", function () { - expect(Q.isFulfilled(void 0)).toBe(true); - expect(Q.isRejected(false)).toBe(false); - expect(Q.isPending(true)).toBe(false); - }); - - it("of fulfillment", function () { - var promise = Q(10); - expect(Q.isFulfilled(promise)).toBe(true); - expect(promise.isFulfilled()).toBe(true); - expect(Q.isRejected(promise)).toBe(false); - expect(promise.isRejected()).toBe(false); - expect(Q.isPending(promise)).toBe(false); - expect(promise.isPending()).toBe(false); - }); - - it("of rejection", function () { - var error = new Error("Oh, snap."); - var promise = Q.reject(error); - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(true); - expect(promise.isPending()).toBe(false); - }); - - it("of rejection with a falsy value", function () { - var promise = Q.reject(undefined); - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(true); - expect(promise.isPending()).toBe(false); - }); - - it("of deferred", function () { - var deferred = Q.defer(); - var promise = deferred.promise; - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(false); - expect(promise.isPending()).toBe(true); - }); - - it("of deferred rejection", function () { - var deferred = Q.defer(); - var rejection = Q.reject(new Error("Rejected!")); - deferred.resolve(rejection); - var promise = deferred.promise; - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(true); - expect(promise.isPending()).toBe(false); - }); - - it("of deferred fulfillment", function () { - var deferred = Q.defer(); - deferred.resolve(10); - var promise = deferred.promise; - expect(promise.isFulfilled()).toBe(true); - expect(promise.isRejected()).toBe(false); - expect(promise.isPending()).toBe(false); - }); - - it("of deferred deferred", function () { - var a = Q.defer(); - var b = Q.defer(); - a.resolve(b.promise); - var promise = a.promise; - expect(promise.isFulfilled()).toBe(false); - expect(promise.isRejected()).toBe(false); - expect(promise.isPending()).toBe(true); - }); - - it("of isFulfilled side effects", function () { - var deferred = Q.defer(); - var finished = false; - - waitsFor(function () { - return finished; - }); - - var parentPromise = deferred.promise; - - var childPromise = parentPromise.then(function () { - expect(parentPromise.isFulfilled()).toBe(true); - expect(childPromise.isFulfilled()).toBe(false); - - return parentPromise.then(function (value) { - finished = true; - return value + 1; - }); - }); - - deferred.resolve(1); - - runs(function () { - expect(childPromise.isPending()).toBe(false); - expect(childPromise.isRejected()).toBe(false); - expect(childPromise.isFulfilled()).toBe(true); - expect(childPromise.inspect().value).toBe(2); - }); - }); - -}); - -describe("propagation", function () { - - it("propagate through then with no callback", function () { - return Q(10) - .then() - .then(function (ten) { - expect(ten).toBe(10); - }); - }); - - it("propagate through then with modifying callback", function () { - return Q(10) - .then(function (ten) { - return ten + 10; - }) - .then(function (twen) { - expect(twen).toBe(20); - }); - }); - - it("errback recovers from exception", function () { - var error = new Error("Bah!"); - return Q.reject(error) - .then(null, function (_error) { - expect(_error).toBe(error); - return 10; - }) - .then(function (value) { - expect(value).toBe(10); - }); - }); - - it("rejection propagates through then with no errback", function () { - var error = new Error("Foolish mortals!"); - return Q.reject(error) - .then() - .then(null, function (_error) { - expect(_error).toBe(error); - }); - }); - - it("rejection intercepted and rethrown", function () { - var error = new Error("Foolish mortals!"); - var nextError = new Error("Silly humans!"); - return Q.reject(error) - .fail(function () { - throw nextError; - }) - .then(null, function (_error) { - expect(_error).toBe(nextError); - }); - }); - - it("resolution is forwarded through deferred promise", function () { - var a = Q.defer(); - var b = Q.defer(); - a.resolve(b.promise); - b.resolve(10); - return a.promise.then(function (eh) { - expect(eh).toEqual(10); - }); - }); - - it("should propagate progress by default", function () { - var d = Q.defer(); - - var progressValues = []; - var promise = d.promise - .then() - .then( - function () { - expect(progressValues).toEqual([1]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - d.notify(1); - d.resolve(); - - return promise; - }); - - it("should allow translation of progress in the progressback", function () { - var d = Q.defer(); - - var progressValues = []; - var promise = d.promise - .progress(function (p) { - return p + 5; - }) - .then( - function () { - expect(progressValues).toEqual([10]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - d.notify(5); - d.resolve(); - - return promise; - }); - - - it("should stop progress propagation if an error is thrown", function () { - var def = Q.defer(); - var p2 = def.promise.progress(function () { - throw new Error("boo!"); - }); - - Q.onerror = function () { /* just swallow it for this test */ }; - - var progressValues = []; - var result = p2.then( - function () { - expect(progressValues).toEqual([]); - }, - function () { - expect(true).toBe(false); - }, - function (progressValue) { - progressValues.push(progressValue); - } - ); - - def.notify(); - def.resolve(); - return result; - }); -}); - -describe("all", function () { - it("fulfills when passed an empty array", function () { - return Q.all([]); - }); - - it("rejects after any constituent promise is rejected", function () { - var toResolve = Q.defer(); // never resolve - var toReject = Q.defer(); - var promises = [toResolve.promise, toReject.promise]; - var promise = Q.all(promises); - - toReject.reject(new Error("Rejected")); - - return Q.delay(250) - .then(function () { - expect(promise.isRejected()).toBe(true); - }) - .timeout(1000); - }); - - it("resolves foreign thenables", function () { - var normal = Q(1); - var foreign = { then: function (f) { f(2); } }; - - return Q.all([normal, foreign]) - .then(function (result) { - expect(result).toEqual([1, 2]); - }); - }); - - it("fulfills when passed an sparse array", function () { - var toResolve = Q.defer(); - var promises = []; - promises[0] = Q(0); - promises[2] = toResolve.promise; - var promise = Q.all(promises); - - toResolve.resolve(2); - - return promise.then(function (result) { - expect(result).toEqual([0, void 0, 2]); - }); - }); - - it("modifies the input array", function () { - var input = [Q(0), Q(1)]; - - return Q.all(input).then(function (result) { - expect(result).toBe(input); - expect(input).toEqual([0, 1]); - }); - }); - - it("sends { index, value } progress updates", function () { - var deferred1 = Q.defer(); - var deferred2 = Q.defer(); - - var progressValues = []; - - Q.delay(50).then(function () { - deferred1.notify("a"); - }); - Q.delay(100).then(function () { - deferred2.notify("b"); - deferred2.resolve(); - }); - Q.delay(150).then(function () { - deferred1.notify("c"); - deferred1.resolve(); - }); - - return Q.all([deferred1.promise, deferred2.promise]).then( - function () { - expect(progressValues).toEqual([ - { index: 0, value: "a" }, - { index: 1, value: "b" }, - { index: 0, value: "c" } - ]); - }, - undefined, - function (progressValue) { - progressValues.push(progressValue); - } - ) - }); - -}); - -describe("allSettled", function () { - it("works on an empty array", function () { - return Q.allSettled([]) - .then(function (snapshots) { - expect(snapshots).toEqual([]); - }); - }); - - it("deals with a mix of non-promises and promises", function () { - return Q.allSettled([1, Q(2), Q.reject(3)]) - .then(function (snapshots) { - expect(snapshots).toEqual([ - { state: "fulfilled", value: 1 }, - { state: "fulfilled", value: 2 }, - { state: "rejected", reason: 3 } - ]); - }); - }); - - it("is settled after every constituent promise is settled", function () { - var toFulfill = Q.defer(); - var toReject = Q.defer(); - var promises = [toFulfill.promise, toReject.promise]; - var fulfilled; - var rejected; - - Q.fcall(function () { - toReject.reject(); - rejected = true; - }) - .delay(15) - .then(function () { - toFulfill.resolve(); - fulfilled = true; - }); - - return Q.allSettled(promises) - .then(function () { - expect(fulfilled).toBe(true); - expect(rejected).toBe(true); - }); - }); - - it("does not modify the input array", function () { - var input = [1, Q(2), Q.reject(3)]; - - return Q.allSettled(input) - .then(function (snapshots) { - expect(snapshots).not.toBe(input); - expect(snapshots).toEqual([ - { state: "fulfilled", value: 1 }, - { state: "fulfilled", value: 2 }, - { state: "rejected", reason: 3 } - ]); - }); - }); - -}); - -describe("spread", function () { - - it("spreads values across arguments", function () { - return Q.spread([1, 2, 3], function (a, b) { - expect(b).toBe(2); - }); - }); - - it("spreads promises for arrays across arguments", function () { - return Q([Q(10)]) - .spread(function (value) { - expect(value).toEqual(10); - }); - }); - - it("spreads arrays of promises across arguments", function () { - var deferredA = Q.defer(); - var deferredB = Q.defer(); - - var promise = Q.spread([deferredA.promise, deferredB.promise], - function (a, b) { - expect(a).toEqual(10); - expect(b).toEqual(20); - }); - - Q.delay(5).then(function () { - deferredA.resolve(10); - }); - Q.delay(10).then(function () { - deferredB.resolve(20); - }); - - return promise; - }); - - it("calls the errback when given a rejected promise", function () { - var err = new Error(); - return Q.spread([Q(10), Q.reject(err)], - function () { - expect(true).toBe(false); - }, - function (actual) { - expect(actual).toBe(err); - } - ); - }); - -}); - -describe("fin", function () { - - var exception1 = new Error("boo!"); - var exception2 = new TypeError("evil!"); - - describe("when the promise is fulfilled", function () { - - it("should call the callback", function () { - var called = false; - - return Q("foo") - .fin(function () { - called = true; - }) - .then(function () { - expect(called).toBe(true); - }); - }); - - it("should fulfill with the original value", function () { - return Q("foo") - .fin(function () { - return "bar"; - }) - .then(function (result) { - expect(result).toBe("foo"); - }); - }); - - describe("when the callback returns a promise", function () { - - describe("that is fulfilled", function () { - it("should fulfill with the original reason after that promise resolves", function () { - var promise = Q.delay(250); - - return Q("foo") - .fin(function () { - return promise; - }) - .then(function (result) { - expect(Q.isPending(promise)).toBe(false); - expect(result).toBe("foo"); - }); - }); - }); - - describe("that is rejected", function () { - it("should reject with this new rejection reason", function () { - return Q("foo") - .fin(function () { - return Q.reject(exception1); - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - }); - }); - }); - - }); - - describe("when the callback throws an exception", function () { - it("should reject with this new exception", function () { - return Q("foo") - .fin(function () { - throw exception1; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - }); - }); - }); - - }); - - describe("when the promise is rejected", function () { - - it("should call the callback", function () { - var called = false; - - return Q.reject(exception1) - .fin(function () { - called = true; - }) - .then(function () { - expect(called).toBe(true); - }, function () { - expect(called).toBe(true); - }); - }); - - it("should reject with the original reason", function () { - return Q.reject(exception1) - .fin(function () { - return "bar"; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - }); - }); - - describe("when the callback returns a promise", function () { - - describe("that is fulfilled", function () { - it("should reject with the original reason after that promise resolves", function () { - var promise = Q.delay(250); - - return Q.reject(exception1) - .fin(function () { - return promise; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception1); - expect(Q.isPending(promise)).toBe(false); - }); - }); - }); - - describe("that is rejected", function () { - it("should reject with the new reason", function () { - return Q.reject(exception1) - .fin(function () { - return Q.reject(exception2); - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception2); - }); - }); - }); - - }); - - describe("when the callback throws an exception", function () { - it("should reject with this new exception", function () { - return Q.reject(exception1) - .fin(function () { - throw exception2; - }) - .then(function () { - expect(false).toBe(true); - }, - function (exception) { - expect(exception).toBe(exception2); - }); - }); - }); - - }); - -}); - -describe("done", function () { - describe("when the promise is fulfilled", function () { - describe("and the callback does not throw", function () { - it("should call the callback and return nothing", function () { - var called = false; - - var promise = Q(); - - var returnValue = promise.done(function () { - called = true; - }); - - return promise.fail(function () { }).fin(function () { - expect(called).toBe(true); - expect(returnValue).toBe(undefined); - }); - }); - }); - - describe("and the callback throws", function () { - it("should rethrow that error in the next turn and return nothing", function () { - var turn = 0; - Q.nextTick(function () { - ++turn; - }); - - var returnValue = Q().done( - function () { - throw "foo"; - } - ); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(turn).toBe(1); - expect(error).toBe("foo"); - expect(returnValue).toBe(undefined); - deferred.resolve(); - }; - Q.delay(100).then(deferred.reject); - - return deferred.promise; - }); - }); - }); - - describe("when the promise is rejected", function () { - describe("and the errback handles it", function () { - it("should call the errback and return nothing", function () { - var called = false; - - var promise = Q.reject(new Error()); - - var returnValue = promise.done( - function () { }, - function () { - called = true; - } - ); - - return promise.fail(function () { }).fin(function () { - expect(called).toBe(true); - expect(returnValue).toBe(undefined); - }); - }); - }); - - describe("and the errback throws", function () { - it("should rethrow that error in the next turn and return nothing", function () { - var turn = 0; - Q.nextTick(function () { - ++turn; - }); - - var returnValue = Q.reject("bar").done( - null, - function () { - throw "foo"; - } - ); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(turn).toBe(1); - expect(error).toBe("foo"); - expect(returnValue).toBe(undefined); - deferred.resolve(); - }; - Q.delay(100).then(deferred.reject); - - return deferred.promise; - }); - }); - - describe("and there is no errback", function () { - it("should throw the original error in the next turn", function () { - var turn = 0; - Q.nextTick(function () { - ++turn; - }); - - var returnValue = Q.reject("bar").done(); - - var deferred = Q.defer(); - Q.onerror = function (error) { - expect(turn).toBe(1); - expect(error).toBe("bar"); - expect(returnValue).toBe(undefined); - deferred.resolve(); - }; - Q.delay(10).then(deferred.reject); - - return deferred.promise; - }); - }); - }); - - it("should attach a progress listener", function () { - var deferred = Q.defer(); - - var spy = jasmine.createSpy(); - deferred.promise.done(null, null, spy); - - deferred.notify(10); - deferred.resolve(); - - return deferred.promise.then(function () { - expect(spy).toHaveBeenCalledWith(10); - }); - }); -}); - -describe("timeout", function () { - it("should do nothing if the promise fulfills quickly", function () { - return Q.delay(10).timeout(200); - }); - - it("should do nothing if the promise rejects quickly", function () { - var goodError = new Error("haha!"); - return Q.delay(10) - .then(function () { - throw goodError; - }) - .timeout(200) - .then(undefined, function (error) { - expect(error).toBe(goodError); - }); - }); - - it("should reject with a timeout error if the promise is too slow", function () { - return Q.delay(100) - .timeout(10) - .then( - function () { - expect(true).toBe(false); - }, - function (error) { - expect(/time/i.test(error.message)).toBe(true); - } - ); - }); - - it("should pass through progress notifications", function () { - var deferred = Q.defer(); - - var progressValsSeen = []; - var promise = Q.timeout(deferred.promise, 300).then(function () { - expect(progressValsSeen).toEqual([1, 2, 3]); - }, undefined, function (progressVal) { - progressValsSeen.push(progressVal); - }); - - Q.delay(5).then(function () { deferred.notify(1); }); - Q.delay(15).then(function () { deferred.notify(2); }); - Q.delay(25).then(function () { deferred.notify(3); }); - Q.delay(35).then(function () { deferred.resolve(); }); - - return promise; - }); - - it("should reject with a custom timeout error if the promise is too slow and msg was provided", function () { - return Q.delay(100) - .timeout(10, "custom") - .then( - function () { - expect(true).toBe(false); - }, - function (error) { - expect(/custom/i.test(error.message)).toBe(true); - } - ); - }); - - -}); - -describe("delay", function () { - it("should delay fulfillment", function () { - var promise = Q(5).delay(50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise; - }); - - it("should not delay rejection", function () { - var promise = Q.reject(5).delay(50); - - return Q.delay(20).then(function () { - expect(promise.isPending()).toBe(false); - }); - }); - - it("should treat a single argument as a time", function () { - var promise = Q.delay(50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise; - }); - - it("should treat two arguments as a value + a time", function () { - var promise = Q.delay("what", 50); - - setTimeout(function () { - expect(promise.isPending()).toBe(true); - }, 40); - - return promise.then(function (value) { - expect(value).toBe("what"); - }); - }); - - it("should delay after resolution", function () { - var promise1 = Q.delay("what", 30); - var promise2 = promise1.delay(30); - - setTimeout(function () { - expect(promise1.isPending()).toBe(false); - expect(promise2.isPending()).toBe(true); - }, 40); - - return promise2.then(function (value) { - expect(value).toBe("what"); - }); - }); - - - it("should pass through progress notifications from passed promises", function () { - var deferred = Q.defer(); - - var progressValsSeen = []; - var promise = Q.delay(deferred.promise, 100).then(function () { - expect(progressValsSeen).toEqual([1, 2, 3]); - }, undefined, function (progressVal) { - progressValsSeen.push(progressVal); - }); - - Q.delay(5).then(function () { deferred.notify(1); }); - Q.delay(15).then(function () { deferred.notify(2); }); - Q.delay(25).then(function () { deferred.notify(3); }); - Q.delay(35).then(function () { deferred.resolve(); }); - - return promise; - }); -}); - -describe("thenResolve", function () { - describe("Resolving with a non-thenable value", function () { - it("returns a promise for that object once the promise is resolved", function () { - var waited = false; - return Q.delay(20) - .then(function () { - waited = true; - }) - .thenResolve("foo") - .then(function (val) { - expect(waited).toBe(true); - expect(val).toBe("foo"); - }); - }); - - describe("based off a rejected promise", function () { - it("does nothing, letting the rejection flow through", function () { - return Q.reject("boo") - .thenResolve("foo") - .then( - function () { - expect(true).toBe(false); - }, - function (reason) { - expect(reason).toBe("boo"); - } - ); - }); - }); - }); - - describe("Resolving with an promise", function () { - it("returns a promise for the result of that promise once the promise is resolved", function () { - var waited = false; - return Q.delay(20) - .then(function () { - waited = true; - }) - .thenResolve(Q("foo")) - .then(function (val) { - expect(waited).toBe(true); - expect(val).toBe("foo"); - }); - }); - }); -}); - -describe("thenReject", function () { - describe("Rejecting with a reason", function () { - it("returns a promise rejected with that object once the original promise is resolved", function () { - var waited = false; - return Q.delay(20) - .then(function () { - waited = true; - }) - .thenReject("foo") - .then( - function () { - expect(true).toBe(false); - }, - function (reason) { - expect(waited).toBe(true); - expect(reason).toBe("foo"); - } - ); - }); - - describe("based off a rejected promise", function () { - it("does nothing, letting the rejection flow through", function () { - return Q.reject("boo") - .thenResolve("foo") - .then( - function () { - expect(true).toBe(false); - }, - function (reason) { - expect(reason).toBe("boo"); - } - ); - }); - }); - }); -}); - - -describe("thenables", function () { - - it("assimilates a thenable with fulfillment with resolve", function () { - return Q({ - then: function (resolved) { - resolved(10); - } - }) - .then(function (ten) { - expect(ten).toEqual(10); - }) - .then(function (undefined) { - expect(undefined).toEqual(void 0); - }); - }); - - it("assimilates a thenable with progress and fulfillment (using resolve)", function () { - var progressValueArrays = []; - return Q({ - then: function (fulfilled, rejected, progressed) { - Q.nextTick(function () { - progressed(1, 2); - progressed(3, 4, 5); - fulfilled(); - }); - } - }) - .progress(function () { - progressValueArrays.push(Array.prototype.slice.call(arguments)); - }) - .then(function () { - expect(progressValueArrays).toEqual([[1], [3]]); - }); - }); - - it("assimilates a thenable with progress and fulfillment (using when)", function () { - var progressValueArrays = []; - return Q.when({ - then: function (fulfilled, rejected, progressed) { - Q.nextTick(function () { - progressed(1, 2); - progressed(3, 4, 5); - fulfilled(); - }); - } - }) - .progress(function () { - progressValueArrays.push(Array.prototype.slice.call(arguments)); - }) - .then(function () { - expect(progressValueArrays).toEqual([[1], [3]]); - }); - }); - - it("flows fulfillment into a promise pipeline", function () { - return Q({ - then: function (resolved) { - resolved([10]); - } - }) - .get(0) - .then(function (ten) { - expect(ten).toEqual(10); - }); - }); - - it("assimilates an immediately-fulfilled thenable in allSettled", function () { - return Q.allSettled([ - {then: function (win) { - win(10); - }} - ]) - .then(function (snapshots) { - expect(snapshots).toEqual([{ state: "fulfilled", value: 10 }]); - }); - }); - - it("assimilates an eventually-fulfilled thenable in allSettled", function () { - return Q.allSettled([ - {then: function (win) { - setTimeout(function () { - win(10); - }, 100); - }} - ]) - .then(function (snapshots) { - expect(snapshots).toEqual([{ state: "fulfilled", value: 10 }]); - }); - }); - -}); - -describe("node support", function () { - - var exception = new Error("That is not your favorite color."); - - var obj = { - method: function (a, b, c, callback) { - callback(null, a + b + c); - }, - thispChecker: function (callback) { - callback(null, this === obj); - }, - errorCallbacker: function (a, b, c, callback) { - callback(exception); - }, - errorThrower: function () { - throw exception; - } - }; - - describe("nfapply", function () { - - it("fulfills with callback result", function () { - return Q.nfapply(function (a, b, c, callback) { - callback(null, a + b + c); - }, [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("rejects with callback error", function () { - var exception = new Error("That is not your favorite color."); - return Q.nfapply(function (a, b, c, callback) { - callback(exception); - }, [1, 2, 3]) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("nfcall", function () { - it("fulfills with callback result", function () { - return Q.nfcall(function (a, b, c, callback) { - callback(null, a + b + c); - }, 1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("rejects with callback error", function () { - var exception = new Error("That is not your favorite color."); - return Q.nfcall(function (a, b, c, callback) { - callback(exception); - }, 1, 2, 3) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("nfbind", function () { - - it("mixes partial application with complete application", function () { - return Q.nfbind(function (a, b, c, d, callback) { - callback(null, a + b + c + d); - }, 1, 2).call({}, 3, 4) - .then(function (ten) { - expect(ten).toBe(10); - }); - }); - - }); - - describe("nbind", function () { - - it("binds this, and mixes partial application with complete application", function () { - return Q.nbind(function (a, b, c, callback) { - callback(null, this + a + b + c); - }, 1, 2).call(3 /* effectively ignored as fn bound to 1 */, 4, 5) - .then(function (twelve) { - expect(twelve).toBe(12); - }); - }); - - it("second arg binds this", function() { - var expectedThis = { test: null }; - - return Q.nbind(function(callback) { - callback(null, this); - }, expectedThis).call() - .then(function(actualThis) { - expect(actualThis).toEqual(expectedThis); - }); - }); - - }); - describe("npost", function () { - - it("fulfills with callback result", function () { - return Q.npost(obj, "method", [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("gets the correct thisp", function () { - return Q.npost(obj, "thispChecker", []) - .then(function (result) { - expect(result).toBe(true); - }); - }); - - it("rejects with callback error", function () { - return Q.npost(obj, "errorCallbacker", [1, 2, 3]) - .then(function () { - expect("blue").toBe("no, yellow!"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("rejects with thrown error", function () { - return Q.npost(obj, "errorThrower", [1, 2, 3]) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("works on promises for objects with Node methods", function () { - return Q(obj) - .npost("method", [1, 2, 3]) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - }); - - describe("nsend", function () { - - it("fulfills with callback result", function () { - return Q.nsend(obj, "method", 1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - it("gets the correct thisp", function () { - return Q.nsend(obj, "thispChecker") - .then(function (result) { - expect(result).toBe(true); - }); - }); - - it("rejects with callback error", function () { - return Q.nsend(obj, "errorCallbacker", 1, 2, 3) - .then(function () { - expect("blue").toBe("no, yellow!"); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("rejects with thrown error", function () { - return Q.nsend(obj, "errorThrower", 1, 2, 3) - .then(function () { - expect(true).toBe(false); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - it("works on promises for objects with Node methods", function () { - return Q(obj) - .nsend("method", 1, 2, 3) - .then(function (sum) { - expect(sum).toEqual(6); - }); - }); - - }); - - describe("deferred.makeNodeResolver", function () { - - it("fulfills a promise with a single callback argument", function () { - var deferred = Q.defer(); - var callback = deferred.makeNodeResolver(); - callback(null, 10); - return deferred.promise.then(function (value) { - expect(value).toBe(10); - }); - }); - - it("fulfills a promise with multiple callback arguments", function () { - var deferred = Q.defer(); - var callback = deferred.makeNodeResolver(); - callback(null, 10, 20); - return deferred.promise.then(function (value) { - expect(value).toEqual([10, 20]); - }); - }); - - it("rejects a promise", function () { - var deferred = Q.defer(); - var callback = deferred.makeNodeResolver(); - var exception = new Error("Holy Exception of Anitoch"); - callback(exception); - return deferred.promise.then(function () { - expect(5).toBe(3); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - - }); - - describe("nodeify", function () { - - it("calls back with a resolution", function () { - var spy = jasmine.createSpy(); - Q(10).nodeify(spy); - waitsFor(function () { - return spy.argsForCall.length; - }); - runs(function () { - expect(spy.argsForCall).toEqual([[null, 10]]); - }); - }); - - it("calls back with an error", function () { - var spy = jasmine.createSpy(); - Q.reject(10).nodeify(spy); - waitsFor(function () { - return spy.argsForCall.length; - }); - runs(function () { - expect(spy.argsForCall).toEqual([[10]]); - }); - }); - - it("forwards a promise", function () { - return Q(10).nodeify().then(function (ten) { - expect(ten).toBe(10); - }); - }); - - }); - -}); - -describe("isPromise", function () { - it("returns true if passed a promise", function () { - expect(Q.isPromise(Q(10))).toBe(true); - }); - - it("returns false if not passed a promise", function () { - expect(Q.isPromise(undefined)).toBe(false); - expect(Q.isPromise(null)).toBe(false); - expect(Q.isPromise(10)).toBe(false); - expect(Q.isPromise("str")).toBe(false); - expect(Q.isPromise("")).toBe(false); - expect(Q.isPromise(true)).toBe(false); - expect(Q.isPromise(false)).toBe(false); - expect(Q.isPromise({})).toBe(false); - expect(Q.isPromise({ - then: function () {} - })).toBe(false); - expect(Q.isPromise(function () {})).toBe(false); - }); -}); - -describe("isPromiseAlike", function () { - it("returns true if passed a promise like object", function () { - expect(Q.isPromiseAlike(Q(10))).toBe(true); - expect(Q.isPromiseAlike({ - then: function () {} - })).toBe(true); - }); - - it("returns false if not passed a promise like object", function () { - expect(Q.isPromiseAlike(undefined)).toBe(false); - expect(Q.isPromiseAlike(null)).toBe(false); - expect(Q.isPromiseAlike(10)).toBe(false); - expect(Q.isPromiseAlike("str")).toBe(false); - expect(Q.isPromiseAlike("")).toBe(false); - expect(Q.isPromiseAlike(true)).toBe(false); - expect(Q.isPromiseAlike(false)).toBe(false); - expect(Q.isPromiseAlike({})).toBe(false); - expect(Q.isPromiseAlike(function () {})).toBe(false); - }); -}); - -if (typeof require === "function") { - var domain; - try { - domain = require("domain"); - } catch (e) { } - - if (domain) { - var EventEmitter = require("events").EventEmitter; - - describe("node domain support", function () { - var d; - - beforeEach(function () { - d = domain.create(); - }); - afterEach(function() { - d.dispose(); - }); - - it("should work for non-promise async inside a promise handler", - function (done) { - var error = new Error("should be caught by the domain"); - - d.run(function () { - Q().then(function () { - setTimeout(function () { - throw error; - }, 10); - }); - }); - - var errorTimeout = setTimeout(function () { - done(new Error("Wasn't caught")); - }, 100); - - d.on("error", function (theError) { - expect(theError).toBe(error); - clearTimeout(errorTimeout); - done(); - }); - }); - - it("should transfer errors from `done` into the domain", - function (done) { - var error = new Error("should be caught by the domain"); - - d.run(function () { - Q.reject(error).done(); - }); - - var errorTimeout = setTimeout(function () { - done(new Error("Wasn't caught")); - }, 100); - - d.on("error", function (theError) { - expect(theError).toBe(error); - clearTimeout(errorTimeout); - done(); - }); - }); - - it("should take care of re-used event emitters", function (done) { - // See discussion in https://github.com/kriskowal/q/issues/120 - var error = new Error("should be caught by the domain"); - - var e = new EventEmitter(); - - d.run(function () { - callAsync().done(); - }); - setTimeout(function () { - e.emit("beep"); - }, 100); - - var errorTimeout = setTimeout(function () { - done(new Error("Wasn't caught")); - }, 500); - - d.on("error", function (theError) { - expect(theError).toBe(error); - clearTimeout(errorTimeout); - done(); - }); - - function callAsync() { - var def = Q.defer(); - e.once("beep", function () { - def.reject(error); - }); - return def.promise; - } - }); - }); - } -} - -describe("decorator functions", function () { - describe("promised", function () { - var exception = new Error("That is not the meaning of life."); - it("resolves promised arguments", function () { - var sum = Q.promised(function add(a, b) { - return a + b; - }); - return sum(Q(4), Q(5)).then(function (sum) { - expect(sum).toEqual(9); - }); - }); - it("resolves promised `this`", function () { - var inc = Q.promised(function inc(a) { - return this + a; - }); - return inc.call(Q(4), Q(5)).then(function (sum) { - expect(sum).toEqual(9); - }); - }); - it("is rejected if an argument is rejected", function () { - var sum = Q.promised(function add(a, b) { - return a + b; - }); - return sum(Q.reject(exception), Q(4)).then(function () { - expect(4).toEqual(42); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - it("is rejected if `this` is rejected", function () { - var inc = Q.promised(function inc(a) { - return this + a; - }); - return inc.call(Q.reject(exception), Q(4)).then(function () { - expect(4).toEqual(42); - }, function (_exception) { - expect(_exception).toBe(exception); - }); - }); - }); -}); - -describe("stack trace formatting", function () { - it("doesn't mangle a stack trace that gets handled twice", function () { - var d1 = Q.defer(); - var d2 = Q.defer(); - var captured = []; - d1.promise.done(); - d2.promise.done(); - - Q.onerror = function (err) { - captured.push(err.stack); - }; - - var error = new Error("boom!"); - d1.reject(error); - d2.reject(error); - - return Q.all([d1.promise.fail(function () {}), d2.promise.fail(function () { })]) - .then(function () { - expect(captured[0]).toEqual(captured[1]); - }); - }); -}); - -describe("possible regressions", function () { - - describe("gh-9", function () { - it("treats falsy values as resolved values without error", function () { - expect(Q.isPending(null)).toEqual(false); - expect(Q.isPending(void 0)).toEqual(false); - expect(Q.isPending(false)).toEqual(false); - expect(Q.isPending()).toEqual(false); - }); - }); - - describe("gh-22", function () { - it("ensures that the array prototype is intact", function () { - var keys = []; - for (var key in []) { - keys.push(key); - } - expect(keys.length).toBe(0); - }); - }); - - describe("gh-73", function () { - it("does not choke on non-error rejection reasons", function () { - Q.reject(REASON).done(); - - var deferred = Q.defer(); - - Q.onerror = function (error) { - expect(error).toBe(REASON); - deferred.resolve(); - }; - Q.delay(10).then(deferred.reject); - - return deferred.promise; - }); - }); - - describe("gh-90", function () { - it("does not choke on rejection reasons with an undefined `stack`", function () { - var error = new RangeError(REASON); - error.stack = undefined; - Q.reject(error).done(); - - var deferred = Q.defer(); - - Q.onerror = function (theError) { - expect(theError).toBe(error); - deferred.resolve(); - }; - Q.delay(10).then(deferred.reject); - - return deferred.promise; - }); - }); - - describe("gh-75", function () { - it("does not double-resolve misbehaved promises", function () { - var badPromise = Q.makePromise({ - post: function () { return "hello"; } - }); - - var resolutions = 0; - function onResolution() { - ++resolutions; - } - - return Q.when(badPromise, onResolution, onResolution).then(function () { - expect(resolutions).toBe(1); - }); - }); - }); - -}); - -describe("unhandled rejection reporting", function () { - beforeEach(function () { - Q.resetUnhandledRejections(); - }); - - it("doesn't report a resolve, then reject (gh-252)", function () { - var deferred = Q.defer(); - deferred.resolve(); - deferred.reject(); - - expect(Q.getUnhandledReasons().length).toEqual(0); - }); - - it("doesn't report when you chain off a rejection", function () { - return Q.reject("this will be handled").get("property").fail(function () { - // now it should be handled. - }).fin(function() { - expect(Q.getUnhandledReasons().length).toEqual(0); - }); - }); - - it("reports the most basic case", function () { - Q.reject("a reason"); - - expect(Q.getUnhandledReasons()).toEqual(["(no stack) a reason"]); - }); - - it("reports a stack trace", function () { - var error = new Error("a reason"); - Q.reject(error); - - expect(Q.getUnhandledReasons()).toEqual([error.stack]); - }); - - it("doesn't let you mutate the internal array", function () { - Q.reject("a reason"); - - Q.getUnhandledReasons().length = 0; - expect(Q.getUnhandledReasons()).toEqual(["(no stack) a reason"]); - }); - - it("resets after calling `Q.resetUnhandledRejections`", function () { - Q.reject("a reason"); - - Q.resetUnhandledRejections(); - expect(Q.getUnhandledReasons()).toEqual([]); - }); - - it("stops tracking after calling `Q.stopUnhandledRejectionTracking`", function () { - Q.reject("a reason"); - - Q.stopUnhandledRejectionTracking(); - - Q.reject("another reason"); - - expect(Q.getUnhandledReasons()).toEqual([]); - }); -}); diff --git a/packages/q/spec/queue-spec.js b/packages/q/spec/queue-spec.js deleted file mode 100644 index 4cec14e2..00000000 --- a/packages/q/spec/queue-spec.js +++ /dev/null @@ -1,180 +0,0 @@ - -var Q = require("../q"); -var Queue = require("../queue"); - -global.Q = Q; -require("./lib/jasmine-promise"); - -describe("queue", function () { - - it("should enqueue then dequeue", function () { - var queue = Queue(); - queue.put(1); - return queue.get().then(function (value) { - expect(value).toBe(1); - }); - }); - - it("should dequeue then enqueue", function () { - var queue = Queue(); - var promise = queue.get().then(function (value) { - expect(value).toBe(1); - }); - queue.put(1); - return promise; - }); - - it("should stream", function () { - var queue = Queue(); - - Q.try(function () { - return Q.delay(20).then(function () { - queue.put(1); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(2); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(3); - }) - }) - .done(); - - return Q.try(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(1); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(2); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(3); - }); - }) - - }); - - it("should be order agnostic", function () { - var queue = Queue(); - - Q.try(function () { - return Q.delay(20).then(function () { - queue.put(1); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(2); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(3); - }) - }) - .done(); - - return Q.all([ - queue.get() - .then(function (value) { - expect(value).toBe(1); - }), - queue.get() - .then(function (value) { - expect(value).toBe(2); - }), - queue.get() - .then(function (value) { - expect(value).toBe(3); - }) - ]); - }); - - it("should close", function () { - - var queue = Queue(); - - Q.try(function () { - return Q.delay(20).then(function () { - queue.put(1); - }) - }) - .then(function () { - return Q.delay(20).then(function () { - queue.put(2); - }) - }) - .then(function () { - queue.close(); - }) - .done(); - - return Q.try(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(1); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(value).toBe(2); - }); - }) - .then(function () { - return queue.get() - .then(function (value) { - expect(false).toBe(true); // should not get here - }); - }) - .catch(function (error) { - expect(error.message).toBe("Can't get value from closed queue"); - return queue.get(); - }) - .catch(function (error) { - expect(error.message).toBe("Can't get value from closed queue"); - }) - .then(function () { - return queue.closed; - }) - .then(function (error) { - expect(error.message).toBe("Can't get value from closed queue"); - }) - }); - - it("should close with alternate error", function () { - - var queue = Queue(); - queue.close(new Error("Alternate reason")); - - return Q.try(function () { - return queue.get(); - }) - .catch(function (error) { - expect(error.message).toBe("Alternate reason"); - return queue.get(); - }) - .catch(function (error) { - expect(error.message).toBe("Alternate reason"); - }) - .then(function () { - return queue.closed; - }) - .then(function (error) { - expect(error.message).toBe("Alternate reason"); - }) - }); - -}); - diff --git a/require.js b/require.js index ec691283..3f0c9aeb 100644 --- a/require.js +++ b/require.js @@ -6,908 +6,915 @@ https://github.com/motorola-mobility/montage/blob/master/LICENSE.md */ -/*global bootstrap,define */ -(function (definition) { - - // Boostrapping Browser - if (typeof bootstrap !== "undefined") { - - // Window - if (typeof window !== "undefined") { - bootstrap("require", function (require, exports) { - var Promise = require("promise"); - var URL = require("mini-url"); - definition(exports, Promise, URL); - require("require/browser"); - }); - - // Worker - } else { - bootstrap("require", function (require, exports) { - var Promise = require("promise").Promise; - var URL = require("mini-url"); - definition(exports, Promise, URL); - }); +var Require = exports; +var Q = require("q"); +var URL = require("url"); + +if (!this) { + throw new Error("Require does not work in strict mode."); +} + +var globalEval = eval; // reassigning causes eval to not use lexical scope. + +// Non-CommonJS speced extensions should be marked with an "// EXTENSION" +// comment. + +Require.makeRequire = function (config) { + var require; + + // Configuration defaults: + config = config || {}; + config.location = URL.resolve(config.location || Require.getLocation(), "./"); + config.lib = URL.resolve(config.location, config.lib || "./"); + config.paths = config.paths || [config.lib]; + config.mappings = config.mappings || {}; // EXTENSION + config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; + config.makeLoader = config.makeLoader || Require.makeLoader; + config.load = config.load || config.makeLoader(config); + config.makeCompiler = config.makeCompiler || Require.makeCompiler; + config.compile = config.compile || config.makeCompiler(config); + config.parseDependencies = config.parseDependencies || Require.parseDependencies; + config.read = config.read || Require.read; + + // Modules: { exports, id, location, directory, factory, dependencies, + // dependees, text, type } + var modules = config.modules = config.modules || {}; + + // produces an entry in the module state table, which gets built + // up through loading and execution, ultimately serving as the + // ``module`` free variable inside the corresponding module. + function getModuleDescriptor(id) { + var lookupId = id.toLowerCase(); + if (!has(modules, lookupId)) { + modules[lookupId] = { + id: id, + display: (config.name || config.location) + "#" + id, // EXTENSION + require: require + }; } - - // Node Server - } else if (typeof process !== "undefined") { - // the parens trick the heuristic scanner for static dependencies, so - // they are not pre-loaded by the asynchronous browser loader - var Promise = (require)("q"); - var URL = (require)("url"); - definition(exports, Promise, URL); - (require)("./node"); - } else { - throw new Error("Can't support require on this platform"); + return modules[lookupId]; } -})(function (Require, Promise, URL) { - - if (!this) { - throw new Error("Require does not work in strict mode."); + // for preloading modules by their id and exports, useful to + // prevent wasteful multiple instantiation if a module was loaded + // in the bootstrapping process and can be trivially injected into + // the system. + function inject(id, exports) { + var module = getModuleDescriptor(id); + module.exports = exports; + module.location = URL.resolve(config.location, id); + module.directory = URL.resolve(module.location, "./"); + module.injected = true; + delete module.redirect; + delete module.mappingRedirect; } - var globalEval = eval; // reassigning causes eval to not use lexical scope. - - // Non-CommonJS speced extensions should be marked with an "// EXTENSION" - // comment. - - Require.makeRequire = function (config) { - var require; - - // Configuration defaults: - config = config || {}; - config.location = URL.resolve(config.location || Require.getLocation(), "./"); - config.lib = URL.resolve(config.location, config.lib || "./"); - config.paths = config.paths || [config.lib]; - config.mappings = config.mappings || {}; // EXTENSION - config.exposedConfigs = config.exposedConfigs || Require.exposedConfigs; - config.makeLoader = config.makeLoader || Require.makeLoader; - config.load = config.load || config.makeLoader(config); - config.makeCompiler = config.makeCompiler || Require.makeCompiler; - config.compile = config.compile || config.makeCompiler(config); - config.parseDependencies = config.parseDependencies || Require.parseDependencies; - config.read = config.read || Require.read; - - // Modules: { exports, id, location, directory, factory, dependencies, - // dependees, text, type } - var modules = config.modules = config.modules || {}; - - // produces an entry in the module state table, which gets built - // up through loading and execution, ultimately serving as the - // ``module`` free variable inside the corresponding module. - function getModuleDescriptor(id) { - var lookupId = id.toLowerCase(); - if (!has(modules, lookupId)) { - modules[lookupId] = { - id: id, - display: (config.name || config.location) + "#" + id, // EXTENSION - require: require - }; + // Ensures a module definition is loaded, compiled, analyzed + var load = memoize(function (topId, viaId) { + var module = getModuleDescriptor(topId); + return Q.fcall(function () { + // if not already loaded, already instantiated, or + // configured as a redirection to another module + if ( + module.factory === void 0 && + module.exports === void 0 && + module.redirect === void 0 + ) { + return Q.fcall(config.load, topId, module); + } + }) + .then(function () { + // compile and analyze dependencies + config.compile(module); + var dependencies = + module.dependencies = + module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); } - return modules[lookupId]; + }); + }); + + // Load a module definition, and the definitions of its transitive + // dependencies + function deepLoad(topId, viaId, loading) { + var module = getModuleDescriptor(topId); + // this is a memo of modules already being loaded so we don’t + // data-lock on a cycle of dependencies. + loading = loading || {}; + // has this all happened before? will it happen again? + if (has(loading, topId)) { + return; // break the cycle of violence. } - - // for preloading modules by their id and exports, useful to - // prevent wasteful multiple instantiation if a module was loaded - // in the bootstrapping process and can be trivially injected into - // the system. - function inject(id, exports) { - var module = getModuleDescriptor(id); - module.exports = exports; - module.location = URL.resolve(config.location, id); - module.directory = URL.resolve(module.location, "./"); - module.injected = true; - delete module.redirect; - delete module.mappingRedirect; - } - - // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { - var module = getModuleDescriptor(topId); - return Promise.fcall(function () { - // if not already loaded, already instantiated, or - // configured as a redirection to another module - if ( - module.factory === void 0 && - module.exports === void 0 && - module.redirect === void 0 - ) { - return Promise.fcall(config.load, topId, module); - } - }) - .then(function () { - // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } - }); + loading[topId] = true; // this has happened before + return load(topId, viaId) + .then(function () { + // load the transitive dependencies using the magic of + // recursion. + return Q.all(module.dependencies.map(function (depId) { + depId = resolve(depId, topId); + // create dependees set, purely for debug purposes + var module = getModuleDescriptor(depId); + var dependees = module.dependees = module.dependees || {}; + dependees[topId] = true; + return deepLoad(depId, topId, loading); + })); + }, function (error) { + module.error = error; }); + } - // Load a module definition, and the definitions of its transitive - // dependencies - function deepLoad(topId, viaId, loading) { - var module = getModuleDescriptor(topId); - // this is a memo of modules already being loaded so we don’t - // data-lock on a cycle of dependencies. - loading = loading || {}; - // has this all happened before? will it happen again? - if (has(loading, topId)) { - return; // break the cycle of violence. - } - loading[topId] = true; // this has happened before - return load(topId, viaId) - .then(function () { - // load the transitive dependencies using the magic of - // recursion. - return Promise.all(module.dependencies.map(function (depId) { - depId = resolve(depId, topId); - // create dependees set, purely for debug purposes - var module = getModuleDescriptor(depId); - var dependees = module.dependees = module.dependees || {}; - dependees[topId] = true; - return deepLoad(depId, topId, loading); - })); - }, function (error) { - module.error = error; - }); + function lookup(topId, viaId) { + topId = resolve(topId, viaId); + var module = getModuleDescriptor(topId); + + // check for consistent case convention + if (module.id !== topId) { + throw new Error( + "Can't require module " + JSON.stringify(module.id) + + " by alternate spelling " + JSON.stringify(topId) + ); } - // Initializes a module by executing the factory function with a new - // module "exports" object. - function getExports(topId, viaId) { - var module = getModuleDescriptor(topId); + // handle redirects + if (module.redirect !== void 0) { + return lookup(module.redirect, topId); + } - // check for consistent case convention - if (module.id !== topId) { - throw new Error( - "Can't require module " + JSON.stringify(module.id) + - " by alternate spelling " + JSON.stringify(topId) - ); - } + // handle cross-package linkage + if (module.mappingRedirect !== void 0) { + return module.mappingRequire.lookup(module.mappingRedirect, ""); + } - // check for load error - if (module.error) { - var error = new Error( - "Can't require module " + JSON.stringify(module.id) + - " via " + JSON.stringify(viaId) + - " because " + module.error.message - ); - error.cause = module.error; - throw error; - } + return module; + } - // handle redirects - if (module.redirect !== void 0) { - return getExports(module.redirect, viaId); - } + // Initializes a module by executing the factory function with a new + // module "exports" object. + function getExports(topId, viaId) { + var module = getModuleDescriptor(topId); - // handle cross-package linkage - if (module.mappingRedirect !== void 0) { - return module.mappingRequire(module.mappingRedirect, viaId); - } + // check for consistent case convention + if (module.id !== topId) { + throw new Error( + "Can't require module " + JSON.stringify(module.id) + + " by alternate spelling " + JSON.stringify(topId) + ); + } - // do not reinitialize modules - if (module.exports !== void 0) { - return module.exports; - } + // check for load error + if (module.error) { + var error = module.error; + error.message = ( + "Can't require module " + JSON.stringify(module.id) + + " via " + JSON.stringify(viaId) + + " because " + error.message + ); + throw error; + } - // do not initialize modules that do not define a factory function - if (module.factory === void 0) { - throw new Error( - "Can't require module " + JSON.stringify(topId) + - " via " + JSON.stringify(viaId) - ); - } + // handle redirects + if (module.redirect !== void 0) { + return getExports(module.redirect, viaId); + } - module.directory = URL.resolve(module.location, "./"); // EXTENSION - module.exports = {}; + // handle cross-package linkage + if (module.mappingRedirect !== void 0) { + return module.mappingRequire(module.mappingRedirect, viaId); + } - // Execute the factory function: - var returnValue = module.factory.call( - // in the context of the module: - void 0, // this (defaults to global) - makeRequire(topId), // require - module.exports, // exports - module // module + // do not reinitialize modules + if (module.exports !== void 0) { + return module.exports; + } + + // do not initialize modules that do not define a factory function + if (module.factory === void 0) { + throw new Error( + "Can't require module " + JSON.stringify(topId) + + " via " + JSON.stringify(viaId) + " " + JSON.stringify(module) ); + } - // EXTENSION - if (returnValue !== void 0) { - module.exports = returnValue; - } + module.directory = URL.resolve(module.location, "./"); // EXTENSION + module.exports = {}; - return module.exports; + // Execute the factory function: + var returnValue = module.factory.call( + // in the context of the module: + void 0, // this (defaults to global) + makeRequire(topId), // require + module.exports, // exports + module // module + ); + + // EXTENSION + if (returnValue !== void 0) { + module.exports = returnValue; } - // Finds the internal identifier for a module in a subpackage - // The `seen` object is a memo of the packages we have seen to avoid - // infinite recursion of cyclic package dependencies. It also causes - // the function to return null instead of throwing an exception. I’m - // guessing that throwing exceptions *and* being recursive would be - // too much performance evil for one function. - function identify(id2, require2, seen) { - var location = config.location; - if (require2.location === location) { - return id2; - } + return module.exports; + } - var internal = !!seen; - seen = seen || {}; - if (has(seen, location)) { - return null; // break the cycle of violence. - } - seen[location] = true; - /*jshint -W089 */ - for (var name in config.mappings) { - var mapping = config.mappings[name]; - location = mapping.location; - if (!config.hasPackage(location)) { - continue; - } - var candidate = config.getPackage(location); - var id1 = candidate.identify(id2, require2, seen); - if (id1 === null) { - continue; - } else if (id1 === "") { - return name; - } else { - return name + "/" + id1; - } - } - if (internal) { - return null; + // Finds the internal identifier for a module in a subpackage + // The `seen` object is a memo of the packages we have seen to avoid + // infinite recursion of cyclic package dependencies. It also causes + // the function to return null instead of throwing an exception. I’m + // guessing that throwing exceptions *and* being recursive would be + // too much performance evil for one function. + function identify(id2, require2, seen) { + var location = config.location; + if (require2.location === location) { + return id2; + } + + var internal = !!seen; + seen = seen || {}; + if (has(seen, location)) { + return null; // break the cycle of violence. + } + seen[location] = true; + /*jshint -W089 */ + for (var name in config.mappings) { + var mapping = config.mappings[name]; + location = mapping.location; + if (!config.hasPackage(location)) { + continue; + } + var candidate = config.getPackage(location); + var id1 = candidate.identify(id2, require2, seen); + if (id1 === null) { + continue; + } else if (id1 === "") { + return name; } else { - throw new Error( - "Can't identify " + id2 + " from " + require2.location - ); + return name + "/" + id1; } - /*jshint +W089 */ } + if (internal) { + return null; + } else { + throw new Error( + "Can't identify " + id2 + " from " + require2.location + ); + } + /*jshint +W089 */ + } - // Creates a unique require function for each module that encapsulates - // that module's id for resolving relative module IDs against. - function makeRequire(viaId) { - - // Main synchronously executing "require()" function - var require = function(id) { - var topId = resolve(id, viaId); - return getExports(topId, viaId); - }; + // Creates a unique require function for each module that encapsulates + // that module's id for resolving relative module IDs against. + function makeRequire(viaId) { - // Asynchronous "require.async()" which ensures async executation - // (even with synchronous loaders) - require.async = function(id) { - var topId = resolve(id, viaId); - var module = getModuleDescriptor(id); - return deepLoad(topId, viaId) - .then(function () { - return require(topId); - }); - }; + // Main synchronously executing "require()" function + var require = function(id) { + var topId = resolve(id, viaId); + return getExports(topId, viaId); + }; - require.resolve = function (id) { - return normalizeId(resolve(id, viaId)); - }; + // Asynchronous "require.async()" which ensures async executation + // (even with synchronous loaders) + require.async = function(id) { + var topId = resolve(id, viaId); + var module = getModuleDescriptor(id); + return deepLoad(topId, viaId) + .then(function () { + return require(topId); + }); + }; - require.getModule = getModuleDescriptor; // XXX deprecated, use: - require.getModuleDescriptor = getModuleDescriptor; - require.load = load; - require.deepLoad = deepLoad; + require.resolve = function (id) { + return normalizeId(resolve(id, viaId)); + }; - require.loadPackage = function (dependency, givenConfig) { - if (givenConfig) { // explicit configuration, fresh environment - return Require.loadPackage(dependency, givenConfig); - } else { // inherited environment - return config.loadPackage(dependency, config); - } - }; + require.getModule = getModuleDescriptor; // XXX deprecated, use: + require.getModuleDescriptor = getModuleDescriptor; + require.lookup = lookup; + require.load = load; + require.deepLoad = deepLoad; - require.hasPackage = function (dependency) { - return config.hasPackage(dependency); - }; + require.loadPackage = function (dependency, givenConfig) { + if (givenConfig) { // explicit configuration, fresh environment + return Require.loadPackage(dependency, givenConfig); + } else { // inherited environment + return config.loadPackage(dependency, config); + } + }; - require.getPackage = function (dependency) { - return config.getPackage(dependency); - }; + require.hasPackage = function (dependency) { + return config.hasPackage(dependency); + }; - require.isMainPackage = function () { - return require.location === config.mainPackageLocation; - }; + require.getPackage = function (dependency) { + return config.getPackage(dependency); + }; - require.injectPackageDescription = function (location, description) { - Require.injectPackageDescription(location, description, config); - }; + require.isMainPackage = function () { + return require.location === config.mainPackageLocation; + }; - require.injectPackageDescriptionLocation = function (location, descriptionLocation) { - Require.injectPackageDescriptionLocation(location, descriptionLocation, config); - }; + require.injectPackageDescription = function (location, description) { + Require.injectPackageDescription(location, description, config); + }; - require.injectMapping = function (dependency, name) { - dependency = normalizeDependency(dependency, config, name); - name = name || dependency.name; - config.mappings[name] = dependency; - }; + require.injectPackageDescriptionLocation = function (location, descriptionLocation) { + Require.injectPackageDescriptionLocation(location, descriptionLocation, config); + }; - require.injectDependency = function (name) { - require.injectMapping({name: name}, name); - }; + require.injectMapping = function (dependency, name) { + dependency = normalizeDependency(dependency, config, name); + name = name || dependency.name; + config.mappings[name] = dependency; + }; - require.identify = identify; - require.inject = inject; + require.injectDependency = function (name) { + require.injectMapping({name: name}, name); + }; - config.exposedConfigs.forEach(function(name) { - require[name] = config[name]; - }); + require.identify = identify; + require.inject = inject; - require.config = config; + config.exposedConfigs.forEach(function(name) { + require[name] = config[name]; + }); - require.read = config.read; + require.config = config; - return require; - } + require.read = config.read; - require = makeRequire(""); return require; - }; - - Require.injectPackageDescription = function (location, description, config) { - var descriptions = - config.descriptions = - config.descriptions || {}; - descriptions[location] = Promise.resolve(description); - }; + } - Require.injectPackageDescriptionLocation = function (location, descriptionLocation, config) { + require = makeRequire(""); + return require; +}; + +Require.injectPackageDescription = function (location, description, config) { + var descriptions = + config.descriptions = + config.descriptions || {}; + descriptions[location] = Q.resolve(description); +}; + +Require.injectPackageDescriptionLocation = function (location, descriptionLocation, config) { + var descriptionLocations = + config.descriptionLocations = + config.descriptionLocations || {}; + descriptionLocations[location] = descriptionLocation; +}; + +Require.loadPackageDescription = function (dependency, config) { + var location = dependency.location; + var descriptions = + config.descriptions = + config.descriptions || {}; + if (descriptions[location] === void 0) { var descriptionLocations = config.descriptionLocations = config.descriptionLocations || {}; - descriptionLocations[location] = descriptionLocation; - }; - - Require.loadPackageDescription = function (dependency, config) { - var location = dependency.location; - var descriptions = - config.descriptions = - config.descriptions || {}; - if (descriptions[location] === void 0) { - var descriptionLocations = - config.descriptionLocations = - config.descriptionLocations || {}; - var descriptionLocation; - if (descriptionLocations[location]) { - descriptionLocation = descriptionLocations[location]; - } else { - descriptionLocation = URL.resolve(location, "package.json"); - } - descriptions[location] = (config.read || Require.read)(descriptionLocation) - .then(function (json) { - try { - return JSON.parse(json); - } catch (error) { - error.message = error.message + " in " + JSON.stringify(descriptionLocation); - throw error; - } - }); + var descriptionLocation; + if (descriptionLocations[location]) { + descriptionLocation = descriptionLocations[location]; + } else { + descriptionLocation = URL.resolve(location, "package.json"); } - return descriptions[location]; - }; + descriptions[location] = (config.read || Require.read)(descriptionLocation) + .then(function (json) { + try { + return JSON.parse(json); + } catch (error) { + error.message = error.message + " in " + JSON.stringify(descriptionLocation); + throw error; + } + }); + } + return descriptions[location]; +}; - Require.loadPackage = function (dependency, config) { +Require.loadPackage = function (dependency, config) { + dependency = normalizeDependency(dependency, config); + if (!dependency.location) { + throw new Error("Can't find dependency: " + JSON.stringify(dependency)); + } + var location = dependency.location; + config = Object.create(config || null); + var loadingPackages = config.loadingPackages = config.loadingPackages || {}; + var loadedPackages = config.packages = {}; + var registry = config.registry = config.registry || Object.create(null); + config.mainPackageLocation = location; + + config.hasPackage = function (dependency) { dependency = normalizeDependency(dependency, config); if (!dependency.location) { - throw new Error("Can't find dependency: " + JSON.stringify(dependency)); + return false; } var location = dependency.location; - config = Object.create(config || null); - var loadingPackages = config.loadingPackages = config.loadingPackages || {}; - var loadedPackages = config.packages = {}; - var registry = config.registry = config.registry || Object.create(null); - config.mainPackageLocation = location; - - config.hasPackage = function (dependency) { - dependency = normalizeDependency(dependency, config); - if (!dependency.location) { - return false; - } - var location = dependency.location; - return !!loadedPackages[location]; - }; - - config.getPackage = function (dependency) { - dependency = normalizeDependency(dependency, config); - if (!dependency.location) { - throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); - } - var location = dependency.location; - if (!loadedPackages[location]) { - if (loadingPackages[location]) { - throw new Error( - "Dependency has not finished loading: " + JSON.stringify(dependency) - ); - } else { - throw new Error( - "Dependency was not loaded: " + JSON.stringify(dependency) - ); - } - } - return loadedPackages[location]; - }; - - config.loadPackage = function (dependency, viaConfig) { - dependency = normalizeDependency(dependency, viaConfig); - if (!dependency.location) { - throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); - } - var location = dependency.location; - if (!loadingPackages[location]) { - loadingPackages[location] = Require.loadPackageDescription(dependency, config) - .then(function (packageDescription) { - var subconfig = configurePackage( - location, - packageDescription, - config - ); - var pkg = Require.makeRequire(subconfig); - loadedPackages[location] = pkg; - return pkg; - }); - } - return loadingPackages[location]; - }; - - var pkg = config.loadPackage(dependency); - pkg.location = location; - pkg.async = function (id, callback) { - return pkg.then(function (require) { - return require.async(id, callback); - }); - }; - - return pkg; + return !!loadedPackages[location]; }; - function normalizeDependency(dependency, config, name) { - config = config || {}; - if (typeof dependency === "string") { - dependency = { - location: dependency - }; - } - if (dependency.main) { - dependency.location = config.mainPackageLocation; - } - // if the named dependency has already been found at another - // location, refer to the same eventual instance - if ( - dependency.name && - config.registry && - config.registry[dependency.name] - ) { - dependency.location = config.registry[dependency.name]; - } - // default location - if (!dependency.location && config.packagesDirectory && dependency.name) { - dependency.location = URL.resolve( - config.packagesDirectory, - dependency.name + "/" - ); - } + config.getPackage = function (dependency) { + dependency = normalizeDependency(dependency, config); if (!dependency.location) { - return dependency; // partially completed - } - // make sure the dependency location has a trailing slash so that - // relative urls will resolve properly - if (!/\/$/.test(dependency.location)) { - dependency.location += "/"; + throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } - // resolve the location relative to the current package - if (!Require.isAbsolute(dependency.location)) { - if (!config.location) { + var location = dependency.location; + if (!loadedPackages[location]) { + if (loadingPackages[location]) { throw new Error( - "Dependency locations must be fully qualified: " + - JSON.stringify(dependency) + "Dependency has not finished loading: " + JSON.stringify(dependency) + ); + } else { + throw new Error( + "Dependency was not loaded: " + JSON.stringify(dependency) ); } - dependency.location = URL.resolve( - config.location, - dependency.location - ); } - // register the package name so the location can be reused - if (dependency.name) { - config.registry[dependency.name] = dependency.location; - } - return dependency; - } - - function configurePackage(location, description, parent) { + return loadedPackages[location]; + }; - if (!/\/$/.test(location)) { - location += "/"; + config.loadPackage = function (dependency, viaConfig) { + dependency = normalizeDependency(dependency, viaConfig); + if (!dependency.location) { + throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } - - var config = Object.create(parent); - config.name = description.name; - config.location = location || Require.getLocation(); - config.packageDescription = description; - config.useScriptInjection = description.useScriptInjection; - - if (description.production !== void 0) { - config.production = description.production; + var location = dependency.location; + if (!loadingPackages[location]) { + loadingPackages[location] = Require.loadPackageDescription(dependency, config) + .then(function (packageDescription) { + var subconfig = configurePackage( + location, + packageDescription, + config + ); + var pkg = Require.makeRequire(subconfig); + loadedPackages[location] = pkg; + return pkg; + }); + loadingPackages[location].done(); } + return loadingPackages[location]; + }; - // explicitly mask definitions and modules, which must - // not apply to child packages - var modules = config.modules = config.modules || {}; + var pkg = config.loadPackage(dependency); + pkg.location = location; + pkg.async = function (id, callback) { + return pkg.then(function (require) { + return require.async(id, callback); + }); + }; - var registry = config.registry; - if (config.name !== void 0 && !registry[config.name]) { - registry[config.name] = config.location; + return pkg; +}; + +function normalizeDependency(dependency, config, name) { + config = config || {}; + if (typeof dependency === "string") { + dependency = { + location: dependency + }; + } + if (dependency.main) { + dependency.location = config.mainPackageLocation; + } + // if the named dependency has already been found at another + // location, refer to the same eventual instance + if ( + dependency.name && + config.registry && + config.registry[dependency.name] + ) { + dependency.location = config.registry[dependency.name]; + } + // default location + if (!dependency.location && config.packagesDirectory && dependency.name) { + dependency.location = URL.resolve( + config.packagesDirectory, + dependency.name + "/" + ); + } + if (!dependency.location) { + return dependency; // partially completed + } + // make sure the dependency location has a trailing slash so that + // relative urls will resolve properly + if (!/\/$/.test(dependency.location)) { + dependency.location += "/"; + } + // resolve the location relative to the current package + if (!Require.isAbsolute(dependency.location)) { + if (!config.location) { + throw new Error( + "Dependency locations must be fully qualified: " + + JSON.stringify(dependency) + ); } + dependency.location = URL.resolve( + config.location, + dependency.location + ); + } + // register the package name so the location can be reused + if (dependency.name) { + config.registry[dependency.name] = dependency.location; + } + return dependency; +} - // overlay - var overlay = description.overlay || {}; - var layer; - (config.overlays || Require.overlays).forEach(function (engine) { - /*jshint -W089 */ - if (overlay[engine]) { - var layer = overlay[engine]; - for (var name in layer) { - description[name] = layer[name]; - } - } - /*jshint +W089 */ - }); - delete description.overlay; - - // directories - description.directories = description.directories || {}; - description.directories.lib = - description.directories.lib === void 0 ? "./" : description.directories.lib; - var lib = description.directories.lib; - // lib - config.lib = URL.resolve(location, "./" + lib); - var packagesDirectory = description.directories.packages || "node_modules"; - packagesDirectory = URL.resolve(location, packagesDirectory + "/"); - config.packagesDirectory = packagesDirectory; - - // The default "main" module of a package has the same name as the - // package. - if (description.main !== void 0) { - - // main, injects a definition for the main module, with - // only its path. makeRequire goes through special effort - // in deepLoad to re-initialize this definition with the - // loaded definition from the given path. - modules[""] = { - id: "", - redirect: normalizeId(resolve(description.main, "")), - location: config.location - }; +function configurePackage(location, description, parent) { - } + if (!/\/$/.test(location)) { + location += "/"; + } - //Deal with redirects - var redirects = description.redirects; - if (redirects !== void 0) { - Object.keys(redirects).forEach(function (name) { - modules[name] = { - id: name, - redirect: redirects[name], - location: URL.resolve(location, name) - }; - }); - } + var config = Object.create(parent); + config.name = description.name; + config.location = location || Require.getLocation(); + config.packageDescription = description; + config.useScriptInjection = description.useScriptInjection; - // mappings, link this package to other packages. - var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production ? description.devDependencies : null] - .forEach(function (dependencies) { - if (!dependencies) { - return; - } - Object.keys(dependencies).forEach(function (name) { - if (!mappings[name]) { - // dependencies are equivalent to name and version mappings, - // though the version predicate string is presently ignored - // (TODO) - mappings[name] = { - name: name, - version: dependencies[name] - }; - } - }); - }); - // mappings - Object.keys(mappings).forEach(function (name) { - var mapping = mappings[name] = normalizeDependency( - mappings[name], - config, - name - ); - }); - config.mappings = mappings; + if (description.production !== void 0) { + config.production = description.production; + } - return config; + // explicitly mask definitions and modules, which must + // not apply to child packages + var modules = config.modules = config.modules || {}; + + var registry = config.registry; + if (config.name !== void 0 && !registry[config.name]) { + registry[config.name] = config.location; } - // Helper functions: + // overlay + var overlay = description.overlay || {}; - function has(object, property) { - return Object.prototype.hasOwnProperty.call(object, property); + // but first, convert "browser" field, as pioneered by Browserify, to an + // overlay + if (typeof description.browser === "string") { + overlay.browser = { + redirects: {"": description.browser} + }; + } else if (typeof description.browser === "object") { + overlay.browser = { + redirects: description.browser + }; } - // Resolves CommonJS module IDs (not paths) - Require.resolve = resolve; - function resolve(id, baseId) { - id = String(id); - var source = id.split("/"); - var target = []; - if (source.length && source[0] === "." || source[0] === "..") { - var parts = baseId.split("/"); - parts.pop(); - source.unshift.apply(source, parts); - } - for (var i = 0, ii = source.length; i < ii; i++) { - /*jshint -W035 */ - var part = source[i]; - if (part === "" || part === ".") { - } else if (part === "..") { - if (target.length) { - target.pop(); - } - } else { - target.push(part); + // overlay continued... + var layer; + (config.overlays || Require.overlays).forEach(function (engine) { + /*jshint -W089 */ + if (overlay[engine]) { + var layer = overlay[engine]; + for (var name in layer) { + description[name] = layer[name]; } - /*jshint +W035 */ } - return target.join("/"); - } - - Require.base = function (location) { - // matches Unix basename - return String(location) - .replace(/(.+?)\/+$/, "$1") - .match(/([^\/]+$|^\/$|^$)/)[1]; - }; + /*jshint +W089 */ + }); + delete description.overlay; + + // directories + description.directories = description.directories || {}; + description.directories.lib = + description.directories.lib === void 0 ? "./" : description.directories.lib; + var lib = description.directories.lib; + // lib + config.lib = URL.resolve(location, "./" + lib); + var packagesDirectory = description.directories.packages || "node_modules"; + packagesDirectory = URL.resolve(location, packagesDirectory + "/"); + config.packagesDirectory = packagesDirectory; + + // The default "main" module of a package has the same name as the + // package. + if (description.main !== void 0) { + + // main, injects a definition for the main module, with + // only its path. makeRequire goes through special effort + // in deepLoad to re-initialize this definition with the + // loaded definition from the given path. + modules[""] = { + id: "", + redirect: normalizeId(resolve(description.main, "")), + location: config.location + }; - // Tests whether the location or URL is a absolute. - Require.isAbsolute = function(location) { - return (/^[\w\-]+:/).test(location); - }; + } - // Extracts dependencies by parsing code and looking for "require" (currently using a simple regexp) - Require.parseDependencies = function(factory) { - var o = {}; - String(factory).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { - o[id] = true; + //Deal with redirects + var redirects = description.redirects; + if (redirects !== void 0) { + Object.keys(redirects).forEach(function (name) { + modules[name] = { + id: name, + redirect: redirects[name], + location: URL.resolve(location, name) + }; }); - return Object.keys(o); - }; - - // Built-in compiler/preprocessor "middleware": + } - Require.DependenciesCompiler = function(config, compile) { - return function(module) { - if (!module.dependencies && module.text !== void 0) { - module.dependencies = config.parseDependencies(module.text); + // mappings, link this package to other packages. + var mappings = description.mappings || {}; + // dependencies, devDependencies if not in production + [description.dependencies, !config.production? description.devDependencies : null] + .forEach(function (dependencies) { + if (!dependencies) { + return; + } + Object.keys(dependencies).forEach(function (name) { + if (!mappings[name]) { + // dependencies are equivalent to name and version mappings, + // though the version predicate string is presently ignored + // (TODO) + mappings[name] = { + name: name, + version: dependencies[name] + }; } - compile(module); - if (module && !module.dependencies) { - if (module.text || module.factory) { - module.dependencies = Require.parseDependencies(module.text || module.factory); - } else { - module.dependencies = []; - } + }); + }); + // mappings + Object.keys(mappings).forEach(function (name) { + var mapping = mappings[name] = normalizeDependency( + mappings[name], + config, + name + ); + }); + config.mappings = mappings; + + return config; +} + +// Helper functions: + +function has(object, property) { + return Object.prototype.hasOwnProperty.call(object, property); +} + +// Resolves CommonJS module IDs (not paths) +Require.resolve = resolve; +function resolve(id, baseId) { + id = String(id); + var source = id.split("/"); + var target = []; + if (source.length && source[0] === "." || source[0] === "..") { + var parts = baseId.split("/"); + parts.pop(); + source.unshift.apply(source, parts); + } + for (var i = 0, ii = source.length; i < ii; i++) { + /*jshint -W035 */ + var part = source[i]; + if (part === "" || part === ".") { + } else if (part === "..") { + if (target.length) { + target.pop(); } - return module; - }; - }; - - // Support she-bang for shell scripts by commenting it out (it is never - // valid JavaScript syntax anyway) - Require.ShebangCompiler = function(config, compile) { - return function (module) { - if (module.text) { - module.text = module.text.replace(/^#!/, "//#!"); + } else { + target.push(part); + } + /*jshint +W035 */ + } + return target.join("/"); +} + +Require.base = function (location) { + // matches Unix basename + return String(location) + .replace(/(.+?)\/+$/, "$1") + .match(/([^\/]+$|^\/$|^$)/)[1]; +}; + +// Tests whether the location or URL is a absolute. +Require.isAbsolute = function(location) { + return (/^[\w\-]+:/).test(location); +}; + +// Extracts dependencies by parsing code and looking for "require" (currently using a simple regexp) +Require.parseDependencies = function(factory) { + var o = {}; + String(factory).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { + o[id] = true; + }); + return Object.keys(o); +}; + +// Built-in compiler/preprocessor "middleware": + +Require.DependenciesCompiler = function(config, compile) { + return function(module) { + if (!module.dependencies && module.text !== void 0) { + module.dependencies = config.parseDependencies(module.text); + } + compile(module); + if (module && !module.dependencies) { + if (module.text || module.factory) { + module.dependencies = Require.parseDependencies(module.text || module.factory); + } else { + module.dependencies = []; } - compile(module); - }; + } + return module; }; +}; + +// Support she-bang for shell scripts by commenting it out (it is never +// valid JavaScript syntax anyway) +Require.ShebangCompiler = function(config, compile) { + return function (module) { + if (module.text) { + module.text = module.text.replace(/^#!/, "//#!"); + } + compile(module); + }; +}; - Require.LintCompiler = function(config, compile) { - return function(module) { - try { - compile(module); - } catch (error) { - if (config.lint) { - Promise.nextTick(function () { - config.lint(module); - }); - } - throw error; +Require.LintCompiler = function(config, compile) { + return function(module) { + try { + compile(module); + } catch (error) { + if (config.lint) { + // TODO: use ASAP + Q.nextTick(function () { + config.lint(module); + }); } - }; + throw error; + } }; - - Require.exposedConfigs = [ - "paths", - "mappings", - "location", - "packageDescription", - "packages", - "modules" - ]; - - Require.makeCompiler = function(config) { - return Require.JsonCompiler( +}; + +Require.exposedConfigs = [ + "paths", + "mappings", + "location", + "packageDescription", + "packages", + "modules" +]; + +Require.makeCompiler = function(config) { + return Require.JsonCompiler( + config, + Require.ShebangCompiler( config, - Require.ShebangCompiler( + Require.DependenciesCompiler( config, - Require.DependenciesCompiler( + Require.LintCompiler( config, - Require.LintCompiler( - config, - Require.Compiler(config) - ) + Require.Compiler(config) ) ) - ); - }; - - Require.JsonCompiler = function (config, compile) { - return function (module) { - var json = (module.location || "").match(/\.json$/); - if (json) { - module.exports = JSON.parse(module.text); - return module; - } else { - return compile(module); - } - }; + ) + ); +}; + +Require.JsonCompiler = function (config, compile) { + return function (module) { + var json = (module.location || "").match(/\.json$/); + if (json) { + module.exports = JSON.parse(module.text); + return module; + } else { + return compile(module); + } }; +}; - // Built-in loader "middleware": +// Built-in loader "middleware": - // Using mappings hash to load modules that match a mapping. - Require.MappingsLoader = function(config, load) { - config.mappings = config.mappings || {}; - config.name = config.name; +// Using mappings hash to load modules that match a mapping. +Require.MappingsLoader = function(config, load) { + config.mappings = config.mappings || {}; + config.name = config.name; - // finds a mapping to follow, if any - return function (id, module) { - var mappings = config.mappings; - var prefixes = Object.keys(mappings); - var length = prefixes.length; + // finds a mapping to follow, if any + return function (id, module) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + var length = prefixes.length; - if (Require.isAbsolute(id)) { - return load(id, module); - } - // TODO: remove this when all code has been migrated off of the autonomous name-space problem + if (Require.isAbsolute(id)) { + return load(id, module); + } + // TODO: remove this when all code has been migrated off of the autonomous name-space problem + if ( + config.name !== void 0 && + id.indexOf(config.name) === 0 && + id.charAt(config.name.length) === "/" + ) { + console.warn("Package reflexive module ignored:", id); + } + var i, prefix; + for (i = 0; i < length; i++) { + prefix = prefixes[i]; if ( - config.name !== void 0 && - id.indexOf(config.name) === 0 && - id.charAt(config.name.length) === "/" + id === prefix || + id.indexOf(prefix) === 0 && + id.charAt(prefix.length) === "/" ) { - console.warn("Package reflexive module ignored:", id); - } - var i, prefix; - for (i = 0; i < length; i++) { - prefix = prefixes[i]; - if ( - id === prefix || - id.indexOf(prefix) === 0 && - id.charAt(prefix.length) === "/" - ) { - /*jshint -W083 */ - var mapping = mappings[prefix]; - var rest = id.slice(prefix.length + 1); - return config.loadPackage(mapping, config) - .then(function (mappingRequire) { - /*jshint +W083 */ - module.mappingRedirect = rest; - module.mappingRequire = mappingRequire; - return mappingRequire.deepLoad(rest, config.location); - }); - } + /*jshint -W083 */ + var mapping = mappings[prefix]; + var rest = id.slice(prefix.length + 1); + return config.loadPackage(mapping, config) + .then(function (mappingRequire) { + /*jshint +W083 */ + module.mappingRedirect = rest; + module.mappingRequire = mappingRequire; + return mappingRequire.deepLoad(rest, config.location); + }); } - return load(id, module); - }; + } + return load(id, module); }; +}; - Require.ExtensionsLoader = function(config, load) { - var extensions = config.extensions || ["js"]; - var loadWithExtension = extensions.reduceRight(function (next, extension) { - return function (id, module) { - return load(id + "." + extension, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " with extensions " + - JSON.stringify(extensions) + " in package at " + - JSON.stringify(config.location) - ); - }); +Require.ExtensionsLoader = function(config, load) { + var extensions = config.extensions || ["js"]; + var loadWithExtension = extensions.reduceRight(function (next, extension) { return function (id, module) { - if (Require.base(id).indexOf(".") !== -1) { - // already has an extension - return load(id, module); - } else { - return loadWithExtension(id, module); - } + return load(id + "." + extension, module) + .fail(function (error) { + if (/^Can't find /.test(error.message)) { + return next(id, module); + } else { + throw error; + } + }); }; + }, function (id, module) { + throw new Error( + "Can't find " + JSON.stringify(id) + " with extensions " + + JSON.stringify(extensions) + " in package at " + + JSON.stringify(config.location) + ); + }); + return function (id, module) { + if (Require.base(id).indexOf(".") !== -1) { + // already has an extension + return load(id, module); + } else { + return loadWithExtension(id, module); + } }; +}; - // Attempts to load using multiple base paths (or one absolute path) with a - // single loader. - Require.PathsLoader = function(config, load) { - var loadFromPaths = config.paths.reduceRight(function (next, path) { - return function (id, module) { - var newId = URL.resolve(path, id); - return load(newId, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " from paths " + - JSON.stringify(config.paths) + " in package at " + - JSON.stringify(config.location) - ); - }); - return function(id, module) { - if (Require.isAbsolute(id)) { - // already fully qualified - return load(id, module); - } else { - return loadFromPaths(id, module); - } +// Attempts to load using multiple base paths (or one absolute path) with a +// single loader. +Require.PathsLoader = function(config, load) { + var loadFromPaths = config.paths.reduceRight(function (next, path) { + return function (id, module) { + var newId = URL.resolve(path, id); + return load(newId, module) + .fail(function (error) { + if (/^Can't find /.test(error.message)) { + return next(id, module); + } else { + throw error; + } + }); }; + }, function (id, module) { + throw new Error( + "Can't find " + JSON.stringify(id) + " from paths " + + JSON.stringify(config.paths) + " in package at " + + JSON.stringify(config.location) + ); + }); + return function(id, module) { + if (Require.isAbsolute(id)) { + // already fully qualified + return load(id, module); + } else { + return loadFromPaths(id, module); + } }; +}; - Require.MemoizedLoader = function (config, load) { - var cache = config.cache = config.cache || {}; - return memoize(load, cache); - }; +Require.MemoizedLoader = function (config, load) { + var cache = config.cache = config.cache || {}; + return memoize(load, cache); +}; - var normalizeId = function (id) { - var match = /^(.*)\.js$/.exec(id); - if (match) { - id = match[1]; +var normalizeId = function (id) { + var match = /^(.*)\.js$/.exec(id); + if (match) { + id = match[1]; + } + return id; +}; + +var memoize = function (callback, cache) { + cache = cache || {}; + return function (key, arg) { + if (!has(cache, key)) { + cache[key] = Q.fcall(callback, key, arg); } - return id; - }; - - var memoize = function (callback, cache) { - cache = cache || {}; - return function (key, arg) { - if (!has(cache, key)) { - cache[key] = Promise.fcall(callback, key, arg); - } - return cache[key]; - }; + return cache[key]; }; +}; -}); diff --git a/spec/node-spec.js b/spec/node-spec.js index 4b172209..33337697 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -1,12 +1,17 @@ -require("./lib/jasmine-promise"); -var PATH = require("path"); -var Require = require("../bootstrap-node"); +require("./lib/jasmine-promise"); +var URL = require("url"); +var Require = require("../node"); // Use async spec to cause Jasmine to wait until the real specs have been loaded describe("Mr on node", function () { + it("must test on node", function () { + expect(typeof __dirname).toBe("string"); + }); it("loads", function () { - return Require.loadPackage(PATH.join(__dirname, "..")) + var location = Require.directoryPathToLocation(__dirname); + location = URL.resolve(location, "../"); + return Require.loadPackage(location) .then(function (mr) { return mr.async("spec/require-spec"); }) diff --git a/spec/production/program.js b/spec/production/program.js index 023b71ac..7c24e9fd 100644 --- a/spec/production/program.js +++ b/spec/production/program.js @@ -3,7 +3,6 @@ module.exports = require.async("dev-dependency") .then(function (a) { throw "should not be able to require dev-dependency in production mode"; }, function (error) { - console.log(error.message); test.assert( /Can\'t require module "dev-dependency" via "program"/.test(error.message), "cannot require dev-dependency in production mode" diff --git a/spec/read/package.json b/spec/read/package.json index 93f632ab..d356a520 100644 --- a/spec/read/package.json +++ b/spec/read/package.json @@ -1,5 +1,5 @@ { "mappings": { - "q": "../../packages/q" + "q": "../../node_modules/q" } } diff --git a/spec/require-spec.js b/spec/require-spec.js index fbe80d40..e50fe282 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -77,22 +77,19 @@ describe("Require", function () { var done; var message; - console.log(test + ":", "START"); + //console.log(test + ":", "START"); - return require.loadPackage( - module.directory + test + "/", - {} - ) + return require.loadPackage(module.directory + test + "/", {}) .then(function (pkg) { pkg.inject("test", { print: function (_message, level) { - console.log(test + ":", _message); + //console.log(test + ":", _message); if (_message === "DONE") { message = _message; } }, assert: function (guard, message) { - console.log(test + ":", guard ? "PASS" : "FAIL", message); + //console.log(test + ":", guard ? "PASS" : "FAIL", message); expect(!!guard).toBe(true); } }); @@ -100,11 +97,9 @@ describe("Require", function () { return pkg.async("program"); }) .then(function () { - }, function (reason, error) { - spec.fail(error || reason); - }) - .fin(function () { expect(message).toBe("DONE"); + }, function (reason) { + spec.fail(reason); }); }); diff --git a/spec/run.html b/spec/run.html index 3abc3889..7168d63e 100644 --- a/spec/run.html +++ b/spec/run.html @@ -8,7 +8,7 @@ - + diff --git a/spec/sandbox/package.json b/spec/sandbox/package.json index d0f4c5ac..14b0e742 100644 --- a/spec/sandbox/package.json +++ b/spec/sandbox/package.json @@ -1,7 +1,7 @@ { "mappings": { "mr": "../..", - "q": "../../packages/q" + "q": "../../node_modules/q" }, "redirects": { "d": "a" From d5e214c67de9b0fd124a3c74e0d7803cf0c66674 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Aug 2013 09:45:32 -0700 Subject: [PATCH 18/64] Add support for compilers --- boot.js | 38 +++++++++++++++++++++++++--------- boot/preload-boilerplate.js | 38 +++++++++++++++++++++++++--------- require.js | 38 +++++++++++++++++++++++++--------- spec/compiler/hello.text | 1 + spec/compiler/package.json | 7 +++++++ spec/compiler/program.js | 6 ++++++ spec/compiler/text-compiler.js | 3 +++ spec/require-spec.js | 1 + 8 files changed, 102 insertions(+), 30 deletions(-) create mode 100644 spec/compiler/hello.text create mode 100644 spec/compiler/package.json create mode 100644 spec/compiler/program.js create mode 100644 spec/compiler/text-compiler.js diff --git a/boot.js b/boot.js index 5eeb1bfd..6365548c 100644 --- a/boot.js +++ b/boot.js @@ -475,6 +475,7 @@ Require.makeRequire = function (config) { config.compile = config.compile || config.makeCompiler(config); config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; + config.compilers = config.compilers || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -510,7 +511,7 @@ Require.makeRequire = function (config) { } // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { + var load = memoize(function (topId, viaId, loading) { var module = getModuleDescriptor(topId); return Q.fcall(function () { // if not already loaded, already instantiated, or @@ -525,15 +526,23 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); + var extension = Require.extension(topId); + if (config.compilers[extension]) { + var compilerId = config.compilers[extension]; + return deepLoad(compilerId, "", loading) + .then(function () { + var compile = require(compilerId); + compile(module); + }); + } else { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } } }); }); @@ -554,6 +563,7 @@ Require.makeRequire = function (config) { .then(function () { // load the transitive dependencies using the magic of // recursion. + var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { depId = resolve(depId, topId); // create dependees set, purely for debug purposes @@ -973,6 +983,7 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; + config.compilers = description.compilers; if (description.production !== void 0) { config.production = description.production; @@ -1128,6 +1139,13 @@ Require.base = function (location) { .match(/([^\/]+$|^\/$|^$)/)[1]; }; +Require.extension = function (location) { + var match = /\.([^\/]+)$/.exec(location); + if (match) { + return match[1]; + } +}; + // Tests whether the location or URL is a absolute. Require.isAbsolute = function(location) { return (/^[\w\-]+:/).test(location); diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 4126ef4c..3267884f 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -547,6 +547,7 @@ Require.makeRequire = function (config) { config.compile = config.compile || config.makeCompiler(config); config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; + config.compilers = config.compilers || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -582,7 +583,7 @@ Require.makeRequire = function (config) { } // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { + var load = memoize(function (topId, viaId, loading) { var module = getModuleDescriptor(topId); return Q.fcall(function () { // if not already loaded, already instantiated, or @@ -597,15 +598,23 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); + var extension = Require.extension(topId); + if (config.compilers[extension]) { + var compilerId = config.compilers[extension]; + return deepLoad(compilerId, "", loading) + .then(function () { + var compile = require(compilerId); + compile(module); + }); + } else { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } } }); }); @@ -626,6 +635,7 @@ Require.makeRequire = function (config) { .then(function () { // load the transitive dependencies using the magic of // recursion. + var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { depId = resolve(depId, topId); // create dependees set, purely for debug purposes @@ -1045,6 +1055,7 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; + config.compilers = description.compilers; if (description.production !== void 0) { config.production = description.production; @@ -1200,6 +1211,13 @@ Require.base = function (location) { .match(/([^\/]+$|^\/$|^$)/)[1]; }; +Require.extension = function (location) { + var match = /\.([^\/]+)$/.exec(location); + if (match) { + return match[1]; + } +}; + // Tests whether the location or URL is a absolute. Require.isAbsolute = function(location) { return (/^[\w\-]+:/).test(location); diff --git a/require.js b/require.js index 3f0c9aeb..76f5c835 100644 --- a/require.js +++ b/require.js @@ -35,6 +35,7 @@ Require.makeRequire = function (config) { config.compile = config.compile || config.makeCompiler(config); config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; + config.compilers = config.compilers || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -70,7 +71,7 @@ Require.makeRequire = function (config) { } // Ensures a module definition is loaded, compiled, analyzed - var load = memoize(function (topId, viaId) { + var load = memoize(function (topId, viaId, loading) { var module = getModuleDescriptor(topId); return Q.fcall(function () { // if not already loaded, already instantiated, or @@ -85,15 +86,23 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - config.compile(module); - var dependencies = - module.dependencies = - module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); + var extension = Require.extension(topId); + if (config.compilers[extension]) { + var compilerId = config.compilers[extension]; + return deepLoad(compilerId, "", loading) + .then(function () { + var compile = require(compilerId); + compile(module); + }); + } else { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } } }); }); @@ -114,6 +123,7 @@ Require.makeRequire = function (config) { .then(function () { // load the transitive dependencies using the magic of // recursion. + var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { depId = resolve(depId, topId); // create dependees set, purely for debug purposes @@ -533,6 +543,7 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; + config.compilers = description.compilers; if (description.production !== void 0) { config.production = description.production; @@ -688,6 +699,13 @@ Require.base = function (location) { .match(/([^\/]+$|^\/$|^$)/)[1]; }; +Require.extension = function (location) { + var match = /\.([^\/]+)$/.exec(location); + if (match) { + return match[1]; + } +}; + // Tests whether the location or URL is a absolute. Require.isAbsolute = function(location) { return (/^[\w\-]+:/).test(location); diff --git a/spec/compiler/hello.text b/spec/compiler/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/compiler/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/compiler/package.json b/spec/compiler/package.json new file mode 100644 index 00000000..dde692b1 --- /dev/null +++ b/spec/compiler/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "compilers": { + "text": "text-compiler" + } +} diff --git a/spec/compiler/program.js b/spec/compiler/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/compiler/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + diff --git a/spec/compiler/text-compiler.js b/spec/compiler/text-compiler.js new file mode 100644 index 00000000..802085e0 --- /dev/null +++ b/spec/compiler/text-compiler.js @@ -0,0 +1,3 @@ +module.exports = function compile(module) { + module.exports = module.text; +}; diff --git a/spec/require-spec.js b/spec/require-spec.js index e50fe282..4eeab64b 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -64,6 +64,7 @@ describe("Require", function () { "read", "main-name", "main", + "compiler", "sandbox" ].forEach(function (test) { if (typeof test === "object") { From 0aed9bce285b26aabf9c228041caf0398e55a3fd Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 17 Sep 2013 11:39:57 -0700 Subject: [PATCH 19/64] Add support for translators --- boot.js | 33 ++++++++++++++++++++++-------- boot/preload-boilerplate.js | 33 ++++++++++++++++++++++-------- require.js | 33 ++++++++++++++++++++++-------- spec/require-spec.js | 1 + spec/translator/hello.text | 1 + spec/translator/package.json | 7 +++++++ spec/translator/program.js | 6 ++++++ spec/translator/text-translator.js | 3 +++ 8 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 spec/translator/hello.text create mode 100644 spec/translator/package.json create mode 100644 spec/translator/program.js create mode 100644 spec/translator/text-translator.js diff --git a/boot.js b/boot.js index 6365548c..a0488b27 100644 --- a/boot.js +++ b/boot.js @@ -476,6 +476,7 @@ Require.makeRequire = function (config) { config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; config.compilers = config.compilers || {}; + config.translators = config.translators || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -535,14 +536,29 @@ Require.makeRequire = function (config) { compile(module); }); } else { - config.compile(module); - var dependencies = module.dependencies = module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } + return Q.fcall(function () { + if (config.translators[extension]) { + var translatorId = config.translators[extension]; + // TODO try to load translator related modules in a + // parallel module system so that they do not get + // bundled + return deepLoad(translatorId, "", loading) + .then(function () { + var translate = require(translatorId); + module.text = translate(module.text, module); + }); + } + }) + .then(function () { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } + }); } }); }); @@ -984,6 +1000,7 @@ function configurePackage(location, description, parent) { config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; config.compilers = description.compilers; + config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 3267884f..32d734ab 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -548,6 +548,7 @@ Require.makeRequire = function (config) { config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; config.compilers = config.compilers || {}; + config.translators = config.translators || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -607,14 +608,29 @@ Require.makeRequire = function (config) { compile(module); }); } else { - config.compile(module); - var dependencies = module.dependencies = module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } + return Q.fcall(function () { + if (config.translators[extension]) { + var translatorId = config.translators[extension]; + // TODO try to load translator related modules in a + // parallel module system so that they do not get + // bundled + return deepLoad(translatorId, "", loading) + .then(function () { + var translate = require(translatorId); + module.text = translate(module.text, module); + }); + } + }) + .then(function () { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } + }); } }); }); @@ -1056,6 +1072,7 @@ function configurePackage(location, description, parent) { config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; config.compilers = description.compilers; + config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; diff --git a/require.js b/require.js index 76f5c835..94e1ba77 100644 --- a/require.js +++ b/require.js @@ -36,6 +36,7 @@ Require.makeRequire = function (config) { config.parseDependencies = config.parseDependencies || Require.parseDependencies; config.read = config.read || Require.read; config.compilers = config.compilers || {}; + config.translators = config.translators || {}; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -95,14 +96,29 @@ Require.makeRequire = function (config) { compile(module); }); } else { - config.compile(module); - var dependencies = module.dependencies = module.dependencies || []; - if (module.redirect !== void 0) { - dependencies.push(module.redirect); - } - if (module.extraDependencies !== void 0) { - Array.prototype.push.apply(module.dependencies, module.extraDependencies); - } + return Q.fcall(function () { + if (config.translators[extension]) { + var translatorId = config.translators[extension]; + // TODO try to load translator related modules in a + // parallel module system so that they do not get + // bundled + return deepLoad(translatorId, "", loading) + .then(function () { + var translate = require(translatorId); + module.text = translate(module.text, module); + }); + } + }) + .then(function () { + config.compile(module); + var dependencies = module.dependencies = module.dependencies || []; + if (module.redirect !== void 0) { + dependencies.push(module.redirect); + } + if (module.extraDependencies !== void 0) { + Array.prototype.push.apply(module.dependencies, module.extraDependencies); + } + }); } }); }); @@ -544,6 +560,7 @@ function configurePackage(location, description, parent) { config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; config.compilers = description.compilers; + config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; diff --git a/spec/require-spec.js b/spec/require-spec.js index 4eeab64b..f125b143 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -65,6 +65,7 @@ describe("Require", function () { "main-name", "main", "compiler", + "translator", "sandbox" ].forEach(function (test) { if (typeof test === "object") { diff --git a/spec/translator/hello.text b/spec/translator/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/translator/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/translator/package.json b/spec/translator/package.json new file mode 100644 index 00000000..2f577d14 --- /dev/null +++ b/spec/translator/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "translators": { + "text": "text-translator" + } +} diff --git a/spec/translator/program.js b/spec/translator/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/translator/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + diff --git a/spec/translator/text-translator.js b/spec/translator/text-translator.js new file mode 100644 index 00000000..efcaef9d --- /dev/null +++ b/spec/translator/text-translator.js @@ -0,0 +1,3 @@ +module.exports = function (text) { + return "module.exports = " + JSON.stringify(text) + ";"; +}; From 22ca861bee365a4cd25a81378543b13ead6ae2de Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Aug 2013 10:13:08 -0700 Subject: [PATCH 20/64] Add support for translators --- spec/require-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/require-spec.js b/spec/require-spec.js index f125b143..4f285cde 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -64,9 +64,9 @@ describe("Require", function () { "read", "main-name", "main", + "sandbox", "compiler", - "translator", - "sandbox" + "translator" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { From e71af5a5cda4ff04e222ef3e6cf467f59d23bb1b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Aug 2013 11:17:49 -0700 Subject: [PATCH 21/64] Inherit translators and compilers from dependencies --- boot.js | 63 ++++++++++++++++--- boot/preload-boilerplate.js | 63 ++++++++++++++++--- require.js | 63 ++++++++++++++++--- spec/compiler-package/hello.text | 1 + .../node_modules/mr-text/package.json | 7 +++ .../node_modules/mr-text/text-compiler.js | 3 + spec/compiler-package/package.json | 7 +++ spec/compiler-package/program.js | 6 ++ spec/identify/package.json | 4 +- spec/require-spec.js | 4 +- spec/translator-package/hello.text | 1 + .../node_modules/mr-text/mr-text.js | 3 + .../node_modules/mr-text/package.json | 8 +++ spec/translator-package/package.json | 7 +++ spec/translator-package/program.js | 6 ++ 15 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 spec/compiler-package/hello.text create mode 100644 spec/compiler-package/node_modules/mr-text/package.json create mode 100644 spec/compiler-package/node_modules/mr-text/text-compiler.js create mode 100644 spec/compiler-package/package.json create mode 100644 spec/compiler-package/program.js create mode 100644 spec/translator-package/hello.text create mode 100644 spec/translator-package/node_modules/mr-text/mr-text.js create mode 100644 spec/translator-package/node_modules/mr-text/package.json create mode 100644 spec/translator-package/package.json create mode 100644 spec/translator-package/program.js diff --git a/boot.js b/boot.js index a0488b27..a132bb83 100644 --- a/boot.js +++ b/boot.js @@ -490,6 +490,7 @@ Require.makeRequire = function (config) { if (!has(modules, lookupId)) { modules[lookupId] = { id: id, + extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION require: require }; @@ -527,9 +528,8 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - var extension = Require.extension(topId); - if (config.compilers[extension]) { - var compilerId = config.compilers[extension]; + if (config.compilers[module.extension]) { + var compilerId = config.compilers[module.extension]; return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); @@ -537,8 +537,8 @@ Require.makeRequire = function (config) { }); } else { return Q.fcall(function () { - if (config.translators[extension]) { - var translatorId = config.translators[extension]; + if (config.translators[module.extension]) { + var translatorId = config.translators[module.extension]; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled @@ -900,12 +900,19 @@ Require.loadPackage = function (dependency, config) { return loadedPackages[location]; }; - config.loadPackage = function (dependency, viaConfig) { + config.loadPackage = function (dependency, viaConfig, loading) { dependency = normalizeDependency(dependency, viaConfig); if (!dependency.location) { throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } var location = dependency.location; + + loading = loading || {}; + if (loading[location]) { + return Q(); + } + loading[location] = true; + if (!loadingPackages[location]) { loadingPackages[location] = Require.loadPackageDescription(dependency, config) .then(function (packageDescription) { @@ -916,7 +923,14 @@ Require.loadPackage = function (dependency, config) { ); var pkg = Require.makeRequire(subconfig); loadedPackages[location] = pkg; - return pkg; + return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { + var dependency = subconfig.mappings[prefix]; + return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + })) + .then(function () { + postConfigurePackage(subconfig); + }) + .thenResolve(pkg); }); loadingPackages[location].done(); } @@ -1085,8 +1099,8 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production? description.devDependencies : null] + // dependencies, devDependencies if not in production, if not installed by NPM + [description.dependencies, description._id || description.production ? null : description.devDependencies] .forEach(function (dependencies) { if (!dependencies) { return; @@ -1116,6 +1130,37 @@ function configurePackage(location, description, parent) { return config; } +function postConfigurePackage(config) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + prefixes.forEach(function (prefix) { + + var dependency = mappings[prefix]; + if (!config.hasPackage(dependency)) { + return; + } + var package = config.getPackage(dependency); + var extensions; + + // reference translators + var myTranslators = config.translators = config.translators || {}; + var theirTranslators = package.config.translators; + extensions = Object.keys(theirTranslators); + extensions.forEach(function (extension) { + myTranslators[extension] = prefix + "/" + theirTranslators[extension]; + }); + + // reference compilers + var myCompilers = config.compilers = config.compilers || {}; + var theirCompilers = package.config.compilers; + extensions = Object.keys(theirCompilers); + extensions.forEach(function (extension) { + myCompilers[extension] = prefix + "/" + theirCompilers[extension]; + }); + + }); +} + // Helper functions: function has(object, property) { diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 32d734ab..53bc1ac1 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -562,6 +562,7 @@ Require.makeRequire = function (config) { if (!has(modules, lookupId)) { modules[lookupId] = { id: id, + extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION require: require }; @@ -599,9 +600,8 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - var extension = Require.extension(topId); - if (config.compilers[extension]) { - var compilerId = config.compilers[extension]; + if (config.compilers[module.extension]) { + var compilerId = config.compilers[module.extension]; return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); @@ -609,8 +609,8 @@ Require.makeRequire = function (config) { }); } else { return Q.fcall(function () { - if (config.translators[extension]) { - var translatorId = config.translators[extension]; + if (config.translators[module.extension]) { + var translatorId = config.translators[module.extension]; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled @@ -972,12 +972,19 @@ Require.loadPackage = function (dependency, config) { return loadedPackages[location]; }; - config.loadPackage = function (dependency, viaConfig) { + config.loadPackage = function (dependency, viaConfig, loading) { dependency = normalizeDependency(dependency, viaConfig); if (!dependency.location) { throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } var location = dependency.location; + + loading = loading || {}; + if (loading[location]) { + return Q(); + } + loading[location] = true; + if (!loadingPackages[location]) { loadingPackages[location] = Require.loadPackageDescription(dependency, config) .then(function (packageDescription) { @@ -988,7 +995,14 @@ Require.loadPackage = function (dependency, config) { ); var pkg = Require.makeRequire(subconfig); loadedPackages[location] = pkg; - return pkg; + return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { + var dependency = subconfig.mappings[prefix]; + return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + })) + .then(function () { + postConfigurePackage(subconfig); + }) + .thenResolve(pkg); }); loadingPackages[location].done(); } @@ -1157,8 +1171,8 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production? description.devDependencies : null] + // dependencies, devDependencies if not in production, if not installed by NPM + [description.dependencies, description._id || description.production ? null : description.devDependencies] .forEach(function (dependencies) { if (!dependencies) { return; @@ -1188,6 +1202,37 @@ function configurePackage(location, description, parent) { return config; } +function postConfigurePackage(config) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + prefixes.forEach(function (prefix) { + + var dependency = mappings[prefix]; + if (!config.hasPackage(dependency)) { + return; + } + var package = config.getPackage(dependency); + var extensions; + + // reference translators + var myTranslators = config.translators = config.translators || {}; + var theirTranslators = package.config.translators; + extensions = Object.keys(theirTranslators); + extensions.forEach(function (extension) { + myTranslators[extension] = prefix + "/" + theirTranslators[extension]; + }); + + // reference compilers + var myCompilers = config.compilers = config.compilers || {}; + var theirCompilers = package.config.compilers; + extensions = Object.keys(theirCompilers); + extensions.forEach(function (extension) { + myCompilers[extension] = prefix + "/" + theirCompilers[extension]; + }); + + }); +} + // Helper functions: function has(object, property) { diff --git a/require.js b/require.js index 94e1ba77..b6480b6b 100644 --- a/require.js +++ b/require.js @@ -50,6 +50,7 @@ Require.makeRequire = function (config) { if (!has(modules, lookupId)) { modules[lookupId] = { id: id, + extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION require: require }; @@ -87,9 +88,8 @@ Require.makeRequire = function (config) { }) .then(function () { // compile and analyze dependencies - var extension = Require.extension(topId); - if (config.compilers[extension]) { - var compilerId = config.compilers[extension]; + if (config.compilers[module.extension]) { + var compilerId = config.compilers[module.extension]; return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); @@ -97,8 +97,8 @@ Require.makeRequire = function (config) { }); } else { return Q.fcall(function () { - if (config.translators[extension]) { - var translatorId = config.translators[extension]; + if (config.translators[module.extension]) { + var translatorId = config.translators[module.extension]; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled @@ -460,12 +460,19 @@ Require.loadPackage = function (dependency, config) { return loadedPackages[location]; }; - config.loadPackage = function (dependency, viaConfig) { + config.loadPackage = function (dependency, viaConfig, loading) { dependency = normalizeDependency(dependency, viaConfig); if (!dependency.location) { throw new Error("Can't find dependency: " + JSON.stringify(dependency) + " from " + config.location); } var location = dependency.location; + + loading = loading || {}; + if (loading[location]) { + return Q(); + } + loading[location] = true; + if (!loadingPackages[location]) { loadingPackages[location] = Require.loadPackageDescription(dependency, config) .then(function (packageDescription) { @@ -476,7 +483,14 @@ Require.loadPackage = function (dependency, config) { ); var pkg = Require.makeRequire(subconfig); loadedPackages[location] = pkg; - return pkg; + return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { + var dependency = subconfig.mappings[prefix]; + return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + })) + .then(function () { + postConfigurePackage(subconfig); + }) + .thenResolve(pkg); }); loadingPackages[location].done(); } @@ -645,8 +659,8 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; - // dependencies, devDependencies if not in production - [description.dependencies, !config.production? description.devDependencies : null] + // dependencies, devDependencies if not in production, if not installed by NPM + [description.dependencies, description._id || description.production ? null : description.devDependencies] .forEach(function (dependencies) { if (!dependencies) { return; @@ -676,6 +690,37 @@ function configurePackage(location, description, parent) { return config; } +function postConfigurePackage(config) { + var mappings = config.mappings; + var prefixes = Object.keys(mappings); + prefixes.forEach(function (prefix) { + + var dependency = mappings[prefix]; + if (!config.hasPackage(dependency)) { + return; + } + var package = config.getPackage(dependency); + var extensions; + + // reference translators + var myTranslators = config.translators = config.translators || {}; + var theirTranslators = package.config.translators; + extensions = Object.keys(theirTranslators); + extensions.forEach(function (extension) { + myTranslators[extension] = prefix + "/" + theirTranslators[extension]; + }); + + // reference compilers + var myCompilers = config.compilers = config.compilers || {}; + var theirCompilers = package.config.compilers; + extensions = Object.keys(theirCompilers); + extensions.forEach(function (extension) { + myCompilers[extension] = prefix + "/" + theirCompilers[extension]; + }); + + }); +} + // Helper functions: function has(object, property) { diff --git a/spec/compiler-package/hello.text b/spec/compiler-package/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/compiler-package/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/compiler-package/node_modules/mr-text/package.json b/spec/compiler-package/node_modules/mr-text/package.json new file mode 100644 index 00000000..cae5888f --- /dev/null +++ b/spec/compiler-package/node_modules/mr-text/package.json @@ -0,0 +1,7 @@ +{ + "name": "mr-text", + "version": "*", + "compilers": { + "text": "text-compiler" + } +} diff --git a/spec/compiler-package/node_modules/mr-text/text-compiler.js b/spec/compiler-package/node_modules/mr-text/text-compiler.js new file mode 100644 index 00000000..802085e0 --- /dev/null +++ b/spec/compiler-package/node_modules/mr-text/text-compiler.js @@ -0,0 +1,3 @@ +module.exports = function compile(module) { + module.exports = module.text; +}; diff --git a/spec/compiler-package/package.json b/spec/compiler-package/package.json new file mode 100644 index 00000000..6bef9c7e --- /dev/null +++ b/spec/compiler-package/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "dependencies": { + "mr-text": "*" + } +} diff --git a/spec/compiler-package/program.js b/spec/compiler-package/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/compiler-package/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + diff --git a/spec/identify/package.json b/spec/identify/package.json index 4f497ea6..e0f0b820 100644 --- a/spec/identify/package.json +++ b/spec/identify/package.json @@ -2,8 +2,6 @@ "name": "identify", "dependencies": { "cyclic": "*", - "a": "*", - "x": "*", - "y": "*" + "x": "*" } } diff --git a/spec/require-spec.js b/spec/require-spec.js index 4f285cde..f18c12b6 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -66,7 +66,9 @@ describe("Require", function () { "main", "sandbox", "compiler", - "translator" + "translator", + "compiler-package", + "translator-package" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { diff --git a/spec/translator-package/hello.text b/spec/translator-package/hello.text new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/spec/translator-package/hello.text @@ -0,0 +1 @@ +Hello, World! diff --git a/spec/translator-package/node_modules/mr-text/mr-text.js b/spec/translator-package/node_modules/mr-text/mr-text.js new file mode 100644 index 00000000..efcaef9d --- /dev/null +++ b/spec/translator-package/node_modules/mr-text/mr-text.js @@ -0,0 +1,3 @@ +module.exports = function (text) { + return "module.exports = " + JSON.stringify(text) + ";"; +}; diff --git a/spec/translator-package/node_modules/mr-text/package.json b/spec/translator-package/node_modules/mr-text/package.json new file mode 100644 index 00000000..1d5c94d4 --- /dev/null +++ b/spec/translator-package/node_modules/mr-text/package.json @@ -0,0 +1,8 @@ +{ + "name": "mr-text", + "version": "", + "main": "mr-text", + "translators": { + "text": "mr-text" + } +} diff --git a/spec/translator-package/package.json b/spec/translator-package/package.json new file mode 100644 index 00000000..6bef9c7e --- /dev/null +++ b/spec/translator-package/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "dependencies": { + "mr-text": "*" + } +} diff --git a/spec/translator-package/program.js b/spec/translator-package/program.js new file mode 100644 index 00000000..e1cbc835 --- /dev/null +++ b/spec/translator-package/program.js @@ -0,0 +1,6 @@ + +var test = require("test"); +var hello = require("./hello.text"); +test.assert(hello === 'Hello, World!\n'); +test.print("DONE", "info"); + From f798d7be971ec7d5d8503c2ed55a985a6bf49332 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:23:53 -0700 Subject: [PATCH 22/64] Anonymous require in boilerplate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of allocating a require function for every call, only create the closure if it’s needed. --- boot.js | 2 +- boot/boilerplate.js | 2 +- boot/preload-boilerplate.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/boot.js b/boot.js index a132bb83..cea1a2ca 100644 --- a/boot.js +++ b/boot.js @@ -14,7 +14,7 @@ var module = this; if (!module.exports) { module.exports = {}; - function require(id) { + var require = function (id) { var index = module.dependencies[id]; var dependency = modules[index]; if (!dependency) diff --git a/boot/boilerplate.js b/boot/boilerplate.js index 5d53d4ff..9d82d4ef 100644 --- a/boot/boilerplate.js +++ b/boot/boilerplate.js @@ -14,7 +14,7 @@ var module = this; if (!module.exports) { module.exports = {}; - function require(id) { + var require = function (id) { var index = module.dependencies[id]; var dependency = modules[index]; if (!dependency) diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 53bc1ac1..24845996 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -14,7 +14,7 @@ var module = this; if (!module.exports) { module.exports = {}; - function require(id) { + var require = function (id) { var index = module.dependencies[id]; var dependency = modules[index]; if (!dependency) From 4675412517a42fea3a7489dac790d400b8cb6b27 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:26:46 -0700 Subject: [PATCH 23/64] Support Node.js __filename __dirname If you can't beat 'em, join 'em. Also, some Node.js/npm packages depend on this and we would like them to work without modification, even if they're using bad nonstandard variable names that shouldn't have been made in the first place. --- boot.js | 6 ++++-- boot/preload-boilerplate.js | 6 ++++-- browser.js | 2 +- node.js | 2 +- require.js | 4 +++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/boot.js b/boot.js index cea1a2ca..c1cd7992 100644 --- a/boot.js +++ b/boot.js @@ -168,7 +168,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { var __FILE__String = "__FILE__", DoubleUnderscoreString = "__", globalEvalConstantA = "(function ", - globalEvalConstantB = "(require, exports, module) {", + globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; Require.Compiler = function (config) { @@ -674,7 +674,9 @@ Require.makeRequire = function (config) { void 0, // this (defaults to global) makeRequire(topId), // require module.exports, // exports - module // module + module, // module + module.location, // __filename + module.directory // __dirname ); // EXTENSION diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 24845996..82555c7d 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -221,7 +221,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { var __FILE__String = "__FILE__", DoubleUnderscoreString = "__", globalEvalConstantA = "(function ", - globalEvalConstantB = "(require, exports, module) {", + globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; Require.Compiler = function (config) { @@ -746,7 +746,9 @@ Require.makeRequire = function (config) { void 0, // this (defaults to global) makeRequire(topId), // require module.exports, // exports - module // module + module, // module + module.location, // __filename + module.directory // __dirname ); // EXTENSION diff --git a/browser.js b/browser.js index 518d09c9..2bd5d93c 100644 --- a/browser.js +++ b/browser.js @@ -84,7 +84,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { var __FILE__String = "__FILE__", DoubleUnderscoreString = "__", globalEvalConstantA = "(function ", - globalEvalConstantB = "(require, exports, module) {", + globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; Require.Compiler = function (config) { diff --git a/node.js b/node.js index 5e66e088..a3b4c92b 100644 --- a/node.js +++ b/node.js @@ -77,7 +77,7 @@ Require.read = function read(location) { // Can be overriden by the platform to make the engine aware of the source path. Uses sourceURL hack by default. Require.Compiler = function Compiler(config) { config.scope = config.scope || {}; - var names = ["require", "exports", "module"]; + var names = ["require", "exports", "module", "__filename", "__dirname"]; var scopeNames = Object.keys(config.scope); names.push.apply(names, scopeNames); return function (module) { diff --git a/require.js b/require.js index b6480b6b..20c8a8d9 100644 --- a/require.js +++ b/require.js @@ -234,7 +234,9 @@ Require.makeRequire = function (config) { void 0, // this (defaults to global) makeRequire(topId), // require module.exports, // exports - module // module + module, // module + module.location, // __filename + module.directory // __dirname ); // EXTENSION From 1545834af45969e347b688d6645c9ed0e6bb9ed4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:28:47 -0700 Subject: [PATCH 24/64] Delete module text after compile Since it just makes the application heavier at run time. I did have delusions about retaining it and scraping the text for debug purposes, but those illusions can be restored some other day. --- boot.js | 3 +++ boot/preload-boilerplate.js | 3 +++ browser.js | 3 +++ 3 files changed, 9 insertions(+) diff --git a/boot.js b/boot.js index c1cd7992..5c3bc28c 100644 --- a/boot.js +++ b/boot.js @@ -193,6 +193,9 @@ Require.Compiler = function (config) { try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); + if (!config.saveText) { + delete module.text; // save some space + } } catch (exception) { exception.message = exception.message + " in " + module.location; throw exception; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 82555c7d..84c3af19 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -246,6 +246,9 @@ Require.Compiler = function (config) { try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); + if (!config.saveText) { + delete module.text; // save some space + } } catch (exception) { exception.message = exception.message + " in " + module.location; throw exception; diff --git a/browser.js b/browser.js index 2bd5d93c..dede15d5 100644 --- a/browser.js +++ b/browser.js @@ -109,6 +109,9 @@ Require.Compiler = function (config) { try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); + if (!config.saveText) { + delete module.text; // save some space + } } catch (exception) { exception.message = exception.message + " in " + module.location; throw exception; From 4c902fcf93ac5a6dcd595255f483116da59b4414 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:31:31 -0700 Subject: [PATCH 25/64] Use module.require that resolves relative ids Previously, each module descriptor shared the root package object. This made identity checks on the package object possible for test for package equivalence, but `require.location` is a better practice anyway. The advantage of this new approach is that `module.require` is the same as `require` and can be used to resolve module ids releative to the module. --- boot.js | 4 ++-- boot/preload-boilerplate.js | 4 ++-- require.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/boot.js b/boot.js index 5c3bc28c..7e289189 100644 --- a/boot.js +++ b/boot.js @@ -495,7 +495,7 @@ Require.makeRequire = function (config) { id: id, extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION - require: require + require: makeRequire(id) }; } return modules[lookupId]; @@ -675,7 +675,7 @@ Require.makeRequire = function (config) { var returnValue = module.factory.call( // in the context of the module: void 0, // this (defaults to global) - makeRequire(topId), // require + module.require, // require module.exports, // exports module, // module module.location, // __filename diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 84c3af19..e7317500 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -567,7 +567,7 @@ Require.makeRequire = function (config) { id: id, extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION - require: require + require: makeRequire(id) }; } return modules[lookupId]; @@ -747,7 +747,7 @@ Require.makeRequire = function (config) { var returnValue = module.factory.call( // in the context of the module: void 0, // this (defaults to global) - makeRequire(topId), // require + module.require, // require module.exports, // exports module, // module module.location, // __filename diff --git a/require.js b/require.js index 20c8a8d9..805201ec 100644 --- a/require.js +++ b/require.js @@ -52,7 +52,7 @@ Require.makeRequire = function (config) { id: id, extension: Require.extension(id), display: (config.name || config.location) + "#" + id, // EXTENSION - require: require + require: makeRequire(id) }; } return modules[lookupId]; @@ -232,7 +232,7 @@ Require.makeRequire = function (config) { var returnValue = module.factory.call( // in the context of the module: void 0, // this (defaults to global) - makeRequire(topId), // require + module.require, // require module.exports, // exports module, // module module.location, // __filename From f01d4836a84e393cb7675b12692710af7204e771 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:34:09 -0700 Subject: [PATCH 26/64] Add package to require errors --- boot.js | 1 + boot/preload-boilerplate.js | 1 + require.js | 1 + spec/not-found/package.json | 2 +- spec/not-found/program.js | 2 +- 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/boot.js b/boot.js index 7e289189..68e96737 100644 --- a/boot.js +++ b/boot.js @@ -640,6 +640,7 @@ Require.makeRequire = function (config) { error.message = ( "Can't require module " + JSON.stringify(module.id) + " via " + JSON.stringify(viaId) + + " in " + JSON.stringify(config.name || config.location) + " because " + error.message ); throw error; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index e7317500..5d0b9004 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -712,6 +712,7 @@ Require.makeRequire = function (config) { error.message = ( "Can't require module " + JSON.stringify(module.id) + " via " + JSON.stringify(viaId) + + " in " + JSON.stringify(config.name || config.location) + " because " + error.message ); throw error; diff --git a/require.js b/require.js index 805201ec..0a039e75 100644 --- a/require.js +++ b/require.js @@ -197,6 +197,7 @@ Require.makeRequire = function (config) { error.message = ( "Can't require module " + JSON.stringify(module.id) + " via " + JSON.stringify(viaId) + + " in " + JSON.stringify(config.name || config.location) + " because " + error.message ); throw error; diff --git a/spec/not-found/package.json b/spec/not-found/package.json index 0967ef42..95bea58b 100644 --- a/spec/not-found/package.json +++ b/spec/not-found/package.json @@ -1 +1 @@ -{} +{"name": "not-found-spec"} diff --git a/spec/not-found/program.js b/spec/not-found/program.js index b7f0940f..41453627 100644 --- a/spec/not-found/program.js +++ b/spec/not-found/program.js @@ -35,6 +35,6 @@ try { require("a"); } catch (exception) { test.print(exception.message); - test.assert(/Can't require module "a" via "program" because Can't XHR /.test(exception.message)); + test.assert(/Can't require module "a" via "program" in "not-found-spec" because Can't XHR /.test(exception.message)); } test.print('DONE', 'info'); From 7dc899765d49f03909e3aa1c4c0d18a0ac46545d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:34:46 -0700 Subject: [PATCH 27/64] Fix overlays + compilers and redirects These features previously did not compose. Now, compilers and redirects are captured from the configuration after overlays have been applied. --- boot.js | 6 ++++-- boot/preload-boilerplate.js | 6 ++++-- require.js | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/boot.js b/boot.js index 68e96737..7532a5cc 100644 --- a/boot.js +++ b/boot.js @@ -1019,8 +1019,6 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; - config.compilers = description.compilers; - config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; @@ -1133,6 +1131,10 @@ function configurePackage(location, description, parent) { }); config.mappings = mappings; + // compilers, translators, redirect patterns + config.compilers = description.compilers; + config.translators = description.translators; + return config; } diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 5d0b9004..ed3200a6 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -1091,8 +1091,6 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; - config.compilers = description.compilers; - config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; @@ -1205,6 +1203,10 @@ function configurePackage(location, description, parent) { }); config.mappings = mappings; + // compilers, translators, redirect patterns + config.compilers = description.compilers; + config.translators = description.translators; + return config; } diff --git a/require.js b/require.js index 0a039e75..60bf7de2 100644 --- a/require.js +++ b/require.js @@ -576,8 +576,6 @@ function configurePackage(location, description, parent) { config.location = location || Require.getLocation(); config.packageDescription = description; config.useScriptInjection = description.useScriptInjection; - config.compilers = description.compilers; - config.translators = description.translators; if (description.production !== void 0) { config.production = description.production; @@ -690,6 +688,10 @@ function configurePackage(location, description, parent) { }); config.mappings = mappings; + // compilers, translators, redirect patterns + config.compilers = description.compilers; + config.translators = description.translators; + return config; } From 4d7ba0d92261fe38a9f242f9dd9c16e8440643fe Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Sep 2013 22:38:28 -0700 Subject: [PATCH 28/64] Normalize redirects This has the happy side-benefit of increasing support for packages designed for Browserify, since the `browser` field gets converted to one or more `redirects`. --- boot.js | 6 +++--- boot/preload-boilerplate.js | 6 +++--- require.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/boot.js b/boot.js index 7532a5cc..bbc39ce2 100644 --- a/boot.js +++ b/boot.js @@ -405,8 +405,8 @@ function getParams(scriptName) { }],[{},function (require, exports, module){ -// mr mini-url.js -// -------------- +// mr mini-url +// ----------- var head = document.querySelector("head"), @@ -1095,7 +1095,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: redirects[name], + redirect: normalizeId(resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index ed3200a6..35fa415d 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -458,8 +458,8 @@ function getParams(scriptName) { }],[{},function (require, exports, module){ -// mr mini-url.js -// -------------- +// mr mini-url +// ----------- var head = document.querySelector("head"), @@ -1167,7 +1167,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: redirects[name], + redirect: normalizeId(resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); diff --git a/require.js b/require.js index 60bf7de2..df0e2b9f 100644 --- a/require.js +++ b/require.js @@ -652,7 +652,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: redirects[name], + redirect: normalizeId(resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); From bf8c05550c8c3be66dd9e6d61a75443387e36dc1 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 28 Aug 2013 18:28:27 -0700 Subject: [PATCH 29/64] Add support for redirect patterns Inherited by dependee packages. --- boot.js | 73 +++++++++++++++---- boot/preload-boilerplate.js | 73 +++++++++++++++---- browser.js | 14 +--- node.js | 18 +---- require.js | 59 ++++++++++++++- spec/redirect-patterns/foo-bar.js | 1 + .../node_modules/mr-foo/package.json | 7 ++ spec/redirect-patterns/package.json | 7 ++ spec/redirect-patterns/program.js | 4 + spec/require-spec.js | 3 +- 10 files changed, 198 insertions(+), 61 deletions(-) create mode 100644 spec/redirect-patterns/foo-bar.js create mode 100644 spec/redirect-patterns/node_modules/mr-foo/package.json create mode 100644 spec/redirect-patterns/package.json create mode 100644 spec/redirect-patterns/program.js diff --git a/boot.js b/boot.js index bbc39ce2..7e63da1d 100644 --- a/boot.js +++ b/boot.js @@ -316,19 +316,7 @@ Require.makeLoader = function (config) { } else { Loader = Require.XhrLoader; } - return Require.MappingsLoader( - config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Loader(config) - ) - ) - ) - ); + return Require.makeCommonLoader(config, Loader(config)); }; module.exports = Require; @@ -480,6 +468,7 @@ Require.makeRequire = function (config) { config.read = config.read || Require.read; config.compilers = config.compilers || {}; config.translators = config.translators || {}; + config.redirectTable = config.redirectTable || []; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -934,7 +923,7 @@ Require.loadPackage = function (dependency, config) { return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); })) .then(function () { - postConfigurePackage(subconfig); + postConfigurePackage(subconfig, packageDescription); }) .thenResolve(pkg); }); @@ -1138,9 +1127,10 @@ function configurePackage(location, description, parent) { return config; } -function postConfigurePackage(config) { +function postConfigurePackage(config, description) { var mappings = config.mappings; var prefixes = Object.keys(mappings); + var redirectTable = config.redirectTable = config.redirectTable || []; prefixes.forEach(function (prefix) { var dependency = mappings[prefix]; @@ -1166,7 +1156,25 @@ function postConfigurePackage(config) { myCompilers[extension] = prefix + "/" + theirCompilers[extension]; }); + // copy redirect patterns + redirectTable.push.apply( + redirectTable, + package.config.redirectTable + ); + }); + + if (description["redirect-patterns"]) { + var describedPatterns = description["redirect-patterns"]; + for (var pattern in describedPatterns) { + if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + redirectTable.push([ + new RegExp(pattern), + describedPatterns[pattern] + ]); + } + } + } } // Helper functions: @@ -1315,6 +1323,25 @@ Require.JsonCompiler = function (config, compile) { // Built-in loader "middleware": +Require.makeCommonLoader = function (config, load) { + return Require.MappingsLoader( + config, + Require.RedirectPatternsLoader( + config, + Require.ExtensionsLoader( + config, + Require.PathsLoader( + config, + Require.MemoizedLoader( + config, + load + ) + ) + ) + ) + ); +}; + // Using mappings hash to load modules that match a mapping. Require.MappingsLoader = function(config, load) { config.mappings = config.mappings || {}; @@ -1428,6 +1455,22 @@ Require.MemoizedLoader = function (config, load) { return memoize(load, cache); }; +Require.RedirectPatternsLoader = function (config, load) { + return function (id, module) { + var table = config.redirectTable || []; + for (var i = 0; i < table.length; i++) { + var expression = table[i][0]; + var match = expression.exec(id); + if (match) { + var replacement = table[i][1]; + module.redirect = id.replace(expression, replacement); + return; + } + } + return load(id, module); + }; +}; + var normalizeId = function (id) { var match = /^(.*)\.js$/.exec(id); if (match) { diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 35fa415d..8c447bd3 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -369,19 +369,7 @@ Require.makeLoader = function (config) { } else { Loader = Require.XhrLoader; } - return Require.MappingsLoader( - config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Loader(config) - ) - ) - ) - ); + return Require.makeCommonLoader(config, Loader(config)); }; module.exports = Require; @@ -552,6 +540,7 @@ Require.makeRequire = function (config) { config.read = config.read || Require.read; config.compilers = config.compilers || {}; config.translators = config.translators || {}; + config.redirectTable = config.redirectTable || []; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -1006,7 +995,7 @@ Require.loadPackage = function (dependency, config) { return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); })) .then(function () { - postConfigurePackage(subconfig); + postConfigurePackage(subconfig, packageDescription); }) .thenResolve(pkg); }); @@ -1210,9 +1199,10 @@ function configurePackage(location, description, parent) { return config; } -function postConfigurePackage(config) { +function postConfigurePackage(config, description) { var mappings = config.mappings; var prefixes = Object.keys(mappings); + var redirectTable = config.redirectTable = config.redirectTable || []; prefixes.forEach(function (prefix) { var dependency = mappings[prefix]; @@ -1238,7 +1228,25 @@ function postConfigurePackage(config) { myCompilers[extension] = prefix + "/" + theirCompilers[extension]; }); + // copy redirect patterns + redirectTable.push.apply( + redirectTable, + package.config.redirectTable + ); + }); + + if (description["redirect-patterns"]) { + var describedPatterns = description["redirect-patterns"]; + for (var pattern in describedPatterns) { + if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + redirectTable.push([ + new RegExp(pattern), + describedPatterns[pattern] + ]); + } + } + } } // Helper functions: @@ -1387,6 +1395,25 @@ Require.JsonCompiler = function (config, compile) { // Built-in loader "middleware": +Require.makeCommonLoader = function (config, load) { + return Require.MappingsLoader( + config, + Require.RedirectPatternsLoader( + config, + Require.ExtensionsLoader( + config, + Require.PathsLoader( + config, + Require.MemoizedLoader( + config, + load + ) + ) + ) + ) + ); +}; + // Using mappings hash to load modules that match a mapping. Require.MappingsLoader = function(config, load) { config.mappings = config.mappings || {}; @@ -1500,6 +1527,22 @@ Require.MemoizedLoader = function (config, load) { return memoize(load, cache); }; +Require.RedirectPatternsLoader = function (config, load) { + return function (id, module) { + var table = config.redirectTable || []; + for (var i = 0; i < table.length; i++) { + var expression = table[i][0]; + var match = expression.exec(id); + if (match) { + var replacement = table[i][1]; + module.redirect = id.replace(expression, replacement); + return; + } + } + return load(id, module); + }; +}; + var normalizeId = function (id) { var match = /^(.*)\.js$/.exec(id); if (match) { diff --git a/browser.js b/browser.js index dede15d5..8fd65f11 100644 --- a/browser.js +++ b/browser.js @@ -232,19 +232,7 @@ Require.makeLoader = function (config) { } else { Loader = Require.XhrLoader; } - return Require.MappingsLoader( - config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Loader(config) - ) - ) - ) - ); + return Require.makeCommonLoader(config, Loader(config)); }; module.exports = Require; diff --git a/node.js b/node.js index a3b4c92b..2ac23151 100644 --- a/node.js +++ b/node.js @@ -139,22 +139,10 @@ Require.NodeLoader = function NodeLoader(config, load) { }; Require.makeLoader = function makeLoader(config) { - return Require.MappingsLoader( + return Require.makeCommonLoader(config, Require.Loader( config, - Require.ExtensionsLoader( - config, - Require.PathsLoader( - config, - Require.MemoizedLoader( - config, - Require.Loader( - config, - Require.NodeLoader(config) - ) - ) - ) - ) - ); + Require.NodeLoader(config) + )); }; Require.findPackagePath = function findPackagePath(directory) { diff --git a/require.js b/require.js index df0e2b9f..44313ec0 100644 --- a/require.js +++ b/require.js @@ -37,6 +37,7 @@ Require.makeRequire = function (config) { config.read = config.read || Require.read; config.compilers = config.compilers || {}; config.translators = config.translators || {}; + config.redirectTable = config.redirectTable || []; // Modules: { exports, id, location, directory, factory, dependencies, // dependees, text, type } @@ -491,7 +492,7 @@ Require.loadPackage = function (dependency, config) { return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); })) .then(function () { - postConfigurePackage(subconfig); + postConfigurePackage(subconfig, packageDescription); }) .thenResolve(pkg); }); @@ -695,9 +696,10 @@ function configurePackage(location, description, parent) { return config; } -function postConfigurePackage(config) { +function postConfigurePackage(config, description) { var mappings = config.mappings; var prefixes = Object.keys(mappings); + var redirectTable = config.redirectTable = config.redirectTable || []; prefixes.forEach(function (prefix) { var dependency = mappings[prefix]; @@ -723,7 +725,25 @@ function postConfigurePackage(config) { myCompilers[extension] = prefix + "/" + theirCompilers[extension]; }); + // copy redirect patterns + redirectTable.push.apply( + redirectTable, + package.config.redirectTable + ); + }); + + if (description["redirect-patterns"]) { + var describedPatterns = description["redirect-patterns"]; + for (var pattern in describedPatterns) { + if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + redirectTable.push([ + new RegExp(pattern), + describedPatterns[pattern] + ]); + } + } + } } // Helper functions: @@ -872,6 +892,25 @@ Require.JsonCompiler = function (config, compile) { // Built-in loader "middleware": +Require.makeCommonLoader = function (config, load) { + return Require.MappingsLoader( + config, + Require.RedirectPatternsLoader( + config, + Require.ExtensionsLoader( + config, + Require.PathsLoader( + config, + Require.MemoizedLoader( + config, + load + ) + ) + ) + ) + ); +}; + // Using mappings hash to load modules that match a mapping. Require.MappingsLoader = function(config, load) { config.mappings = config.mappings || {}; @@ -985,6 +1024,22 @@ Require.MemoizedLoader = function (config, load) { return memoize(load, cache); }; +Require.RedirectPatternsLoader = function (config, load) { + return function (id, module) { + var table = config.redirectTable || []; + for (var i = 0; i < table.length; i++) { + var expression = table[i][0]; + var match = expression.exec(id); + if (match) { + var replacement = table[i][1]; + module.redirect = id.replace(expression, replacement); + return; + } + } + return load(id, module); + }; +}; + var normalizeId = function (id) { var match = /^(.*)\.js$/.exec(id); if (match) { diff --git a/spec/redirect-patterns/foo-bar.js b/spec/redirect-patterns/foo-bar.js new file mode 100644 index 00000000..34cd7b97 --- /dev/null +++ b/spec/redirect-patterns/foo-bar.js @@ -0,0 +1 @@ +module.exports = "Hello, Foo!"; diff --git a/spec/redirect-patterns/node_modules/mr-foo/package.json b/spec/redirect-patterns/node_modules/mr-foo/package.json new file mode 100644 index 00000000..420c041e --- /dev/null +++ b/spec/redirect-patterns/node_modules/mr-foo/package.json @@ -0,0 +1,7 @@ +{ + "name": "mr-foo", + "version": "*", + "redirect-patterns": { + "^(.*)\\.foo$": "foo-$1" + } +} diff --git a/spec/redirect-patterns/package.json b/spec/redirect-patterns/package.json new file mode 100644 index 00000000..9936de83 --- /dev/null +++ b/spec/redirect-patterns/package.json @@ -0,0 +1,7 @@ +{ + "name": "*", + "version": "*", + "dependencies": { + "mr-foo": "*" + } +} diff --git a/spec/redirect-patterns/program.js b/spec/redirect-patterns/program.js new file mode 100644 index 00000000..bce64e9a --- /dev/null +++ b/spec/redirect-patterns/program.js @@ -0,0 +1,4 @@ +var test = require("test"); +test.assert(require.lookup("bar.foo").id === "foo-bar"); +test.assert(require("bar.foo") === "Hello, Foo!"); +test.print("DONE", "info"); diff --git a/spec/require-spec.js b/spec/require-spec.js index f18c12b6..004ba087 100644 --- a/spec/require-spec.js +++ b/spec/require-spec.js @@ -68,7 +68,8 @@ describe("Require", function () { "compiler", "translator", "compiler-package", - "translator-package" + "translator-package", + "redirect-patterns" ].forEach(function (test) { if (typeof test === "object") { if (test.node === false && typeof process !== "undefined") { From 7d940be598e03b185f503de470fe8b591b1f850e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 17 Sep 2013 16:05:46 -0700 Subject: [PATCH 30/64] Per review: only capture last term of an extension And a comment or two --- boot.js | 4 +++- boot/preload-boilerplate.js | 4 +++- require.js | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/boot.js b/boot.js index 7e63da1d..44465860 100644 --- a/boot.js +++ b/boot.js @@ -902,8 +902,10 @@ Require.loadPackage = function (dependency, config) { } var location = dependency.location; + // prevent data-lock if there is a package dependency cycle loading = loading || {}; if (loading[location]) { + // returns an already-fulfilled promise for `undefined` return Q(); } loading[location] = true; @@ -1218,7 +1220,7 @@ Require.base = function (location) { }; Require.extension = function (location) { - var match = /\.([^\/]+)$/.exec(location); + var match = /\.([^\/\.]+)$/.exec(location); if (match) { return match[1]; } diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 8c447bd3..afc1db19 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -974,8 +974,10 @@ Require.loadPackage = function (dependency, config) { } var location = dependency.location; + // prevent data-lock if there is a package dependency cycle loading = loading || {}; if (loading[location]) { + // returns an already-fulfilled promise for `undefined` return Q(); } loading[location] = true; @@ -1290,7 +1292,7 @@ Require.base = function (location) { }; Require.extension = function (location) { - var match = /\.([^\/]+)$/.exec(location); + var match = /\.([^\/\.]+)$/.exec(location); if (match) { return match[1]; } diff --git a/require.js b/require.js index 44313ec0..20e7e22e 100644 --- a/require.js +++ b/require.js @@ -471,8 +471,10 @@ Require.loadPackage = function (dependency, config) { } var location = dependency.location; + // prevent data-lock if there is a package dependency cycle loading = loading || {}; if (loading[location]) { + // returns an already-fulfilled promise for `undefined` return Q(); } loading[location] = true; @@ -787,7 +789,7 @@ Require.base = function (location) { }; Require.extension = function (location) { - var match = /\.([^\/]+)$/.exec(location); + var match = /\.([^\/\.]+)$/.exec(location); if (match) { return match[1]; } From 348a2fc59409ad332bc6984ab12d075dc0b40f87 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 18 Nov 2013 21:29:37 -0800 Subject: [PATCH 31/64] Elide translator modules from build products The Mr standalone bundler can now use translators that only load on Node.js, and will never include a translator in a bundle. --- boot.js | 743 ++++++++++++++++++++------------ boot/preload-boilerplate.js | 743 ++++++++++++++++++++------------ build.js | 13 +- require.js | 5 +- spec/translator-package/test.js | 2 + 5 files changed, 946 insertions(+), 560 deletions(-) create mode 100644 spec/translator-package/test.js diff --git a/boot.js b/boot.js index c5fb09fa..ae08ea6a 100644 --- a/boot.js +++ b/boot.js @@ -530,12 +530,13 @@ Require.makeRequire = function (config) { return Q.fcall(function () { if (config.translators[module.extension]) { var translatorId = config.translators[module.extension]; + var translatorPackage = config.translatorPackage || require; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled - return deepLoad(translatorId, "", loading) + return translatorPackage.deepLoad(translatorId, "", loading) .then(function () { - var translate = require(translatorId); + var translate = translatorPackage(translatorId); module.text = translate(module.text, module); }); } @@ -1579,6 +1580,8 @@ var nextTick =(function () { var isNodeJS = false; function flush() { + /* jshint loopfunc: true */ + while (head.next) { head = head.next; var task = head.task; @@ -1601,9 +1604,13 @@ var nextTick =(function () { // Ensure continuation if the uncaught exception is suppressed // listening "uncaughtException" events (as domains does). // Continue in next event to avoid tick recursion. - domain && domain.exit(); + if (domain) { + domain.exit(); + } setTimeout(flush, 0); - domain && domain.enter(); + if (domain) { + domain.enter(); + } throw e; @@ -1660,10 +1667,22 @@ var nextTick =(function () { // modern browsers // http://www.nonblocking.io/2011/06/windownexttick.html var channel = new MessageChannel(); - channel.port1.onmessage = flush; - requestTick = function () { + // At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create + // working message ports the first time a page loads. + channel.port1.onmessage = function () { + requestTick = requestPortTick; + channel.port1.onmessage = flush; + flush(); + }; + var requestPortTick = function () { + // Opera requires us to provide a message payload, regardless of + // whether we use it. channel.port2.postMessage(0); }; + requestTick = function () { + setTimeout(flush, 0); + requestPortTick(); + }; } else { // old browsers @@ -1686,8 +1705,8 @@ var nextTick =(function () { // hard-to-minify characters. // See Mark Miller’s explanation of what this does. // http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming +var call = Function.call; function uncurryThis(f) { - var call = Function.call; return function () { return call.apply(f, arguments); }; @@ -1929,13 +1948,26 @@ function deprecate(callback, name, alternative) { // beginning of real work /** - * Creates fulfilled promises from non-thenables, - * Passes Q promises through, - * Coerces other thenables to Q promises. + * Constructs a promise for an immediate reference, passes promises through, or + * coerces promises from different systems. + * @param value immediate reference or promise */ function Q(value) { - return resolve(value); + // If the object is already a Promise, return it directly. This enables + // the resolve function to both be used to created references from objects, + // but to tolerably coerce non-promises to promises. + if (isPromise(value)) { + return value; + } + + // assimilate thenables + if (isPromiseAlike(value)) { + return coerce(value); + } else { + return fulfill(value); + } } +Q.resolve = Q; /** * Performs a task in a future turn of the event loop. @@ -2042,7 +2074,7 @@ function defer() { return; } - become(resolve(value)); + become(Q(value)); }; deferred.fulfill = function (value) { @@ -2103,17 +2135,77 @@ function promise(resolver) { if (typeof resolver !== "function") { throw new TypeError("resolver must be a function."); } - var deferred = defer(); - fcall( - resolver, - deferred.resolve, - deferred.reject, - deferred.notify - ).fail(deferred.reject); + try { + resolver(deferred.resolve, deferred.reject, deferred.notify); + } catch (reason) { + deferred.reject(reason); + } return deferred.promise; } +// XXX experimental. This method is a way to denote that a local value is +// serializable and should be immediately dispatched to a remote upon request, +// instead of passing a reference. +Q.passByCopy = function (object) { + //freeze(object); + //passByCopies.set(object, true); + return object; +}; + +Promise.prototype.passByCopy = function () { + //freeze(object); + //passByCopies.set(object, true); + return this; +}; + +/** + * If two promises eventually fulfill to the same value, promises that value, + * but otherwise rejects. + * @param x {Any*} + * @param y {Any*} + * @returns {Any*} a promise for x and y if they are the same, but a rejection + * otherwise. + * + */ +Q.join = function (x, y) { + return Q(x).join(y); +}; + +Promise.prototype.join = function (that) { + return Q([this, that]).spread(function (x, y) { + if (x === y) { + // TODO: "===" should be Object.is or equiv + return x; + } else { + throw new Error("Can't join: not the same: " + x + " " + y); + } + }); +}; + +/** + * Returns a promise for the first of an array of promises to become fulfilled. + * @param answers {Array[Any*]} promises to race + * @returns {Any*} the first promise to be fulfilled + */ +Q.race = race; +function race(answerPs) { + return promise(function(resolve, reject) { + // Switch to this once we can assume at least ES5 + // answerPs.forEach(function(answerP) { + // Q(answerP).then(resolve, reject); + // }); + // Use this in the meantime + for (var i = 0, len = answerPs.length; i < len; i++) { + Q(answerPs[i]).then(resolve, reject); + } + }); +} + +Promise.prototype.race = function () { + return this.then(Q.race); +}; + /** * Constructs a Promise with a promise descriptor object and optional fallback * function. The descriptor contains methods like when(rejected), get(name), @@ -2180,6 +2272,10 @@ function Promise(descriptor, fallback, inspect) { return promise; } +Promise.prototype.toString = function () { + return "[object Promise]"; +}; + Promise.prototype.then = function (fulfilled, rejected, progressed) { var self = this; var deferred = defer(); @@ -2251,48 +2347,41 @@ Promise.prototype.then = function (fulfilled, rejected, progressed) { return deferred.promise; }; +/** + * Registers an observer on a promise. + * + * Guarantees: + * + * 1. that fulfilled and rejected will be called only once. + * 2. that either the fulfilled callback or the rejected callback will be + * called, but not both. + * 3. that fulfilled and rejected will not be called in this turn. + * + * @param value promise or immediate reference to observe + * @param fulfilled function to be called with the fulfilled value + * @param rejected function to be called with the rejection exception + * @param progressed function to be called on any progress notifications + * @return promise for the return value from the invoked callback + */ +Q.when = when; +function when(value, fulfilled, rejected, progressed) { + return Q(value).then(fulfilled, rejected, progressed); +} + Promise.prototype.thenResolve = function (value) { - return when(this, function () { return value; }); + return this.then(function () { return value; }); }; -Promise.prototype.thenReject = function (reason) { - return when(this, function () { throw reason; }); -}; - -// Chainable methods -array_reduce( - [ - "isFulfilled", "isRejected", "isPending", - "dispatch", - "when", "spread", - "get", "set", "del", "delete", - "post", "send", "mapply", "invoke", "mcall", - "keys", - "fapply", "fcall", "fbind", - "all", "allResolved", - "timeout", "delay", - "catch", "finally", "fail", "fin", "progress", "done", - "nfcall", "nfapply", "nfbind", "denodeify", "nbind", - "npost", "nsend", "nmapply", "ninvoke", "nmcall", - "nodeify" - ], - function (undefined, name) { - Promise.prototype[name] = function () { - return Q[name].apply( - Q, - [this].concat(array_slice(arguments)) - ); - }; - }, - void 0 -); +Q.thenResolve = function (promise, value) { + return Q(promise).thenResolve(value); +}; -Promise.prototype.toSource = function () { - return this.toString(); +Promise.prototype.thenReject = function (reason) { + return this.then(function () { throw reason; }); }; -Promise.prototype.toString = function () { - return "[object Promise]"; +Q.thenReject = function (promise, reason) { + return Q(promise).thenReject(reason); }; /** @@ -2342,6 +2431,10 @@ function isPending(object) { return isPromise(object) && object.inspect().state === "pending"; } +Promise.prototype.isPending = function () { + return this.inspect().state === "pending"; +}; + /** * @returns whether the given object is a value or fulfilled * promise. @@ -2351,6 +2444,10 @@ function isFulfilled(object) { return !isPromise(object) || object.inspect().state === "fulfilled"; } +Promise.prototype.isFulfilled = function () { + return this.inspect().state === "fulfilled"; +}; + /** * @returns whether the given object is a rejected promise. */ @@ -2359,6 +2456,10 @@ function isRejected(object) { return isPromise(object) && object.inspect().state === "rejected"; } +Promise.prototype.isRejected = function () { + return this.inspect().state === "rejected"; +}; + //// BEGIN UNHANDLED REJECTION TRACKING // This promise library consumes exceptions thrown in handlers so they can be @@ -2386,11 +2487,7 @@ function displayUnhandledReasons() { function logUnhandledReasons() { for (var i = 0; i < unhandledReasons.length; i++) { var reason = unhandledReasons[i]; - if (reason && typeof reason.stack !== "undefined") { - console.warn("Unhandled rejection reason:", reason.stack); - } else { - console.warn("Unhandled rejection reason (no stack):", reason); - } + console.warn("Unhandled rejection reason:", reason); } } @@ -2417,7 +2514,11 @@ function trackRejection(promise, reason) { } unhandledRejections.push(promise); - unhandledReasons.push(reason); + if (reason && typeof reason.stack !== "undefined") { + unhandledReasons.push(reason.stack); + } else { + unhandledReasons.push("(no stack) " + reason); + } displayUnhandledReasons(); } @@ -2506,8 +2607,8 @@ function fulfill(value) { return value[name].apply(value, args); } }, - "apply": function (thisP, args) { - return value.apply(thisP, args); + "apply": function (thisp, args) { + return value.apply(thisp, args); }, "keys": function () { return object_keys(value); @@ -2517,28 +2618,6 @@ function fulfill(value) { }); } -/** - * Constructs a promise for an immediate reference, passes promises through, or - * coerces promises from different systems. - * @param value immediate reference or promise - */ -Q.resolve = resolve; -function resolve(value) { - // If the object is already a Promise, return it directly. This enables - // the resolve function to both be used to created references from objects, - // but to tolerably coerce non-promises to promises. - if (isPromise(value)) { - return value; - } - - // assimilate thenables - if (isPromiseAlike(value)) { - return coerce(value); - } else { - return fulfill(value); - } -} - /** * Converts thenables to Q promises. * @param promise thenable promise @@ -2572,31 +2651,10 @@ function master(object) { }, function fallback(op, args) { return dispatch(object, op, args); }, function () { - return resolve(object).inspect(); + return Q(object).inspect(); }); } -/** - * Registers an observer on a promise. - * - * Guarantees: - * - * 1. that fulfilled and rejected will be called only once. - * 2. that either the fulfilled callback or the rejected callback will be - * called, but not both. - * 3. that fulfilled and rejected will not be called in this turn. - * - * @param value promise or immediate reference to observe - * @param fulfilled function to be called with the fulfilled value - * @param rejected function to be called with the rejection exception - * @param progressed function to be called on any progress notifications - * @return promise for the return value from the invoked callback - */ -Q.when = when; -function when(value, fulfilled, rejected, progressed) { - return Q(value).then(fulfilled, rejected, progressed); -} - /** * Spreads the values of a promised array of arguments into the * fulfillment callback. @@ -2608,14 +2666,16 @@ function when(value, fulfilled, rejected, progressed) { * either callback. */ Q.spread = spread; -function spread(promise, fulfilled, rejected) { - return when(promise, function (valuesOrPromises) { - return all(valuesOrPromises).then(function (values) { - return fulfilled.apply(void 0, values); - }, rejected); - }, rejected); +function spread(value, fulfilled, rejected) { + return Q(value).spread(fulfilled, rejected); } +Promise.prototype.spread = function (fulfilled, rejected) { + return this.all().then(function (array) { + return fulfilled.apply(void 0, array); + }, rejected); +}; + /** * The async function is a decorator for generator functions, turning * them into asynchronous generators. Although generators are only part @@ -2675,7 +2735,7 @@ function async(makeGenerator) { } } var generator = makeGenerator.apply(this, arguments); - var callback = continuer.bind(continuer, "send"); + var callback = continuer.bind(continuer, "next"); var errback = continuer.bind(continuer, "throw"); return callback(); }; @@ -2733,7 +2793,7 @@ function _return(value) { * var add = Q.promised(function (a, b) { * return a + b; * }); - * add(Q.resolve(a), Q.resolve(B)); + * add(Q(a), Q(B)); * * @param {function} callback The function to decorate * @returns {function} a function that has been decorated. @@ -2756,26 +2816,17 @@ function promised(callback) { */ Q.dispatch = dispatch; function dispatch(object, op, args) { + return Q(object).dispatch(op, args); +} + +Promise.prototype.dispatch = function (op, args) { + var self = this; var deferred = defer(); nextTick(function () { - resolve(object).promiseDispatch(deferred.resolve, op, args); + self.promiseDispatch(deferred.resolve, op, args); }); return deferred.promise; -} - -/** - * Constructs a promise method that can be used to safely observe resolution of - * a promise for an arbitrarily named method like "propfind" in a future turn. - * - * "dispatcher" constructs methods like "get(promise, name)" and "set(promise)". - */ -Q.dispatcher = dispatcher; -function dispatcher(op) { - return function (object) { - var args = array_slice(arguments, 1); - return dispatch(object, op, args); - }; -} +}; /** * Gets the value of a property in a future turn. @@ -2783,7 +2834,13 @@ function dispatcher(op) { * @param name name of property to get * @return promise for the property value */ -Q.get = dispatcher("get"); +Q.get = function (object, key) { + return Q(object).dispatch("get", [key]); +}; + +Promise.prototype.get = function (key) { + return this.dispatch("get", [key]); +}; /** * Sets the value of a property in a future turn. @@ -2792,7 +2849,13 @@ Q.get = dispatcher("get"); * @param value new value of property * @return promise for the return value */ -Q.set = dispatcher("set"); +Q.set = function (object, key, value) { + return Q(object).dispatch("set", [key, value]); +}; + +Promise.prototype.set = function (key, value) { + return this.dispatch("set", [key, value]); +}; /** * Deletes a property in a future turn. @@ -2800,8 +2863,15 @@ Q.set = dispatcher("set"); * @param name name of property to delete * @return promise for the return value */ -Q["delete"] = // XXX experimental -Q.del = dispatcher("delete"); +Q.del = // XXX legacy +Q["delete"] = function (object, key) { + return Q(object).dispatch("delete", [key]); +}; + +Promise.prototype.del = // XXX legacy +Promise.prototype["delete"] = function (key) { + return this.dispatch("delete", [key]); +}; /** * Invokes a method in a future turn. @@ -2816,8 +2886,15 @@ Q.del = dispatcher("delete"); * @return promise for the return value */ // bound locally because it is used by other methods -var post = Q.post = dispatcher("post"); -Q.mapply = post; // experimental +Q.mapply = // XXX As proposed by "Redsandro" +Q.post = function (object, name, args) { + return Q(object).dispatch("post", [name, args]); +}; + +Promise.prototype.mapply = // XXX As proposed by "Redsandro" +Promise.prototype.post = function (name, args) { + return this.dispatch("post", [name, args]); +}; /** * Invokes a method in a future turn. @@ -2826,35 +2903,44 @@ Q.mapply = post; // experimental * @param ...args array of invocation arguments * @return promise for the return value */ -Q.send = send; -Q.invoke = send; // synonyms -Q.mcall = send; // experimental -function send(value, name) { - var args = array_slice(arguments, 2); - return post(value, name, args); -} +Q.send = // XXX Mark Miller's proposed parlance +Q.mcall = // XXX As proposed by "Redsandro" +Q.invoke = function (object, name /*...args*/) { + return Q(object).dispatch("post", [name, array_slice(arguments, 2)]); +}; + +Promise.prototype.send = // XXX Mark Miller's proposed parlance +Promise.prototype.mcall = // XXX As proposed by "Redsandro" +Promise.prototype.invoke = function (name /*...args*/) { + return this.dispatch("post", [name, array_slice(arguments, 1)]); +}; /** * Applies the promised function in a future turn. * @param object promise or immediate reference for target function * @param args array of application arguments */ -Q.fapply = fapply; -function fapply(value, args) { - return dispatch(value, "apply", [void 0, args]); -} +Q.fapply = function (object, args) { + return Q(object).dispatch("apply", [void 0, args]); +}; + +Promise.prototype.fapply = function (args) { + return this.dispatch("apply", [void 0, args]); +}; /** * Calls the promised function in a future turn. * @param object promise or immediate reference for target function * @param ...args array of application arguments */ -Q["try"] = fcall; // XXX experimental -Q.fcall = fcall; -function fcall(value) { - var args = array_slice(arguments, 1); - return fapply(value, args); -} +Q["try"] = +Q.fcall = function (object /* ...args*/) { + return Q(object).dispatch("apply", [void 0, array_slice(arguments, 1)]); +}; + +Promise.prototype.fcall = function (/*...args*/) { + return this.dispatch("apply", [void 0, array_slice(arguments)]); +}; /** * Binds the promised function, transforming return values into a fulfilled @@ -2862,14 +2948,26 @@ function fcall(value) { * @param object promise or immediate reference for target function * @param ...args array of application arguments */ -Q.fbind = fbind; -function fbind(value) { +Q.fbind = function (object /*...args*/) { + var promise = Q(object); var args = array_slice(arguments, 1); return function fbound() { - var allArgs = args.concat(array_slice(arguments)); - return dispatch(value, "apply", [this, allArgs]); + return promise.dispatch("apply", [ + this, + args.concat(array_slice(arguments)) + ]); }; -} +}; +Promise.prototype.fbind = function (/*...args*/) { + var promise = this; + var args = array_slice(arguments); + return function fbound() { + return promise.dispatch("apply", [ + this, + args.concat(array_slice(arguments)) + ]); + }; +}; /** * Requests the names of the owned properties of a promised @@ -2877,7 +2975,13 @@ function fbind(value) { * @param object promise or immediate reference for target object * @return promise for the keys of the eventually settled object */ -Q.keys = dispatcher("keys"); +Q.keys = function (object) { + return Q(object).dispatch("keys", []); +}; + +Promise.prototype.keys = function () { + return this.dispatch("keys", []); +}; /** * Turns an array of promises into a promise for an array. If any of @@ -2902,12 +3006,19 @@ function all(promises) { promises[index] = snapshot.value; } else { ++countDown; - when(promise, function (value) { - promises[index] = value; - if (--countDown === 0) { - deferred.resolve(promises); + when( + promise, + function (value) { + promises[index] = value; + if (--countDown === 0) { + deferred.resolve(promises); + } + }, + deferred.reject, + function (progress) { + deferred.notify({ index: index, value: progress }); } - }, deferred.reject); + ); } }, void 0); if (countDown === 0) { @@ -2917,6 +3028,10 @@ function all(promises) { }); } +Promise.prototype.all = function () { + return all(this); +}; + /** * Waits for all promises to be settled, either fulfilled or * rejected. This is distinct from `all` since that would stop @@ -2929,7 +3044,7 @@ function all(promises) { Q.allResolved = deprecate(allResolved, "allResolved", "allSettled"); function allResolved(promises) { return when(promises, function (promises) { - promises = array_map(promises, resolve); + promises = array_map(promises, Q); return when(all(array_map(promises, function (promise) { return when(promise, noop, noop); })), function () { @@ -2938,25 +3053,37 @@ function allResolved(promises) { }); } +Promise.prototype.allResolved = function () { + return allResolved(this); +}; + +/** + * @see Promise#allSettled + */ Q.allSettled = allSettled; -function allSettled(values) { - return when(values, function (values) { - return all(array_map(values, function (value, i) { - return when( - value, - function (fulfillmentValue) { - values[i] = { state: "fulfilled", value: fulfillmentValue }; - return values[i]; - }, - function (reason) { - values[i] = { state: "rejected", reason: reason }; - return values[i]; - } - ); - })).thenResolve(values); - }); +function allSettled(promises) { + return Q(promises).allSettled(); } +/** + * Turns an array of promises into a promise for an array of their states (as + * returned by `inspect`) when they have all settled. + * @param {Array[Any*]} values an array (or promise for an array) of values (or + * promises for values) + * @returns {Array[State]} an array of states for the respective values. + */ +Promise.prototype.allSettled = function () { + return this.then(function (promises) { + return all(array_map(promises, function (promise) { + promise = Q(promise); + function regardless() { + return promise.inspect(); + } + return promise.then(regardless, regardless); + })); + }); +}; + /** * Captures the failure of a promise, giving an oportunity to recover * with a callback. If the given promise is fulfilled, the returned @@ -2966,11 +3093,15 @@ function allSettled(values) { * given promise is rejected * @returns a promise for the return value of the callback */ -Q["catch"] = // XXX experimental -Q.fail = fail; -function fail(promise, rejected) { - return when(promise, void 0, rejected); -} +Q.fail = // XXX legacy +Q["catch"] = function (object, rejected) { + return Q(object).then(void 0, rejected); +}; + +Promise.prototype.fail = // XXX legacy +Promise.prototype["catch"] = function (rejected) { + return this.then(void 0, rejected); +}; /** * Attaches a listener that can respond to progress notifications from a @@ -2981,10 +3112,14 @@ function fail(promise, rejected) { * @returns the given promise, unchanged */ Q.progress = progress; -function progress(promise, progressed) { - return when(promise, void 0, void 0, progressed); +function progress(object, progressed) { + return Q(object).then(void 0, void 0, progressed); } +Promise.prototype.progress = function (progressed) { + return this.then(void 0, void 0, progressed); +}; + /** * Provides an opportunity to observe the settling of a promise, * regardless of whether the promise is fulfilled or rejected. Forwards @@ -2996,19 +3131,25 @@ function progress(promise, progressed) { * @returns a promise for the resolution of the given promise when * ``fin`` is done. */ -Q["finally"] = // XXX experimental -Q.fin = fin; -function fin(promise, callback) { - return when(promise, function (value) { - return when(callback(), function () { +Q.fin = // XXX legacy +Q["finally"] = function (object, callback) { + return Q(object)["finally"](callback); +}; + +Promise.prototype.fin = // XXX legacy +Promise.prototype["finally"] = function (callback) { + callback = Q(callback); + return this.then(function (value) { + return callback.fcall().then(function () { return value; }); - }, function (exception) { - return when(callback(), function () { - return reject(exception); + }, function (reason) { + // TODO attempt to recycle the rejection with "this". + return callback.fcall().then(function () { + throw reason; }); }); -} +}; /** * Terminates a chain of promises, forcing rejections to be @@ -3016,14 +3157,16 @@ function fin(promise, callback) { * @param {Any*} promise at the end of a chain of promises * @returns nothing */ -Q.done = done; -function done(promise, fulfilled, rejected, progress) { +Q.done = function (object, fulfilled, rejected, progress) { + return Q(object).done(fulfilled, rejected, progress); +}; + +Promise.prototype.done = function (fulfilled, rejected, progress) { var onUnhandledError = function (error) { // forward to a future turn so that ``when`` // does not catch it and turn it into a rejection. nextTick(function () { makeStackTraceLong(error, promise); - if (Q.onerror) { Q.onerror(error); } else { @@ -3033,15 +3176,16 @@ function done(promise, fulfilled, rejected, progress) { }; // Avoid unnecessary `nextTick`ing via an unnecessary `when`. - var promiseToHandle = fulfilled || rejected || progress ? - when(promise, fulfilled, rejected, progress) : - promise; + var promise = fulfilled || rejected || progress ? + this.then(fulfilled, rejected, progress) : + this; if (typeof process === "object" && process && process.domain) { onUnhandledError = process.domain.bind(onUnhandledError); } - fail(promiseToHandle, onUnhandledError); -} + + promise.then(void 0, onUnhandledError); +}; /** * Causes a promise to be rejected if it does not get fulfilled before @@ -3052,14 +3196,17 @@ function done(promise, fulfilled, rejected, progress) { * @returns a promise for the resolution of the given promise if it is * fulfilled before the timeout, otherwise rejected. */ -Q.timeout = timeout; -function timeout(promise, ms, msg) { +Q.timeout = function (object, ms, message) { + return Q(object).timeout(ms, message); +}; + +Promise.prototype.timeout = function (ms, message) { var deferred = defer(); var timeoutId = setTimeout(function () { - deferred.reject(new Error(msg || "Timed out after " + ms + " ms")); + deferred.reject(new Error(message || "Timed out after " + ms + " ms")); }, ms); - when(promise, function (value) { + this.then(function (value) { clearTimeout(timeoutId); deferred.resolve(value); }, function (exception) { @@ -3068,32 +3215,34 @@ function timeout(promise, ms, msg) { }, deferred.notify); return deferred.promise; -} +}; /** - * Returns a promise for the given value (or promised value) after some - * milliseconds. + * Returns a promise for the given value (or promised value), some + * milliseconds after it resolved. Passes rejections immediately. * @param {Any*} promise * @param {Number} milliseconds - * @returns a promise for the resolution of the given promise after some - * time has elapsed. + * @returns a promise for the resolution of the given promise after milliseconds + * time has elapsed since the resolution of the given promise. + * If the given promise rejects, that is passed immediately. */ -Q.delay = delay; -function delay(promise, timeout) { +Q.delay = function (object, timeout) { if (timeout === void 0) { - timeout = promise; - promise = void 0; + timeout = object; + object = void 0; } + return Q(object).delay(timeout); +}; - var deferred = defer(); - - when(promise, undefined, undefined, deferred.notify); - setTimeout(function () { - deferred.resolve(promise); - }, timeout); - - return deferred.promise; -} +Promise.prototype.delay = function (timeout) { + return this.then(function (value) { + var deferred = defer(); + setTimeout(function () { + deferred.resolve(value); + }, timeout); + return deferred.promise; + }); +}; /** * Passes a continuation to a Node function, which is called with the given @@ -3104,74 +3253,86 @@ function delay(promise, timeout) { * }) * */ -Q.nfapply = nfapply; -function nfapply(callback, args) { - var nodeArgs = array_slice(args); +Q.nfapply = function (callback, args) { + return Q(callback).nfapply(args); +}; + +Promise.prototype.nfapply = function (args) { var deferred = defer(); + var nodeArgs = array_slice(args); nodeArgs.push(deferred.makeNodeResolver()); - - fapply(callback, nodeArgs).fail(deferred.reject); + this.fapply(nodeArgs).fail(deferred.reject); return deferred.promise; -} +}; /** * Passes a continuation to a Node function, which is called with the given * arguments provided individually, and returns a promise. - * - * Q.nfcall(FS.readFile, __filename) - * .then(function (content) { - * }) + * @example + * Q.nfcall(FS.readFile, __filename) + * .then(function (content) { + * }) * */ -Q.nfcall = nfcall; -function nfcall(callback/*, ...args */) { - var nodeArgs = array_slice(arguments, 1); +Q.nfcall = function (callback /*...args*/) { + var args = array_slice(arguments, 1); + return Q(callback).nfapply(args); +}; + +Promise.prototype.nfcall = function (/*...args*/) { + var nodeArgs = array_slice(arguments); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - - fapply(callback, nodeArgs).fail(deferred.reject); + this.fapply(nodeArgs).fail(deferred.reject); return deferred.promise; -} +}; /** * Wraps a NodeJS continuation passing function and returns an equivalent * version that returns a promise. - * - * Q.nfbind(FS.readFile, __filename)("utf-8") - * .then(console.log) - * .done() - * + * @example + * Q.nfbind(FS.readFile, __filename)("utf-8") + * .then(console.log) + * .done() */ -Q.nfbind = nfbind; -Q.denodeify = Q.nfbind; // synonyms -function nfbind(callback/*, ...args */) { +Q.nfbind = +Q.denodeify = function (callback /*...args*/) { var baseArgs = array_slice(arguments, 1); return function () { var nodeArgs = baseArgs.concat(array_slice(arguments)); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - - fapply(callback, nodeArgs).fail(deferred.reject); + Q(callback).fapply(nodeArgs).fail(deferred.reject); return deferred.promise; }; -} +}; + +Promise.prototype.nfbind = +Promise.prototype.denodeify = function (/*...args*/) { + var args = array_slice(arguments); + args.unshift(this); + return Q.denodeify.apply(void 0, args); +}; -Q.nbind = nbind; -function nbind(callback, thisArg /*, ... args*/) { +Q.nbind = function (callback, thisp /*...args*/) { var baseArgs = array_slice(arguments, 2); return function () { var nodeArgs = baseArgs.concat(array_slice(arguments)); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - function bound() { - return callback.apply(thisArg, arguments); + return callback.apply(thisp, arguments); } - - fapply(bound, nodeArgs).fail(deferred.reject); + Q(bound).fapply(nodeArgs).fail(deferred.reject); return deferred.promise; }; -} +}; + +Promise.prototype.nbind = function (/*thisp, ...args*/) { + var args = array_slice(arguments, 0); + args.unshift(this); + return Q.nbind.apply(void 0, args); +}; /** * Calls a method of a Node-style object that accepts a Node-style @@ -3182,16 +3343,19 @@ function nbind(callback, thisArg /*, ... args*/) { * will be provided by Q and appended to these arguments. * @returns a promise for the value or error */ -Q.npost = npost; -Q.nmapply = npost; // synonyms -function npost(object, name, args) { +Q.nmapply = // XXX As proposed by "Redsandro" +Q.npost = function (object, name, args) { + return Q(object).npost(name, args); +}; + +Promise.prototype.nmapply = // XXX As proposed by "Redsandro" +Promise.prototype.npost = function (name, args) { var nodeArgs = array_slice(args || []); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - - post(object, name, nodeArgs).fail(deferred.reject); + this.dispatch("post", [name, nodeArgs]).fail(deferred.reject); return deferred.promise; -} +}; /** * Calls a method of a Node-style object that accepts a Node-style @@ -3203,21 +3367,44 @@ function npost(object, name, args) { * be provided by Q and appended to these arguments. * @returns a promise for the value or error */ -Q.nsend = nsend; -Q.ninvoke = Q.nsend; // synonyms -Q.nmcall = Q.nsend; // synonyms -function nsend(object, name /*, ...args*/) { +Q.nsend = // XXX Based on Mark Miller's proposed "send" +Q.nmcall = // XXX Based on "Redsandro's" proposal +Q.ninvoke = function (object, name /*...args*/) { var nodeArgs = array_slice(arguments, 2); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - post(object, name, nodeArgs).fail(deferred.reject); + Q(object).dispatch("post", [name, nodeArgs]).fail(deferred.reject); return deferred.promise; -} +}; +Promise.prototype.nsend = // XXX Based on Mark Miller's proposed "send" +Promise.prototype.nmcall = // XXX Based on "Redsandro's" proposal +Promise.prototype.ninvoke = function (name /*...args*/) { + var nodeArgs = array_slice(arguments, 1); + var deferred = defer(); + nodeArgs.push(deferred.makeNodeResolver()); + this.dispatch("post", [name, nodeArgs]).fail(deferred.reject); + return deferred.promise; +}; + +/** + * If a function would like to support both Node continuation-passing-style and + * promise-returning-style, it can end its internal promise chain with + * `nodeify(nodeback)`, forwarding the optional nodeback argument. If the user + * elects to use a nodeback, the result will be sent there. If they do not + * pass a nodeback, they will receive the result promise. + * @param object a result (or a promise for a result) + * @param {Function} nodeback a Node.js-style callback + * @returns either the promise or nothing + */ Q.nodeify = nodeify; -function nodeify(promise, nodeback) { +function nodeify(object, nodeback) { + return Q(object).nodeify(nodeback); +} + +Promise.prototype.nodeify = function (nodeback) { if (nodeback) { - promise.then(function (value) { + this.then(function (value) { nextTick(function () { nodeback(null, value); }); @@ -3227,9 +3414,9 @@ function nodeify(promise, nodeback) { }); }); } else { - return promise; + return this; } -} +}; // All code before this point will be filtered from stack traces. var qEndingLine = captureLine(); diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index d01af09b..7b9cb860 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -602,12 +602,13 @@ Require.makeRequire = function (config) { return Q.fcall(function () { if (config.translators[module.extension]) { var translatorId = config.translators[module.extension]; + var translatorPackage = config.translatorPackage || require; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled - return deepLoad(translatorId, "", loading) + return translatorPackage.deepLoad(translatorId, "", loading) .then(function () { - var translate = require(translatorId); + var translate = translatorPackage(translatorId); module.text = translate(module.text, module); }); } @@ -1651,6 +1652,8 @@ var nextTick =(function () { var isNodeJS = false; function flush() { + /* jshint loopfunc: true */ + while (head.next) { head = head.next; var task = head.task; @@ -1673,9 +1676,13 @@ var nextTick =(function () { // Ensure continuation if the uncaught exception is suppressed // listening "uncaughtException" events (as domains does). // Continue in next event to avoid tick recursion. - domain && domain.exit(); + if (domain) { + domain.exit(); + } setTimeout(flush, 0); - domain && domain.enter(); + if (domain) { + domain.enter(); + } throw e; @@ -1732,10 +1739,22 @@ var nextTick =(function () { // modern browsers // http://www.nonblocking.io/2011/06/windownexttick.html var channel = new MessageChannel(); - channel.port1.onmessage = flush; - requestTick = function () { + // At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create + // working message ports the first time a page loads. + channel.port1.onmessage = function () { + requestTick = requestPortTick; + channel.port1.onmessage = flush; + flush(); + }; + var requestPortTick = function () { + // Opera requires us to provide a message payload, regardless of + // whether we use it. channel.port2.postMessage(0); }; + requestTick = function () { + setTimeout(flush, 0); + requestPortTick(); + }; } else { // old browsers @@ -1758,8 +1777,8 @@ var nextTick =(function () { // hard-to-minify characters. // See Mark Miller’s explanation of what this does. // http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming +var call = Function.call; function uncurryThis(f) { - var call = Function.call; return function () { return call.apply(f, arguments); }; @@ -2001,13 +2020,26 @@ function deprecate(callback, name, alternative) { // beginning of real work /** - * Creates fulfilled promises from non-thenables, - * Passes Q promises through, - * Coerces other thenables to Q promises. + * Constructs a promise for an immediate reference, passes promises through, or + * coerces promises from different systems. + * @param value immediate reference or promise */ function Q(value) { - return resolve(value); + // If the object is already a Promise, return it directly. This enables + // the resolve function to both be used to created references from objects, + // but to tolerably coerce non-promises to promises. + if (isPromise(value)) { + return value; + } + + // assimilate thenables + if (isPromiseAlike(value)) { + return coerce(value); + } else { + return fulfill(value); + } } +Q.resolve = Q; /** * Performs a task in a future turn of the event loop. @@ -2114,7 +2146,7 @@ function defer() { return; } - become(resolve(value)); + become(Q(value)); }; deferred.fulfill = function (value) { @@ -2175,17 +2207,77 @@ function promise(resolver) { if (typeof resolver !== "function") { throw new TypeError("resolver must be a function."); } - var deferred = defer(); - fcall( - resolver, - deferred.resolve, - deferred.reject, - deferred.notify - ).fail(deferred.reject); + try { + resolver(deferred.resolve, deferred.reject, deferred.notify); + } catch (reason) { + deferred.reject(reason); + } return deferred.promise; } +// XXX experimental. This method is a way to denote that a local value is +// serializable and should be immediately dispatched to a remote upon request, +// instead of passing a reference. +Q.passByCopy = function (object) { + //freeze(object); + //passByCopies.set(object, true); + return object; +}; + +Promise.prototype.passByCopy = function () { + //freeze(object); + //passByCopies.set(object, true); + return this; +}; + +/** + * If two promises eventually fulfill to the same value, promises that value, + * but otherwise rejects. + * @param x {Any*} + * @param y {Any*} + * @returns {Any*} a promise for x and y if they are the same, but a rejection + * otherwise. + * + */ +Q.join = function (x, y) { + return Q(x).join(y); +}; + +Promise.prototype.join = function (that) { + return Q([this, that]).spread(function (x, y) { + if (x === y) { + // TODO: "===" should be Object.is or equiv + return x; + } else { + throw new Error("Can't join: not the same: " + x + " " + y); + } + }); +}; + +/** + * Returns a promise for the first of an array of promises to become fulfilled. + * @param answers {Array[Any*]} promises to race + * @returns {Any*} the first promise to be fulfilled + */ +Q.race = race; +function race(answerPs) { + return promise(function(resolve, reject) { + // Switch to this once we can assume at least ES5 + // answerPs.forEach(function(answerP) { + // Q(answerP).then(resolve, reject); + // }); + // Use this in the meantime + for (var i = 0, len = answerPs.length; i < len; i++) { + Q(answerPs[i]).then(resolve, reject); + } + }); +} + +Promise.prototype.race = function () { + return this.then(Q.race); +}; + /** * Constructs a Promise with a promise descriptor object and optional fallback * function. The descriptor contains methods like when(rejected), get(name), @@ -2252,6 +2344,10 @@ function Promise(descriptor, fallback, inspect) { return promise; } +Promise.prototype.toString = function () { + return "[object Promise]"; +}; + Promise.prototype.then = function (fulfilled, rejected, progressed) { var self = this; var deferred = defer(); @@ -2323,48 +2419,41 @@ Promise.prototype.then = function (fulfilled, rejected, progressed) { return deferred.promise; }; +/** + * Registers an observer on a promise. + * + * Guarantees: + * + * 1. that fulfilled and rejected will be called only once. + * 2. that either the fulfilled callback or the rejected callback will be + * called, but not both. + * 3. that fulfilled and rejected will not be called in this turn. + * + * @param value promise or immediate reference to observe + * @param fulfilled function to be called with the fulfilled value + * @param rejected function to be called with the rejection exception + * @param progressed function to be called on any progress notifications + * @return promise for the return value from the invoked callback + */ +Q.when = when; +function when(value, fulfilled, rejected, progressed) { + return Q(value).then(fulfilled, rejected, progressed); +} + Promise.prototype.thenResolve = function (value) { - return when(this, function () { return value; }); + return this.then(function () { return value; }); }; -Promise.prototype.thenReject = function (reason) { - return when(this, function () { throw reason; }); -}; - -// Chainable methods -array_reduce( - [ - "isFulfilled", "isRejected", "isPending", - "dispatch", - "when", "spread", - "get", "set", "del", "delete", - "post", "send", "mapply", "invoke", "mcall", - "keys", - "fapply", "fcall", "fbind", - "all", "allResolved", - "timeout", "delay", - "catch", "finally", "fail", "fin", "progress", "done", - "nfcall", "nfapply", "nfbind", "denodeify", "nbind", - "npost", "nsend", "nmapply", "ninvoke", "nmcall", - "nodeify" - ], - function (undefined, name) { - Promise.prototype[name] = function () { - return Q[name].apply( - Q, - [this].concat(array_slice(arguments)) - ); - }; - }, - void 0 -); +Q.thenResolve = function (promise, value) { + return Q(promise).thenResolve(value); +}; -Promise.prototype.toSource = function () { - return this.toString(); +Promise.prototype.thenReject = function (reason) { + return this.then(function () { throw reason; }); }; -Promise.prototype.toString = function () { - return "[object Promise]"; +Q.thenReject = function (promise, reason) { + return Q(promise).thenReject(reason); }; /** @@ -2414,6 +2503,10 @@ function isPending(object) { return isPromise(object) && object.inspect().state === "pending"; } +Promise.prototype.isPending = function () { + return this.inspect().state === "pending"; +}; + /** * @returns whether the given object is a value or fulfilled * promise. @@ -2423,6 +2516,10 @@ function isFulfilled(object) { return !isPromise(object) || object.inspect().state === "fulfilled"; } +Promise.prototype.isFulfilled = function () { + return this.inspect().state === "fulfilled"; +}; + /** * @returns whether the given object is a rejected promise. */ @@ -2431,6 +2528,10 @@ function isRejected(object) { return isPromise(object) && object.inspect().state === "rejected"; } +Promise.prototype.isRejected = function () { + return this.inspect().state === "rejected"; +}; + //// BEGIN UNHANDLED REJECTION TRACKING // This promise library consumes exceptions thrown in handlers so they can be @@ -2458,11 +2559,7 @@ function displayUnhandledReasons() { function logUnhandledReasons() { for (var i = 0; i < unhandledReasons.length; i++) { var reason = unhandledReasons[i]; - if (reason && typeof reason.stack !== "undefined") { - console.warn("Unhandled rejection reason:", reason.stack); - } else { - console.warn("Unhandled rejection reason (no stack):", reason); - } + console.warn("Unhandled rejection reason:", reason); } } @@ -2489,7 +2586,11 @@ function trackRejection(promise, reason) { } unhandledRejections.push(promise); - unhandledReasons.push(reason); + if (reason && typeof reason.stack !== "undefined") { + unhandledReasons.push(reason.stack); + } else { + unhandledReasons.push("(no stack) " + reason); + } displayUnhandledReasons(); } @@ -2578,8 +2679,8 @@ function fulfill(value) { return value[name].apply(value, args); } }, - "apply": function (thisP, args) { - return value.apply(thisP, args); + "apply": function (thisp, args) { + return value.apply(thisp, args); }, "keys": function () { return object_keys(value); @@ -2589,28 +2690,6 @@ function fulfill(value) { }); } -/** - * Constructs a promise for an immediate reference, passes promises through, or - * coerces promises from different systems. - * @param value immediate reference or promise - */ -Q.resolve = resolve; -function resolve(value) { - // If the object is already a Promise, return it directly. This enables - // the resolve function to both be used to created references from objects, - // but to tolerably coerce non-promises to promises. - if (isPromise(value)) { - return value; - } - - // assimilate thenables - if (isPromiseAlike(value)) { - return coerce(value); - } else { - return fulfill(value); - } -} - /** * Converts thenables to Q promises. * @param promise thenable promise @@ -2644,31 +2723,10 @@ function master(object) { }, function fallback(op, args) { return dispatch(object, op, args); }, function () { - return resolve(object).inspect(); + return Q(object).inspect(); }); } -/** - * Registers an observer on a promise. - * - * Guarantees: - * - * 1. that fulfilled and rejected will be called only once. - * 2. that either the fulfilled callback or the rejected callback will be - * called, but not both. - * 3. that fulfilled and rejected will not be called in this turn. - * - * @param value promise or immediate reference to observe - * @param fulfilled function to be called with the fulfilled value - * @param rejected function to be called with the rejection exception - * @param progressed function to be called on any progress notifications - * @return promise for the return value from the invoked callback - */ -Q.when = when; -function when(value, fulfilled, rejected, progressed) { - return Q(value).then(fulfilled, rejected, progressed); -} - /** * Spreads the values of a promised array of arguments into the * fulfillment callback. @@ -2680,14 +2738,16 @@ function when(value, fulfilled, rejected, progressed) { * either callback. */ Q.spread = spread; -function spread(promise, fulfilled, rejected) { - return when(promise, function (valuesOrPromises) { - return all(valuesOrPromises).then(function (values) { - return fulfilled.apply(void 0, values); - }, rejected); - }, rejected); +function spread(value, fulfilled, rejected) { + return Q(value).spread(fulfilled, rejected); } +Promise.prototype.spread = function (fulfilled, rejected) { + return this.all().then(function (array) { + return fulfilled.apply(void 0, array); + }, rejected); +}; + /** * The async function is a decorator for generator functions, turning * them into asynchronous generators. Although generators are only part @@ -2747,7 +2807,7 @@ function async(makeGenerator) { } } var generator = makeGenerator.apply(this, arguments); - var callback = continuer.bind(continuer, "send"); + var callback = continuer.bind(continuer, "next"); var errback = continuer.bind(continuer, "throw"); return callback(); }; @@ -2805,7 +2865,7 @@ function _return(value) { * var add = Q.promised(function (a, b) { * return a + b; * }); - * add(Q.resolve(a), Q.resolve(B)); + * add(Q(a), Q(B)); * * @param {function} callback The function to decorate * @returns {function} a function that has been decorated. @@ -2828,26 +2888,17 @@ function promised(callback) { */ Q.dispatch = dispatch; function dispatch(object, op, args) { + return Q(object).dispatch(op, args); +} + +Promise.prototype.dispatch = function (op, args) { + var self = this; var deferred = defer(); nextTick(function () { - resolve(object).promiseDispatch(deferred.resolve, op, args); + self.promiseDispatch(deferred.resolve, op, args); }); return deferred.promise; -} - -/** - * Constructs a promise method that can be used to safely observe resolution of - * a promise for an arbitrarily named method like "propfind" in a future turn. - * - * "dispatcher" constructs methods like "get(promise, name)" and "set(promise)". - */ -Q.dispatcher = dispatcher; -function dispatcher(op) { - return function (object) { - var args = array_slice(arguments, 1); - return dispatch(object, op, args); - }; -} +}; /** * Gets the value of a property in a future turn. @@ -2855,7 +2906,13 @@ function dispatcher(op) { * @param name name of property to get * @return promise for the property value */ -Q.get = dispatcher("get"); +Q.get = function (object, key) { + return Q(object).dispatch("get", [key]); +}; + +Promise.prototype.get = function (key) { + return this.dispatch("get", [key]); +}; /** * Sets the value of a property in a future turn. @@ -2864,7 +2921,13 @@ Q.get = dispatcher("get"); * @param value new value of property * @return promise for the return value */ -Q.set = dispatcher("set"); +Q.set = function (object, key, value) { + return Q(object).dispatch("set", [key, value]); +}; + +Promise.prototype.set = function (key, value) { + return this.dispatch("set", [key, value]); +}; /** * Deletes a property in a future turn. @@ -2872,8 +2935,15 @@ Q.set = dispatcher("set"); * @param name name of property to delete * @return promise for the return value */ -Q["delete"] = // XXX experimental -Q.del = dispatcher("delete"); +Q.del = // XXX legacy +Q["delete"] = function (object, key) { + return Q(object).dispatch("delete", [key]); +}; + +Promise.prototype.del = // XXX legacy +Promise.prototype["delete"] = function (key) { + return this.dispatch("delete", [key]); +}; /** * Invokes a method in a future turn. @@ -2888,8 +2958,15 @@ Q.del = dispatcher("delete"); * @return promise for the return value */ // bound locally because it is used by other methods -var post = Q.post = dispatcher("post"); -Q.mapply = post; // experimental +Q.mapply = // XXX As proposed by "Redsandro" +Q.post = function (object, name, args) { + return Q(object).dispatch("post", [name, args]); +}; + +Promise.prototype.mapply = // XXX As proposed by "Redsandro" +Promise.prototype.post = function (name, args) { + return this.dispatch("post", [name, args]); +}; /** * Invokes a method in a future turn. @@ -2898,35 +2975,44 @@ Q.mapply = post; // experimental * @param ...args array of invocation arguments * @return promise for the return value */ -Q.send = send; -Q.invoke = send; // synonyms -Q.mcall = send; // experimental -function send(value, name) { - var args = array_slice(arguments, 2); - return post(value, name, args); -} +Q.send = // XXX Mark Miller's proposed parlance +Q.mcall = // XXX As proposed by "Redsandro" +Q.invoke = function (object, name /*...args*/) { + return Q(object).dispatch("post", [name, array_slice(arguments, 2)]); +}; + +Promise.prototype.send = // XXX Mark Miller's proposed parlance +Promise.prototype.mcall = // XXX As proposed by "Redsandro" +Promise.prototype.invoke = function (name /*...args*/) { + return this.dispatch("post", [name, array_slice(arguments, 1)]); +}; /** * Applies the promised function in a future turn. * @param object promise or immediate reference for target function * @param args array of application arguments */ -Q.fapply = fapply; -function fapply(value, args) { - return dispatch(value, "apply", [void 0, args]); -} +Q.fapply = function (object, args) { + return Q(object).dispatch("apply", [void 0, args]); +}; + +Promise.prototype.fapply = function (args) { + return this.dispatch("apply", [void 0, args]); +}; /** * Calls the promised function in a future turn. * @param object promise or immediate reference for target function * @param ...args array of application arguments */ -Q["try"] = fcall; // XXX experimental -Q.fcall = fcall; -function fcall(value) { - var args = array_slice(arguments, 1); - return fapply(value, args); -} +Q["try"] = +Q.fcall = function (object /* ...args*/) { + return Q(object).dispatch("apply", [void 0, array_slice(arguments, 1)]); +}; + +Promise.prototype.fcall = function (/*...args*/) { + return this.dispatch("apply", [void 0, array_slice(arguments)]); +}; /** * Binds the promised function, transforming return values into a fulfilled @@ -2934,14 +3020,26 @@ function fcall(value) { * @param object promise or immediate reference for target function * @param ...args array of application arguments */ -Q.fbind = fbind; -function fbind(value) { +Q.fbind = function (object /*...args*/) { + var promise = Q(object); var args = array_slice(arguments, 1); return function fbound() { - var allArgs = args.concat(array_slice(arguments)); - return dispatch(value, "apply", [this, allArgs]); + return promise.dispatch("apply", [ + this, + args.concat(array_slice(arguments)) + ]); }; -} +}; +Promise.prototype.fbind = function (/*...args*/) { + var promise = this; + var args = array_slice(arguments); + return function fbound() { + return promise.dispatch("apply", [ + this, + args.concat(array_slice(arguments)) + ]); + }; +}; /** * Requests the names of the owned properties of a promised @@ -2949,7 +3047,13 @@ function fbind(value) { * @param object promise or immediate reference for target object * @return promise for the keys of the eventually settled object */ -Q.keys = dispatcher("keys"); +Q.keys = function (object) { + return Q(object).dispatch("keys", []); +}; + +Promise.prototype.keys = function () { + return this.dispatch("keys", []); +}; /** * Turns an array of promises into a promise for an array. If any of @@ -2974,12 +3078,19 @@ function all(promises) { promises[index] = snapshot.value; } else { ++countDown; - when(promise, function (value) { - promises[index] = value; - if (--countDown === 0) { - deferred.resolve(promises); + when( + promise, + function (value) { + promises[index] = value; + if (--countDown === 0) { + deferred.resolve(promises); + } + }, + deferred.reject, + function (progress) { + deferred.notify({ index: index, value: progress }); } - }, deferred.reject); + ); } }, void 0); if (countDown === 0) { @@ -2989,6 +3100,10 @@ function all(promises) { }); } +Promise.prototype.all = function () { + return all(this); +}; + /** * Waits for all promises to be settled, either fulfilled or * rejected. This is distinct from `all` since that would stop @@ -3001,7 +3116,7 @@ function all(promises) { Q.allResolved = deprecate(allResolved, "allResolved", "allSettled"); function allResolved(promises) { return when(promises, function (promises) { - promises = array_map(promises, resolve); + promises = array_map(promises, Q); return when(all(array_map(promises, function (promise) { return when(promise, noop, noop); })), function () { @@ -3010,25 +3125,37 @@ function allResolved(promises) { }); } +Promise.prototype.allResolved = function () { + return allResolved(this); +}; + +/** + * @see Promise#allSettled + */ Q.allSettled = allSettled; -function allSettled(values) { - return when(values, function (values) { - return all(array_map(values, function (value, i) { - return when( - value, - function (fulfillmentValue) { - values[i] = { state: "fulfilled", value: fulfillmentValue }; - return values[i]; - }, - function (reason) { - values[i] = { state: "rejected", reason: reason }; - return values[i]; - } - ); - })).thenResolve(values); - }); +function allSettled(promises) { + return Q(promises).allSettled(); } +/** + * Turns an array of promises into a promise for an array of their states (as + * returned by `inspect`) when they have all settled. + * @param {Array[Any*]} values an array (or promise for an array) of values (or + * promises for values) + * @returns {Array[State]} an array of states for the respective values. + */ +Promise.prototype.allSettled = function () { + return this.then(function (promises) { + return all(array_map(promises, function (promise) { + promise = Q(promise); + function regardless() { + return promise.inspect(); + } + return promise.then(regardless, regardless); + })); + }); +}; + /** * Captures the failure of a promise, giving an oportunity to recover * with a callback. If the given promise is fulfilled, the returned @@ -3038,11 +3165,15 @@ function allSettled(values) { * given promise is rejected * @returns a promise for the return value of the callback */ -Q["catch"] = // XXX experimental -Q.fail = fail; -function fail(promise, rejected) { - return when(promise, void 0, rejected); -} +Q.fail = // XXX legacy +Q["catch"] = function (object, rejected) { + return Q(object).then(void 0, rejected); +}; + +Promise.prototype.fail = // XXX legacy +Promise.prototype["catch"] = function (rejected) { + return this.then(void 0, rejected); +}; /** * Attaches a listener that can respond to progress notifications from a @@ -3053,10 +3184,14 @@ function fail(promise, rejected) { * @returns the given promise, unchanged */ Q.progress = progress; -function progress(promise, progressed) { - return when(promise, void 0, void 0, progressed); +function progress(object, progressed) { + return Q(object).then(void 0, void 0, progressed); } +Promise.prototype.progress = function (progressed) { + return this.then(void 0, void 0, progressed); +}; + /** * Provides an opportunity to observe the settling of a promise, * regardless of whether the promise is fulfilled or rejected. Forwards @@ -3068,19 +3203,25 @@ function progress(promise, progressed) { * @returns a promise for the resolution of the given promise when * ``fin`` is done. */ -Q["finally"] = // XXX experimental -Q.fin = fin; -function fin(promise, callback) { - return when(promise, function (value) { - return when(callback(), function () { +Q.fin = // XXX legacy +Q["finally"] = function (object, callback) { + return Q(object)["finally"](callback); +}; + +Promise.prototype.fin = // XXX legacy +Promise.prototype["finally"] = function (callback) { + callback = Q(callback); + return this.then(function (value) { + return callback.fcall().then(function () { return value; }); - }, function (exception) { - return when(callback(), function () { - return reject(exception); + }, function (reason) { + // TODO attempt to recycle the rejection with "this". + return callback.fcall().then(function () { + throw reason; }); }); -} +}; /** * Terminates a chain of promises, forcing rejections to be @@ -3088,14 +3229,16 @@ function fin(promise, callback) { * @param {Any*} promise at the end of a chain of promises * @returns nothing */ -Q.done = done; -function done(promise, fulfilled, rejected, progress) { +Q.done = function (object, fulfilled, rejected, progress) { + return Q(object).done(fulfilled, rejected, progress); +}; + +Promise.prototype.done = function (fulfilled, rejected, progress) { var onUnhandledError = function (error) { // forward to a future turn so that ``when`` // does not catch it and turn it into a rejection. nextTick(function () { makeStackTraceLong(error, promise); - if (Q.onerror) { Q.onerror(error); } else { @@ -3105,15 +3248,16 @@ function done(promise, fulfilled, rejected, progress) { }; // Avoid unnecessary `nextTick`ing via an unnecessary `when`. - var promiseToHandle = fulfilled || rejected || progress ? - when(promise, fulfilled, rejected, progress) : - promise; + var promise = fulfilled || rejected || progress ? + this.then(fulfilled, rejected, progress) : + this; if (typeof process === "object" && process && process.domain) { onUnhandledError = process.domain.bind(onUnhandledError); } - fail(promiseToHandle, onUnhandledError); -} + + promise.then(void 0, onUnhandledError); +}; /** * Causes a promise to be rejected if it does not get fulfilled before @@ -3124,14 +3268,17 @@ function done(promise, fulfilled, rejected, progress) { * @returns a promise for the resolution of the given promise if it is * fulfilled before the timeout, otherwise rejected. */ -Q.timeout = timeout; -function timeout(promise, ms, msg) { +Q.timeout = function (object, ms, message) { + return Q(object).timeout(ms, message); +}; + +Promise.prototype.timeout = function (ms, message) { var deferred = defer(); var timeoutId = setTimeout(function () { - deferred.reject(new Error(msg || "Timed out after " + ms + " ms")); + deferred.reject(new Error(message || "Timed out after " + ms + " ms")); }, ms); - when(promise, function (value) { + this.then(function (value) { clearTimeout(timeoutId); deferred.resolve(value); }, function (exception) { @@ -3140,32 +3287,34 @@ function timeout(promise, ms, msg) { }, deferred.notify); return deferred.promise; -} +}; /** - * Returns a promise for the given value (or promised value) after some - * milliseconds. + * Returns a promise for the given value (or promised value), some + * milliseconds after it resolved. Passes rejections immediately. * @param {Any*} promise * @param {Number} milliseconds - * @returns a promise for the resolution of the given promise after some - * time has elapsed. + * @returns a promise for the resolution of the given promise after milliseconds + * time has elapsed since the resolution of the given promise. + * If the given promise rejects, that is passed immediately. */ -Q.delay = delay; -function delay(promise, timeout) { +Q.delay = function (object, timeout) { if (timeout === void 0) { - timeout = promise; - promise = void 0; + timeout = object; + object = void 0; } + return Q(object).delay(timeout); +}; - var deferred = defer(); - - when(promise, undefined, undefined, deferred.notify); - setTimeout(function () { - deferred.resolve(promise); - }, timeout); - - return deferred.promise; -} +Promise.prototype.delay = function (timeout) { + return this.then(function (value) { + var deferred = defer(); + setTimeout(function () { + deferred.resolve(value); + }, timeout); + return deferred.promise; + }); +}; /** * Passes a continuation to a Node function, which is called with the given @@ -3176,74 +3325,86 @@ function delay(promise, timeout) { * }) * */ -Q.nfapply = nfapply; -function nfapply(callback, args) { - var nodeArgs = array_slice(args); +Q.nfapply = function (callback, args) { + return Q(callback).nfapply(args); +}; + +Promise.prototype.nfapply = function (args) { var deferred = defer(); + var nodeArgs = array_slice(args); nodeArgs.push(deferred.makeNodeResolver()); - - fapply(callback, nodeArgs).fail(deferred.reject); + this.fapply(nodeArgs).fail(deferred.reject); return deferred.promise; -} +}; /** * Passes a continuation to a Node function, which is called with the given * arguments provided individually, and returns a promise. - * - * Q.nfcall(FS.readFile, __filename) - * .then(function (content) { - * }) + * @example + * Q.nfcall(FS.readFile, __filename) + * .then(function (content) { + * }) * */ -Q.nfcall = nfcall; -function nfcall(callback/*, ...args */) { - var nodeArgs = array_slice(arguments, 1); +Q.nfcall = function (callback /*...args*/) { + var args = array_slice(arguments, 1); + return Q(callback).nfapply(args); +}; + +Promise.prototype.nfcall = function (/*...args*/) { + var nodeArgs = array_slice(arguments); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - - fapply(callback, nodeArgs).fail(deferred.reject); + this.fapply(nodeArgs).fail(deferred.reject); return deferred.promise; -} +}; /** * Wraps a NodeJS continuation passing function and returns an equivalent * version that returns a promise. - * - * Q.nfbind(FS.readFile, __filename)("utf-8") - * .then(console.log) - * .done() - * + * @example + * Q.nfbind(FS.readFile, __filename)("utf-8") + * .then(console.log) + * .done() */ -Q.nfbind = nfbind; -Q.denodeify = Q.nfbind; // synonyms -function nfbind(callback/*, ...args */) { +Q.nfbind = +Q.denodeify = function (callback /*...args*/) { var baseArgs = array_slice(arguments, 1); return function () { var nodeArgs = baseArgs.concat(array_slice(arguments)); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - - fapply(callback, nodeArgs).fail(deferred.reject); + Q(callback).fapply(nodeArgs).fail(deferred.reject); return deferred.promise; }; -} +}; + +Promise.prototype.nfbind = +Promise.prototype.denodeify = function (/*...args*/) { + var args = array_slice(arguments); + args.unshift(this); + return Q.denodeify.apply(void 0, args); +}; -Q.nbind = nbind; -function nbind(callback, thisArg /*, ... args*/) { +Q.nbind = function (callback, thisp /*...args*/) { var baseArgs = array_slice(arguments, 2); return function () { var nodeArgs = baseArgs.concat(array_slice(arguments)); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - function bound() { - return callback.apply(thisArg, arguments); + return callback.apply(thisp, arguments); } - - fapply(bound, nodeArgs).fail(deferred.reject); + Q(bound).fapply(nodeArgs).fail(deferred.reject); return deferred.promise; }; -} +}; + +Promise.prototype.nbind = function (/*thisp, ...args*/) { + var args = array_slice(arguments, 0); + args.unshift(this); + return Q.nbind.apply(void 0, args); +}; /** * Calls a method of a Node-style object that accepts a Node-style @@ -3254,16 +3415,19 @@ function nbind(callback, thisArg /*, ... args*/) { * will be provided by Q and appended to these arguments. * @returns a promise for the value or error */ -Q.npost = npost; -Q.nmapply = npost; // synonyms -function npost(object, name, args) { +Q.nmapply = // XXX As proposed by "Redsandro" +Q.npost = function (object, name, args) { + return Q(object).npost(name, args); +}; + +Promise.prototype.nmapply = // XXX As proposed by "Redsandro" +Promise.prototype.npost = function (name, args) { var nodeArgs = array_slice(args || []); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - - post(object, name, nodeArgs).fail(deferred.reject); + this.dispatch("post", [name, nodeArgs]).fail(deferred.reject); return deferred.promise; -} +}; /** * Calls a method of a Node-style object that accepts a Node-style @@ -3275,21 +3439,44 @@ function npost(object, name, args) { * be provided by Q and appended to these arguments. * @returns a promise for the value or error */ -Q.nsend = nsend; -Q.ninvoke = Q.nsend; // synonyms -Q.nmcall = Q.nsend; // synonyms -function nsend(object, name /*, ...args*/) { +Q.nsend = // XXX Based on Mark Miller's proposed "send" +Q.nmcall = // XXX Based on "Redsandro's" proposal +Q.ninvoke = function (object, name /*...args*/) { var nodeArgs = array_slice(arguments, 2); var deferred = defer(); nodeArgs.push(deferred.makeNodeResolver()); - post(object, name, nodeArgs).fail(deferred.reject); + Q(object).dispatch("post", [name, nodeArgs]).fail(deferred.reject); return deferred.promise; -} +}; +Promise.prototype.nsend = // XXX Based on Mark Miller's proposed "send" +Promise.prototype.nmcall = // XXX Based on "Redsandro's" proposal +Promise.prototype.ninvoke = function (name /*...args*/) { + var nodeArgs = array_slice(arguments, 1); + var deferred = defer(); + nodeArgs.push(deferred.makeNodeResolver()); + this.dispatch("post", [name, nodeArgs]).fail(deferred.reject); + return deferred.promise; +}; + +/** + * If a function would like to support both Node continuation-passing-style and + * promise-returning-style, it can end its internal promise chain with + * `nodeify(nodeback)`, forwarding the optional nodeback argument. If the user + * elects to use a nodeback, the result will be sent there. If they do not + * pass a nodeback, they will receive the result promise. + * @param object a result (or a promise for a result) + * @param {Function} nodeback a Node.js-style callback + * @returns either the promise or nothing + */ Q.nodeify = nodeify; -function nodeify(promise, nodeback) { +function nodeify(object, nodeback) { + return Q(object).nodeify(nodeback); +} + +Promise.prototype.nodeify = function (nodeback) { if (nodeback) { - promise.then(function (value) { + this.then(function (value) { nextTick(function () { nodeback(null, value); }); @@ -3299,9 +3486,9 @@ function nodeify(promise, nodeback) { }); }); } else { - return promise; + return this; } -} +}; // All code before this point will be filtered from stack traces. var qEndingLine = captureLine(); diff --git a/build.js b/build.js index a8e768b9..1cfd9366 100644 --- a/build.js +++ b/build.js @@ -1,4 +1,5 @@ +var Q = require("q"); var FS = require("fs"); var Path = require("path"); var Require = require("./node"); @@ -9,8 +10,16 @@ module.exports = build; function build(path) { return Require.findPackageLocationAndModuleId(path) .then(function (arg) { - return Require.loadPackage(arg.location, { - overlays: ["browser"] + return Q.fcall(function () { + return Require.loadPackage(arg.location, { + overlays: ["node"] + }); + }) + .then(function (translatorPackage) { + return Require.loadPackage(arg.location, { + overlays: ["browser"], + translatorPackage: translatorPackage + }) }) .then(function (package) { return package.deepLoad(arg.id) diff --git a/require.js b/require.js index e8797391..a2504111 100644 --- a/require.js +++ b/require.js @@ -99,12 +99,13 @@ Require.makeRequire = function (config) { return Q.fcall(function () { if (config.translators[module.extension]) { var translatorId = config.translators[module.extension]; + var translatorPackage = config.translatorPackage || require; // TODO try to load translator related modules in a // parallel module system so that they do not get // bundled - return deepLoad(translatorId, "", loading) + return translatorPackage.deepLoad(translatorId, "", loading) .then(function () { - var translate = require(translatorId); + var translate = translatorPackage(translatorId); module.text = translate(module.text, module); }); } diff --git a/spec/translator-package/test.js b/spec/translator-package/test.js new file mode 100644 index 00000000..aa60b61a --- /dev/null +++ b/spec/translator-package/test.js @@ -0,0 +1,2 @@ +exports.assert = function () {}; +exports.print = console.log.bind(console); From 0deff2dea0d358636b88918adf8983ea54ea3278 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 18 Nov 2013 23:48:38 -0800 Subject: [PATCH 32/64] Melt off Extensions and Path loaders Replace with simple location loader. We don't branch. --- boot.js | 82 +++-------------------- boot/preload-boilerplate.js | 130 ++++++++++-------------------------- build.js | 7 +- require.js | 82 +++-------------------- 4 files changed, 62 insertions(+), 239 deletions(-) diff --git a/boot.js b/boot.js index ae08ea6a..6365e1a0 100644 --- a/boot.js +++ b/boot.js @@ -1160,7 +1160,7 @@ function postConfigurePackage(config, description) { if (description["redirect-patterns"]) { var describedPatterns = description["redirect-patterns"]; for (var pattern in describedPatterns) { - if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + if (has(describedPatterns, pattern)) { redirectTable.push([ new RegExp(pattern), describedPatterns[pattern] @@ -1203,13 +1203,6 @@ function resolve(id, baseId) { return target.join("/"); } -Require.base = function (location) { - // matches Unix basename - return String(location) - .replace(/(.+?)\/+$/, "$1") - .match(/([^\/]+$|^\/$|^$)/)[1]; -}; - Require.extension = function (location) { var match = /\.([^\/\.]+)$/.exec(location); if (match) { @@ -1321,14 +1314,11 @@ Require.makeCommonLoader = function (config, load) { config, Require.RedirectPatternsLoader( config, - Require.ExtensionsLoader( + Require.LocationLoader( config, - Require.PathsLoader( + Require.MemoizedLoader( config, - Require.MemoizedLoader( - config, - load - ) + load ) ) ) @@ -1381,65 +1371,15 @@ Require.MappingsLoader = function(config, load) { }; }; -Require.ExtensionsLoader = function(config, load) { - var extensions = config.extensions || ["js"]; - var loadWithExtension = extensions.reduceRight(function (next, extension) { - return function (id, module) { - return load(id + "." + extension, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " with extensions " + - JSON.stringify(extensions) + " in package at " + - JSON.stringify(config.location) - ); - }); +Require.LocationLoader = function (config, load) { return function (id, module) { - if (Require.base(id).indexOf(".") !== -1) { - // already has an extension - return load(id, module); - } else { - return loadWithExtension(id, module); - } - }; -}; - -// Attempts to load using multiple base paths (or one absolute path) with a -// single loader. -Require.PathsLoader = function(config, load) { - var loadFromPaths = config.paths.reduceRight(function (next, path) { - return function (id, module) { - var newId = URL.resolve(path, id); - return load(newId, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " from paths " + - JSON.stringify(config.paths) + " in package at " + - JSON.stringify(config.location) - ); - }); - return function(id, module) { - if (Require.isAbsolute(id)) { - // already fully qualified - return load(id, module); - } else { - return loadFromPaths(id, module); + var base = id; + var extension = Require.extension(id); + if (!extension && extension !== "js") { + base += ".js"; } + var location = URL.resolve(config.location, base); + return load(location, module); }; }; diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 7b9cb860..00726119 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -40,7 +40,7 @@ module.exports = function bootstrapPreload(plan) { return boot(preload(plan)); }; -}],[{"../browser":3,"url":5,"q":8,"./script-params":4},function (require, exports, module){ +}],[{"../browser":4,"url":6,"q":8,"./script-params":5},function (require, exports, module){ // mr boot/browser // --------------- @@ -90,7 +90,7 @@ function boot(preloaded) { } -}],[{"./script-injection":6,"q":8},function (require, exports, module){ +}],[{"./script-injection":3,"q":8},function (require, exports, module){ // mr boot/preload // --------------- @@ -130,7 +130,26 @@ module.exports = function preload(plan) { return preloaded; }; -}],[{"./require":7,"url":5,"q":8},function (require, exports, module){ +}],[{},function (require, exports, module){ + +// mr boot/script-injection +// ------------------------ + + +module.exports = load; + +var head = document.querySelector("head"); +function load(location) { + var script = document.createElement("script"); + script.src = URL.resolve(params.mrLocation, location); + script.onload = function () { + // remove clutter + script.parentNode.removeChild(script); + }; + head.appendChild(script); +}; + +}],[{"./require":7,"url":6,"q":8},function (require, exports, module){ // mr browser // ---------- @@ -374,7 +393,7 @@ Require.makeLoader = function (config) { module.exports = Require; -}],[{"url":5},function (require, exports, module){ +}],[{"url":6},function (require, exports, module){ // mr boot/script-params // --------------------- @@ -477,26 +496,7 @@ exports.resolve = function resolve(base, relative) { return resolved; }; -}],[{},function (require, exports, module){ - -// mr boot/script-injection -// ------------------------ - - -module.exports = load; - -var head = document.querySelector("head"); -function load(location) { - var script = document.createElement("script"); - script.src = URL.resolve(params.mrLocation, location); - script.onload = function () { - // remove clutter - script.parentNode.removeChild(script); - }; - head.appendChild(script); -}; - -}],[{"q":8,"url":5},function (require, exports, module){ +}],[{"q":8,"url":6},function (require, exports, module){ // mr require // ---------- @@ -1232,7 +1232,7 @@ function postConfigurePackage(config, description) { if (description["redirect-patterns"]) { var describedPatterns = description["redirect-patterns"]; for (var pattern in describedPatterns) { - if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + if (has(describedPatterns, pattern)) { redirectTable.push([ new RegExp(pattern), describedPatterns[pattern] @@ -1275,13 +1275,6 @@ function resolve(id, baseId) { return target.join("/"); } -Require.base = function (location) { - // matches Unix basename - return String(location) - .replace(/(.+?)\/+$/, "$1") - .match(/([^\/]+$|^\/$|^$)/)[1]; -}; - Require.extension = function (location) { var match = /\.([^\/\.]+)$/.exec(location); if (match) { @@ -1393,14 +1386,11 @@ Require.makeCommonLoader = function (config, load) { config, Require.RedirectPatternsLoader( config, - Require.ExtensionsLoader( + Require.LocationLoader( config, - Require.PathsLoader( + Require.MemoizedLoader( config, - Require.MemoizedLoader( - config, - load - ) + load ) ) ) @@ -1453,65 +1443,15 @@ Require.MappingsLoader = function(config, load) { }; }; -Require.ExtensionsLoader = function(config, load) { - var extensions = config.extensions || ["js"]; - var loadWithExtension = extensions.reduceRight(function (next, extension) { - return function (id, module) { - return load(id + "." + extension, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " with extensions " + - JSON.stringify(extensions) + " in package at " + - JSON.stringify(config.location) - ); - }); +Require.LocationLoader = function (config, load) { return function (id, module) { - if (Require.base(id).indexOf(".") !== -1) { - // already has an extension - return load(id, module); - } else { - return loadWithExtension(id, module); - } - }; -}; - -// Attempts to load using multiple base paths (or one absolute path) with a -// single loader. -Require.PathsLoader = function(config, load) { - var loadFromPaths = config.paths.reduceRight(function (next, path) { - return function (id, module) { - var newId = URL.resolve(path, id); - return load(newId, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " from paths " + - JSON.stringify(config.paths) + " in package at " + - JSON.stringify(config.location) - ); - }); - return function(id, module) { - if (Require.isAbsolute(id)) { - // already fully qualified - return load(id, module); - } else { - return loadFromPaths(id, module); + var base = id; + var extension = Require.extension(id); + if (!extension && extension !== "js") { + base += ".js"; } + var location = URL.resolve(config.location, base); + return load(location, module); }; }; diff --git a/build.js b/build.js index 1cfd9366..e3483530 100644 --- a/build.js +++ b/build.js @@ -10,15 +10,18 @@ module.exports = build; function build(path) { return Require.findPackageLocationAndModuleId(path) .then(function (arg) { + var cache = {}; return Q.fcall(function () { return Require.loadPackage(arg.location, { - overlays: ["node"] + overlays: ["node"], + cache: cache }); }) .then(function (translatorPackage) { return Require.loadPackage(arg.location, { overlays: ["browser"], - translatorPackage: translatorPackage + translatorPackage: translatorPackage, + cache: cache }) }) .then(function (package) { diff --git a/require.js b/require.js index a2504111..0fd2efee 100644 --- a/require.js +++ b/require.js @@ -729,7 +729,7 @@ function postConfigurePackage(config, description) { if (description["redirect-patterns"]) { var describedPatterns = description["redirect-patterns"]; for (var pattern in describedPatterns) { - if (Object.prototype.hasOwnProperty.call(describedPatterns, pattern)) { + if (has(describedPatterns, pattern)) { redirectTable.push([ new RegExp(pattern), describedPatterns[pattern] @@ -772,13 +772,6 @@ function resolve(id, baseId) { return target.join("/"); } -Require.base = function (location) { - // matches Unix basename - return String(location) - .replace(/(.+?)\/+$/, "$1") - .match(/([^\/]+$|^\/$|^$)/)[1]; -}; - Require.extension = function (location) { var match = /\.([^\/\.]+)$/.exec(location); if (match) { @@ -890,14 +883,11 @@ Require.makeCommonLoader = function (config, load) { config, Require.RedirectPatternsLoader( config, - Require.ExtensionsLoader( + Require.LocationLoader( config, - Require.PathsLoader( + Require.MemoizedLoader( config, - Require.MemoizedLoader( - config, - load - ) + load ) ) ) @@ -950,65 +940,15 @@ Require.MappingsLoader = function(config, load) { }; }; -Require.ExtensionsLoader = function(config, load) { - var extensions = config.extensions || ["js"]; - var loadWithExtension = extensions.reduceRight(function (next, extension) { - return function (id, module) { - return load(id + "." + extension, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " with extensions " + - JSON.stringify(extensions) + " in package at " + - JSON.stringify(config.location) - ); - }); +Require.LocationLoader = function (config, load) { return function (id, module) { - if (Require.base(id).indexOf(".") !== -1) { - // already has an extension - return load(id, module); - } else { - return loadWithExtension(id, module); - } - }; -}; - -// Attempts to load using multiple base paths (or one absolute path) with a -// single loader. -Require.PathsLoader = function(config, load) { - var loadFromPaths = config.paths.reduceRight(function (next, path) { - return function (id, module) { - var newId = URL.resolve(path, id); - return load(newId, module) - .fail(function (error) { - if (/^Can't find /.test(error.message)) { - return next(id, module); - } else { - throw error; - } - }); - }; - }, function (id, module) { - throw new Error( - "Can't find " + JSON.stringify(id) + " from paths " + - JSON.stringify(config.paths) + " in package at " + - JSON.stringify(config.location) - ); - }); - return function(id, module) { - if (Require.isAbsolute(id)) { - // already fully qualified - return load(id, module); - } else { - return loadFromPaths(id, module); + var base = id; + var extension = Require.extension(id); + if (!extension && extension !== "js") { + base += ".js"; } + var location = URL.resolve(config.location, base); + return load(location, module); }; }; From 3317962695d82288b455ae6363788631e9582267 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 19 Nov 2013 11:45:16 -0800 Subject: [PATCH 33/64] Fix loading of preload bundles Test in mop-integration#22e8d30accb05fa37fba46fd961238bec0a7b29d Ported from 6ba67790e5a4d7539e6b3e24be8695724b9ab4ad and 518f94791f15a986a8771bdf7a95bd54d56a91a8 --- boot.js | 53 +++++----- boot/browser.js | 6 +- boot/preload-boilerplate.js | 201 +++++++++++++++++++----------------- boot/preload-entry.js | 4 +- boot/preload.js | 4 +- boot/script-injection.js | 2 +- boot/script-params.js | 2 +- browser.js | 45 ++++---- 8 files changed, 168 insertions(+), 149 deletions(-) diff --git a/boot.js b/boot.js index dc553076..5f47e7a4 100644 --- a/boot.js +++ b/boot.js @@ -37,11 +37,11 @@ var Require = require("../browser"); var URL = require("url"); var Q = require("q"); - -var params = require("./script-params")("boot.js"); +var getParams = require("./script-params"); module.exports = boot; -function boot(preloaded) { +function boot(preloaded, params) { + params = params || getParams("boot.js"); var config = {preloaded: preloaded}; var applicationLocation = URL.resolve(window.location, params.package || "."); @@ -226,6 +226,7 @@ var getDefinition = function (hash, id) { definitions[hash][id] = definitions[hash][id] || Q.defer(); return definitions[hash][id]; }; + // global montageDefine = function (hash, id, module) { getDefinition(hash, id).resolve(module); @@ -262,7 +263,9 @@ Require.ScriptLoader = function (config) { Require.loadScript(location); - return getDefinition(hash, module.id).promise; + var definition = getDefinition(hash, module.id).promise; + loadIfNotPreloaded(location, definition, config.preloaded); + return definition; }) .then(function (definition) { /*jshint -W089 */ @@ -283,25 +286,7 @@ Require.loadPackageDescription = function (dependency, config) { if (dependency.hash) { // use script injection var definition = getDefinition(dependency.hash, "package.json").promise; var location = URL.resolve(dependency.location, "package.json.load.js"); - - // The package.json might come in a preloading bundle. If so, we do not - // want to issue a script injection. However, if by the time preloading - // has finished the package.json has not arrived, we will need to kick off - // a request for the package.json.load.js script. - if (config.preloaded && config.preloaded.isPending()) { - config.preloaded - .then(function () { - if (definition.isPending()) { - Require.loadScript(location); - } - }) - .done(); - } else if (definition.isPending()) { - // otherwise preloading has already completed and we don't have the - // package description, so load it - Require.loadScript(location); - } - + loadIfNotPreloaded(location, definition, config.preloaded); return definition.get("exports"); } else { // fall back to normal means @@ -319,6 +304,26 @@ Require.makeLoader = function (config) { return Require.makeCommonLoader(config, Loader(config)); }; +function loadIfNotPreloaded(location, definition, preloaded) { + // The package.json might come in a preloading bundle. If so, we do not + // want to issue a script injection. However, if by the time preloading + // has finished the package.json has not arrived, we will need to kick off + // a request for the requested script. + if (preloaded && preloaded.isPending()) { + preloaded + .then(function () { + if (definition.isPending()) { + Require.loadScript(location); + } + }) + .done(); + } else if (definition.isPending()) { + // otherwise preloading has already completed and we don't have the + // module, so load it + Require.loadScript(location); + } +} + module.exports = Require; }],[{"url":3},function (require, exports, module){ @@ -351,7 +356,7 @@ function getParams(scriptName) { // `data-boot-location` property on the script instead. This will also // serve to inform the boot script of the location of the loading // package, albeit Montage or Mr. - if (script.src && (match = script.src.match(re))) { + if (scriptName && script.src && (match = script.src.match(re))) { location = match[1]; } if (script.hasAttribute("data-boot-location")) { diff --git a/boot/browser.js b/boot/browser.js index c1883abe..cea8a161 100644 --- a/boot/browser.js +++ b/boot/browser.js @@ -3,11 +3,11 @@ var Require = require("../browser"); var URL = require("url"); var Q = require("q"); - -var params = require("./script-params")("boot.js"); +var getParams = require("./script-params"); module.exports = boot; -function boot(preloaded) { +function boot(preloaded, params) { + params = params || getParams("boot.js"); var config = {preloaded: preloaded}; var applicationLocation = URL.resolve(window.location, params.package || "."); diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js index 1bab9ae5..87d500a7 100644 --- a/boot/preload-boilerplate.js +++ b/boot/preload-boilerplate.js @@ -27,7 +27,7 @@ }; return modules[0].getExports(); -})((function (global){return[[{"./browser":1,"./preload":2},function (require, exports, module){ +})((function (global){return[[{"./browser":1,"./preload":2,"./script-params":3},function (require, exports, module){ // mr boot/preload-entry // --------------------- @@ -35,12 +35,14 @@ var boot = require("./browser"); var preload = require("./preload"); +var getParams = require("./script-params"); module.exports = function bootstrapPreload(plan) { - return boot(preload(plan)); + var params = getParams(); + return boot(preload(plan, params), params); }; -}],[{"../browser":3,"url":5,"q":8,"./script-params":4},function (require, exports, module){ +}],[{"../browser":4,"url":5,"q":8,"./script-params":3},function (require, exports, module){ // mr boot/browser // --------------- @@ -50,11 +52,11 @@ module.exports = function bootstrapPreload(plan) { var Require = require("../browser"); var URL = require("url"); var Q = require("q"); - -var params = require("./script-params")("boot.js"); +var getParams = require("./script-params"); module.exports = boot; -function boot(preloaded) { +function boot(preloaded, params) { + params = params || getParams("boot.js"); var config = {preloaded: preloaded}; var applicationLocation = URL.resolve(window.location, params.package || "."); @@ -99,7 +101,7 @@ function boot(preloaded) { var load = require("./script-injection"); var Q = require("q"); -module.exports = function preload(plan) { +module.exports = function preload(plan, params) { // Each bundle ends with a bundleLoaded(name) call. We use these hooks to // synchronize the preloader. @@ -117,7 +119,7 @@ module.exports = function preload(plan) { var preloaded = plan.reduce(function (previous, bundleLocations) { return previous.then(function () { return Q.all(bundleLocations.map(function (bundleLocation) { - load(bundleLocation); + load(resolve(params.location, bundleLocation)); return getHook(bundleLocation).promise; })); }); @@ -130,6 +132,76 @@ module.exports = function preload(plan) { return preloaded; }; +}],[{"url":5},function (require, exports, module){ + +// mr boot/script-params +// --------------------- + + +var URL = require("url"); + +module.exports = getParams; +function getParams(scriptName) { + var i, j, + match, + script, + location, + attr, + name, + re = new RegExp("^(.*)" + scriptName + "(?:[\\?\\.]|$)", "i"); + var params = {}; + // Find the + + diff --git a/spec/index.js b/spec/index.js new file mode 100644 index 00000000..124f84d9 --- /dev/null +++ b/spec/index.js @@ -0,0 +1,118 @@ +/* +Copyright (c) 2012, Motorola Mobility LLC. +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Motorola Mobility LLC nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +var Require = require("../require"); + +var location = __dirname; +if (Require.directoryPathToLocation) { + location = Require.directoryPathToLocation(location); +} + +describe("require", function () { + [ + "cyclic", + "determinism", + "exactExports", + "hasOwnProperty", + "method", + "missing", + "monkeys", + "nested", + "relative", + "top-level", + "transitive", + "module-exports", + "return", + {name: "named-packages", node: false}, + {name: "named-mappings", node: false}, + "named-parent-package", + {name: "load-package", node: false}, + {name: "load-package-name", node: false}, + {name: "not-found", node: false}, + "redirects", + "redirects-package", + "comments", + "identify", + "dev-dependencies", + "production", + "case-sensitive", + "inject-dependency", + "inject-mapping", + {name: "script-injection-dep", node: false}, + {name: "script-injection", node: false}, + "read", + "main-name", + "main", + "sandbox", + "compiler", + "translator", + "compiler-package", + "translator-package", + "redirect-patterns", + "main" + ].forEach(function (test) { + if (typeof test === "object") { + if (test.node === false && typeof process !== "undefined") { + return; + } + test = test.name; + } + it(test, function () { + var spec = this; + var done; + var message; + + //console.log(test + ":", "START"); + + return Require.loadPackage(location + test + "/", {}) + .then(function (pkg) { + pkg.inject("test", { + print: function (_message, level) { + //console.log(test + ":", _message); + if (_message === "DONE") { + message = _message; + } + }, + assert: function (guard, message) { + //console.log(test + ":", guard ? "PASS" : "FAIL", message); + expect(!!guard).toBe(true); + } + }); + + return pkg.async("program"); + }) + .then(function () { + expect(message).toBe("DONE"); + }); + }); + }); +}); + diff --git a/spec/lib/jasmine-1.2.0/MIT.LICENSE b/spec/lib/jasmine-1.2.0/MIT.LICENSE deleted file mode 100644 index 7c435baa..00000000 --- a/spec/lib/jasmine-1.2.0/MIT.LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2008-2011 Pivotal Labs - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/spec/lib/jasmine-1.2.0/jasmine-html.js b/spec/lib/jasmine-1.2.0/jasmine-html.js deleted file mode 100644 index a0b06394..00000000 --- a/spec/lib/jasmine-1.2.0/jasmine-html.js +++ /dev/null @@ -1,616 +0,0 @@ -jasmine.HtmlReporterHelpers = {}; - -jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { - var results = child.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - - return status; -}; - -jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { - var parentDiv = this.dom.summary; - var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; - var parent = child[parentSuite]; - - if (parent) { - if (typeof this.views.suites[parent.id] == 'undefined') { - this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); - } - parentDiv = this.views.suites[parent.id].element; - } - - parentDiv.appendChild(childElement); -}; - - -jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { - for(var fn in jasmine.HtmlReporterHelpers) { - ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; - } -}; - -jasmine.HtmlReporter = function(_doc) { - var self = this; - var doc = _doc || window.document; - - var reporterView; - - var dom = {}; - - // Jasmine Reporter Public Interface - self.logRunningSpecs = false; - - self.reportRunnerStarting = function(runner) { - var specs = runner.specs() || []; - - if (specs.length == 0) { - return; - } - - createReporterDom(runner.env.versionString()); - doc.body.appendChild(dom.reporter); - - reporterView = new jasmine.HtmlReporter.ReporterView(dom); - reporterView.addSpecs(specs, self.specFilter); - }; - - self.reportRunnerResults = function(runner) { - reporterView && reporterView.complete(); - }; - - self.reportSuiteResults = function(suite) { - reporterView.suiteComplete(suite); - }; - - self.reportSpecStarting = function(spec) { - if (self.logRunningSpecs) { - self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } - }; - - self.reportSpecResults = function(spec) { - reporterView.specComplete(spec); - }; - - self.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } - }; - - self.specFilter = function(spec) { - if (!focusedSpecName()) { - return true; - } - - return spec.getFullName().indexOf(focusedSpecName()) === 0; - }; - - return self; - - function focusedSpecName() { - var specName; - - (function memoizeFocusedSpec() { - if (specName) { - return; - } - - var paramMap = []; - var params = doc.location.search.substring(1).split('&'); - - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - specName = paramMap.spec; - })(); - - return specName; - } - - function createReporterDom(version) { - dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, - dom.banner = self.createDom('div', { className: 'banner' }, - self.createDom('span', { className: 'title' }, "Jasmine "), - self.createDom('span', { className: 'version' }, version)), - - dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), - dom.alert = self.createDom('div', {className: 'alert'}), - dom.results = self.createDom('div', {className: 'results'}, - dom.summary = self.createDom('div', { className: 'summary' }), - dom.details = self.createDom('div', { id: 'details' })) - ); - } -}; -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) { - this.startedAt = new Date(); - this.runningSpecCount = 0; - this.completeSpecCount = 0; - this.passedCount = 0; - this.failedCount = 0; - this.skippedCount = 0; - - this.createResultsMenu = function() { - this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, - this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), - ' | ', - this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); - - this.summaryMenuItem.onclick = function() { - dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); - }; - - this.detailsMenuItem.onclick = function() { - showDetails(); - }; - }; - - this.addSpecs = function(specs, specFilter) { - this.totalSpecCount = specs.length; - - this.views = { - specs: {}, - suites: {} - }; - - for (var i = 0; i < specs.length; i++) { - var spec = specs[i]; - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); - if (specFilter(spec)) { - this.runningSpecCount++; - } - } - }; - - this.specComplete = function(spec) { - this.completeSpecCount++; - - if (isUndefined(this.views.specs[spec.id])) { - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); - } - - var specView = this.views.specs[spec.id]; - - switch (specView.status()) { - case 'passed': - this.passedCount++; - break; - - case 'failed': - this.failedCount++; - break; - - case 'skipped': - this.skippedCount++; - break; - } - - specView.refresh(); - this.refresh(); - }; - - this.suiteComplete = function(suite) { - var suiteView = this.views.suites[suite.id]; - if (isUndefined(suiteView)) { - return; - } - suiteView.refresh(); - }; - - this.refresh = function() { - - if (isUndefined(this.resultsMenu)) { - this.createResultsMenu(); - } - - // currently running UI - if (isUndefined(this.runningAlert)) { - this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); - dom.alert.appendChild(this.runningAlert); - } - this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); - - // skipped specs UI - if (isUndefined(this.skippedAlert)) { - this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); - } - - this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.skippedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.skippedAlert); - } - - // passing specs UI - if (isUndefined(this.passedAlert)) { - this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); - } - this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); - - // failing specs UI - if (isUndefined(this.failedAlert)) { - this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); - } - this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); - - if (this.failedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.failedAlert); - dom.alert.appendChild(this.resultsMenu); - } - - // summary info - this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); - this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; - }; - - this.complete = function() { - dom.alert.removeChild(this.runningAlert); - - this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.failedCount === 0) { - dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); - } else { - showDetails(); - } - - dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); - }; - - return this; - - function showDetails() { - if (dom.reporter.className.search(/showDetails/) === -1) { - dom.reporter.className += " showDetails"; - } - } - - function isUndefined(obj) { - return typeof obj === 'undefined'; - } - - function isDefined(obj) { - return !isUndefined(obj); - } - - function specPluralizedFor(count) { - var str = count + " spec"; - if (count > 1) { - str += "s" - } - return str; - } - -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); - - -jasmine.HtmlReporter.SpecView = function(spec, dom, views) { - this.spec = spec; - this.dom = dom; - this.views = views; - - this.symbol = this.createDom('li', { className: 'pending' }); - this.dom.symbolSummary.appendChild(this.symbol); - - this.summary = this.createDom('div', { className: 'specSummary' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.description) - ); - - this.detail = this.createDom('div', { className: 'specDetail' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.getFullName()) - ); -}; - -jasmine.HtmlReporter.SpecView.prototype.status = function() { - return this.getSpecStatus(this.spec); -}; - -jasmine.HtmlReporter.SpecView.prototype.refresh = function() { - this.symbol.className = this.status(); - - switch (this.status()) { - case 'skipped': - break; - - case 'passed': - this.appendSummaryToSuiteDiv(); - break; - - case 'failed': - this.appendSummaryToSuiteDiv(); - this.appendFailureDetail(); - break; - } -}; - -jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { - this.summary.className += ' ' + this.status(); - this.appendToSummary(this.spec, this.summary); -}; - -jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { - this.detail.className += ' ' + this.status(); - - var resultItems = this.spec.results().getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - this.detail.appendChild(messagesDiv); - this.dom.details.appendChild(this.detail); - } -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { - this.suite = suite; - this.dom = dom; - this.views = views; - - this.element = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) - ); - - this.appendToSummary(this.suite, this.element); -}; - -jasmine.HtmlReporter.SuiteView.prototype.status = function() { - return this.getSpecStatus(this.suite); -}; - -jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { - this.element.className += " " + this.status(); -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); - -/* @deprecated Use jasmine.HtmlReporter instead - */ -jasmine.TrivialReporter = function(doc) { - this.document = doc || document; - this.suiteDivs = {}; - this.logRunningSpecs = false; -}; - -jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { el.appendChild(child); } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { - var showPassed, showSkipped; - - this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, - this.createDom('div', { className: 'banner' }, - this.createDom('div', { className: 'logo' }, - this.createDom('span', { className: 'title' }, "Jasmine"), - this.createDom('span', { className: 'version' }, runner.env.versionString())), - this.createDom('div', { className: 'options' }, - "Show ", - showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), - showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") - ) - ), - - this.runnerDiv = this.createDom('div', { className: 'runner running' }, - this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), - this.runnerMessageSpan = this.createDom('span', {}, "Running..."), - this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) - ); - - this.document.body.appendChild(this.outerDiv); - - var suites = runner.suites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - var suiteDiv = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); - this.suiteDivs[suite.id] = suiteDiv; - var parentDiv = this.outerDiv; - if (suite.parentSuite) { - parentDiv = this.suiteDivs[suite.parentSuite.id]; - } - parentDiv.appendChild(suiteDiv); - } - - this.startedAt = new Date(); - - var self = this; - showPassed.onclick = function(evt) { - if (showPassed.checked) { - self.outerDiv.className += ' show-passed'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); - } - }; - - showSkipped.onclick = function(evt) { - if (showSkipped.checked) { - self.outerDiv.className += ' show-skipped'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); - } - }; -}; - -jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { - var results = runner.results(); - var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; - this.runnerDiv.setAttribute("class", className); - //do it twice for IE - this.runnerDiv.setAttribute("className", className); - var specs = runner.specs(); - var specCount = 0; - for (var i = 0; i < specs.length; i++) { - if (this.specFilter(specs[i])) { - specCount++; - } - } - var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); - message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; - this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); - - this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); -}; - -jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { - var results = suite.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.totalCount === 0) { // todo: change this to check results.skipped - status = 'skipped'; - } - this.suiteDivs[suite.id].className += " " + status; -}; - -jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { - if (this.logRunningSpecs) { - this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } -}; - -jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { - var results = spec.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - var specDiv = this.createDom('div', { className: 'spec ' + status }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(spec.getFullName()), - title: spec.getFullName() - }, spec.description)); - - - var resultItems = results.getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - specDiv.appendChild(messagesDiv); - } - - this.suiteDivs[spec.suite.id].appendChild(specDiv); -}; - -jasmine.TrivialReporter.prototype.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } -}; - -jasmine.TrivialReporter.prototype.getLocation = function() { - return this.document.location; -}; - -jasmine.TrivialReporter.prototype.specFilter = function(spec) { - var paramMap = {}; - var params = this.getLocation().search.substring(1).split('&'); - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - if (!paramMap.spec) { - return true; - } - return spec.getFullName().indexOf(paramMap.spec) === 0; -}; diff --git a/spec/lib/jasmine-1.2.0/jasmine.css b/spec/lib/jasmine-1.2.0/jasmine.css deleted file mode 100644 index 826e5753..00000000 --- a/spec/lib/jasmine-1.2.0/jasmine.css +++ /dev/null @@ -1,81 +0,0 @@ -body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } - -#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } -#HTMLReporter a { text-decoration: none; } -#HTMLReporter a:hover { text-decoration: underline; } -#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } -#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } -#HTMLReporter #jasmine_content { position: fixed; right: 100%; } -#HTMLReporter .version { color: #aaaaaa; } -#HTMLReporter .banner { margin-top: 14px; } -#HTMLReporter .duration { color: #aaaaaa; float: right; } -#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } -#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } -#HTMLReporter .symbolSummary li.passed { font-size: 14px; } -#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } -#HTMLReporter .symbolSummary li.failed { line-height: 9px; } -#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } -#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } -#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } -#HTMLReporter .symbolSummary li.pending { line-height: 11px; } -#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } -#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } -#HTMLReporter .runningAlert { background-color: #666666; } -#HTMLReporter .skippedAlert { background-color: #aaaaaa; } -#HTMLReporter .skippedAlert:first-child { background-color: #333333; } -#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } -#HTMLReporter .passingAlert { background-color: #a6b779; } -#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } -#HTMLReporter .failingAlert { background-color: #cf867e; } -#HTMLReporter .failingAlert:first-child { background-color: #b03911; } -#HTMLReporter .results { margin-top: 14px; } -#HTMLReporter #details { display: none; } -#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } -#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter.showDetails .summary { display: none; } -#HTMLReporter.showDetails #details { display: block; } -#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter .summary { margin-top: 14px; } -#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } -#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } -#HTMLReporter .summary .specSummary.failed a { color: #b03911; } -#HTMLReporter .description + .suite { margin-top: 0; } -#HTMLReporter .suite { margin-top: 14px; } -#HTMLReporter .suite a { color: #333333; } -#HTMLReporter #details .specDetail { margin-bottom: 28px; } -#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } -#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } -#HTMLReporter .resultMessage span.result { display: block; } -#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } - -#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } -#TrivialReporter a:visited, #TrivialReporter a { color: #303; } -#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } -#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } -#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } -#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } -#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } -#TrivialReporter .runner.running { background-color: yellow; } -#TrivialReporter .options { text-align: right; font-size: .8em; } -#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } -#TrivialReporter .suite .suite { margin: 5px; } -#TrivialReporter .suite.passed { background-color: #dfd; } -#TrivialReporter .suite.failed { background-color: #fdd; } -#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } -#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } -#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } -#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } -#TrivialReporter .spec.skipped { background-color: #bbb; } -#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } -#TrivialReporter .passed { background-color: #cfc; display: none; } -#TrivialReporter .failed { background-color: #fbb; } -#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } -#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } -#TrivialReporter .resultMessage .mismatch { color: black; } -#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } -#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } -#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } -#TrivialReporter #jasmine_content { position: fixed; right: 100%; } -#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/spec/lib/jasmine-1.2.0/jasmine.js b/spec/lib/jasmine-1.2.0/jasmine.js deleted file mode 100644 index 03bf89a0..00000000 --- a/spec/lib/jasmine-1.2.0/jasmine.js +++ /dev/null @@ -1,2529 +0,0 @@ -var isCommonJS = typeof window == "undefined"; - -/** - * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. - * - * @namespace - */ -var jasmine = {}; -if (isCommonJS) exports.jasmine = jasmine; -/** - * @private - */ -jasmine.unimplementedMethod_ = function() { - throw new Error("unimplemented method"); -}; - -/** - * Use jasmine.undefined instead of undefined, since undefined is just - * a plain old variable and may be redefined by somebody else. - * - * @private - */ -jasmine.undefined = jasmine.___undefined___; - -/** - * Show diagnostic messages in the console if set to true - * - */ -jasmine.VERBOSE = false; - -/** - * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. - * - */ -jasmine.DEFAULT_UPDATE_INTERVAL = 250; - -/** - * Default timeout interval in milliseconds for waitsFor() blocks. - */ -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; - -jasmine.getGlobal = function() { - function getGlobal() { - return this; - } - - return getGlobal(); -}; - -/** - * Allows for bound functions to be compared. Internal use only. - * - * @ignore - * @private - * @param base {Object} bound 'this' for the function - * @param name {Function} function to find - */ -jasmine.bindOriginal_ = function(base, name) { - var original = base[name]; - if (original.apply) { - return function() { - return original.apply(base, arguments); - }; - } else { - // IE support - return jasmine.getGlobal()[name]; - } -}; - -jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); -jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); -jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); -jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); - -jasmine.MessageResult = function(values) { - this.type = 'log'; - this.values = values; - this.trace = new Error(); // todo: test better -}; - -jasmine.MessageResult.prototype.toString = function() { - var text = ""; - for (var i = 0; i < this.values.length; i++) { - if (i > 0) text += " "; - if (jasmine.isString_(this.values[i])) { - text += this.values[i]; - } else { - text += jasmine.pp(this.values[i]); - } - } - return text; -}; - -jasmine.ExpectationResult = function(params) { - this.type = 'expect'; - this.matcherName = params.matcherName; - this.passed_ = params.passed; - this.expected = params.expected; - this.actual = params.actual; - this.message = this.passed_ ? 'Passed.' : params.message; - - var trace = (params.trace || new Error(this.message)); - this.trace = this.passed_ ? '' : trace; -}; - -jasmine.ExpectationResult.prototype.toString = function () { - return this.message; -}; - -jasmine.ExpectationResult.prototype.passed = function () { - return this.passed_; -}; - -/** - * Getter for the Jasmine environment. Ensures one gets created - */ -jasmine.getEnv = function() { - var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); - return env; -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isArray_ = function(value) { - return jasmine.isA_("Array", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isString_ = function(value) { - return jasmine.isA_("String", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isNumber_ = function(value) { - return jasmine.isA_("Number", value); -}; - -/** - * @ignore - * @private - * @param {String} typeName - * @param value - * @returns {Boolean} - */ -jasmine.isA_ = function(typeName, value) { - return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; -}; - -/** - * Pretty printer for expecations. Takes any object and turns it into a human-readable string. - * - * @param value {Object} an object to be outputted - * @returns {String} - */ -jasmine.pp = function(value) { - var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); - stringPrettyPrinter.format(value); - return stringPrettyPrinter.string; -}; - -/** - * Returns true if the object is a DOM Node. - * - * @param {Object} obj object to check - * @returns {Boolean} - */ -jasmine.isDomNode = function(obj) { - return obj.nodeType > 0; -}; - -/** - * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. - * - * @example - * // don't care about which function is passed in, as long as it's a function - * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); - * - * @param {Class} clazz - * @returns matchable object of the type clazz - */ -jasmine.any = function(clazz) { - return new jasmine.Matchers.Any(clazz); -}; - -/** - * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the - * attributes on the object. - * - * @example - * // don't care about any other attributes than foo. - * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); - * - * @param sample {Object} sample - * @returns matchable object for the sample - */ -jasmine.objectContaining = function (sample) { - return new jasmine.Matchers.ObjectContaining(sample); -}; - -/** - * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. - * - * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine - * expectation syntax. Spies can be checked if they were called or not and what the calling params were. - * - * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). - * - * Spies are torn down at the end of every spec. - * - * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. - * - * @example - * // a stub - * var myStub = jasmine.createSpy('myStub'); // can be used anywhere - * - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // actual foo.not will not be called, execution stops - * spyOn(foo, 'not'); - - // foo.not spied upon, execution will continue to implementation - * spyOn(foo, 'not').andCallThrough(); - * - * // fake example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // foo.not(val) will return val - * spyOn(foo, 'not').andCallFake(function(value) {return value;}); - * - * // mock example - * foo.not(7 == 7); - * expect(foo.not).toHaveBeenCalled(); - * expect(foo.not).toHaveBeenCalledWith(true); - * - * @constructor - * @see spyOn, jasmine.createSpy, jasmine.createSpyObj - * @param {String} name - */ -jasmine.Spy = function(name) { - /** - * The name of the spy, if provided. - */ - this.identity = name || 'unknown'; - /** - * Is this Object a spy? - */ - this.isSpy = true; - /** - * The actual function this spy stubs. - */ - this.plan = function() { - }; - /** - * Tracking of the most recent call to the spy. - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy.mostRecentCall.args = [1, 2]; - */ - this.mostRecentCall = {}; - - /** - * Holds arguments for each call to the spy, indexed by call count - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy(7, 8); - * mySpy.mostRecentCall.args = [7, 8]; - * mySpy.argsForCall[0] = [1, 2]; - * mySpy.argsForCall[1] = [7, 8]; - */ - this.argsForCall = []; - this.calls = []; -}; - -/** - * Tells a spy to call through to the actual implemenatation. - * - * @example - * var foo = { - * bar: function() { // do some stuff } - * } - * - * // defining a spy on an existing property: foo.bar - * spyOn(foo, 'bar').andCallThrough(); - */ -jasmine.Spy.prototype.andCallThrough = function() { - this.plan = this.originalValue; - return this; -}; - -/** - * For setting the return value of a spy. - * - * @example - * // defining a spy from scratch: foo() returns 'baz' - * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); - * - * // defining a spy on an existing property: foo.bar() returns 'baz' - * spyOn(foo, 'bar').andReturn('baz'); - * - * @param {Object} value - */ -jasmine.Spy.prototype.andReturn = function(value) { - this.plan = function() { - return value; - }; - return this; -}; - -/** - * For throwing an exception when a spy is called. - * - * @example - * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' - * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); - * - * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' - * spyOn(foo, 'bar').andThrow('baz'); - * - * @param {String} exceptionMsg - */ -jasmine.Spy.prototype.andThrow = function(exceptionMsg) { - this.plan = function() { - throw exceptionMsg; - }; - return this; -}; - -/** - * Calls an alternate implementation when a spy is called. - * - * @example - * var baz = function() { - * // do some stuff, return something - * } - * // defining a spy from scratch: foo() calls the function baz - * var foo = jasmine.createSpy('spy on foo').andCall(baz); - * - * // defining a spy on an existing property: foo.bar() calls an anonymnous function - * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); - * - * @param {Function} fakeFunc - */ -jasmine.Spy.prototype.andCallFake = function(fakeFunc) { - this.plan = fakeFunc; - return this; -}; - -/** - * Resets all of a spy's the tracking variables so that it can be used again. - * - * @example - * spyOn(foo, 'bar'); - * - * foo.bar(); - * - * expect(foo.bar.callCount).toEqual(1); - * - * foo.bar.reset(); - * - * expect(foo.bar.callCount).toEqual(0); - */ -jasmine.Spy.prototype.reset = function() { - this.wasCalled = false; - this.callCount = 0; - this.argsForCall = []; - this.calls = []; - this.mostRecentCall = {}; -}; - -jasmine.createSpy = function(name) { - - var spyObj = function() { - spyObj.wasCalled = true; - spyObj.callCount++; - var args = jasmine.util.argsToArray(arguments); - spyObj.mostRecentCall.object = this; - spyObj.mostRecentCall.args = args; - spyObj.argsForCall.push(args); - spyObj.calls.push({object: this, args: args}); - return spyObj.plan.apply(this, arguments); - }; - - var spy = new jasmine.Spy(name); - - for (var prop in spy) { - spyObj[prop] = spy[prop]; - } - - spyObj.reset(); - - return spyObj; -}; - -/** - * Determines whether an object is a spy. - * - * @param {jasmine.Spy|Object} putativeSpy - * @returns {Boolean} - */ -jasmine.isSpy = function(putativeSpy) { - return putativeSpy && putativeSpy.isSpy; -}; - -/** - * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something - * large in one call. - * - * @param {String} baseName name of spy class - * @param {Array} methodNames array of names of methods to make spies - */ -jasmine.createSpyObj = function(baseName, methodNames) { - if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { - throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); - } - var obj = {}; - for (var i = 0; i < methodNames.length; i++) { - obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); - } - return obj; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the current spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.log = function() { - var spec = jasmine.getEnv().currentSpec; - spec.log.apply(spec, arguments); -}; - -/** - * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. - * - * @example - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops - * - * @see jasmine.createSpy - * @param obj - * @param methodName - * @returns a Jasmine spy that can be chained with all spy methods - */ -var spyOn = function(obj, methodName) { - return jasmine.getEnv().currentSpec.spyOn(obj, methodName); -}; -if (isCommonJS) exports.spyOn = spyOn; - -/** - * Creates a Jasmine spec that will be added to the current suite. - * - * // TODO: pending tests - * - * @example - * it('should be true', function() { - * expect(true).toEqual(true); - * }); - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var it = function(desc, func) { - return jasmine.getEnv().it(desc, func); -}; -if (isCommonJS) exports.it = it; - -/** - * Creates a disabled Jasmine spec. - * - * A convenience method that allows existing specs to be disabled temporarily during development. - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var xit = function(desc, func) { - return jasmine.getEnv().xit(desc, func); -}; -if (isCommonJS) exports.xit = xit; - -/** - * Starts a chain for a Jasmine expectation. - * - * It is passed an Object that is the actual value and should chain to one of the many - * jasmine.Matchers functions. - * - * @param {Object} actual Actual value to test against and expected value - */ -var expect = function(actual) { - return jasmine.getEnv().currentSpec.expect(actual); -}; -if (isCommonJS) exports.expect = expect; - -/** - * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. - * - * @param {Function} func Function that defines part of a jasmine spec. - */ -var runs = function(func) { - jasmine.getEnv().currentSpec.runs(func); -}; -if (isCommonJS) exports.runs = runs; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -var waits = function(timeout) { - jasmine.getEnv().currentSpec.waits(timeout); -}; -if (isCommonJS) exports.waits = waits; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); -}; -if (isCommonJS) exports.waitsFor = waitsFor; - -/** - * A function that is called before each spec in a suite. - * - * Used for spec setup, including validating assumptions. - * - * @param {Function} beforeEachFunction - */ -var beforeEach = function(beforeEachFunction) { - jasmine.getEnv().beforeEach(beforeEachFunction); -}; -if (isCommonJS) exports.beforeEach = beforeEach; - -/** - * A function that is called after each spec in a suite. - * - * Used for restoring any state that is hijacked during spec execution. - * - * @param {Function} afterEachFunction - */ -var afterEach = function(afterEachFunction) { - jasmine.getEnv().afterEach(afterEachFunction); -}; -if (isCommonJS) exports.afterEach = afterEach; - -/** - * Defines a suite of specifications. - * - * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared - * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization - * of setup in some tests. - * - * @example - * // TODO: a simple suite - * - * // TODO: a simple suite with a nested describe block - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var describe = function(description, specDefinitions) { - return jasmine.getEnv().describe(description, specDefinitions); -}; -if (isCommonJS) exports.describe = describe; - -/** - * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var xdescribe = function(description, specDefinitions) { - return jasmine.getEnv().xdescribe(description, specDefinitions); -}; -if (isCommonJS) exports.xdescribe = xdescribe; - - -// Provide the XMLHttpRequest class for IE 5.x-6.x: -jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { - function tryIt(f) { - try { - return f(); - } catch(e) { - } - return null; - } - - var xhr = tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.6.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.3.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP"); - }) || - tryIt(function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }); - - if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); - - return xhr; -} : XMLHttpRequest; -/** - * @namespace - */ -jasmine.util = {}; - -/** - * Declare that a child class inherit it's prototype from the parent class. - * - * @private - * @param {Function} childClass - * @param {Function} parentClass - */ -jasmine.util.inherit = function(childClass, parentClass) { - /** - * @private - */ - var subclass = function() { - }; - subclass.prototype = parentClass.prototype; - childClass.prototype = new subclass(); -}; - -jasmine.util.formatException = function(e) { - var lineNumber; - if (e.line) { - lineNumber = e.line; - } - else if (e.lineNumber) { - lineNumber = e.lineNumber; - } - - var file; - - if (e.sourceURL) { - file = e.sourceURL; - } - else if (e.fileName) { - file = e.fileName; - } - - var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); - - if (file && lineNumber) { - message += ' in ' + file + ' (line ' + lineNumber + ')'; - } - - return message; -}; - -jasmine.util.htmlEscape = function(str) { - if (!str) return str; - return str.replace(/&/g, '&') - .replace(//g, '>'); -}; - -jasmine.util.argsToArray = function(args) { - var arrayOfArgs = []; - for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); - return arrayOfArgs; -}; - -jasmine.util.extend = function(destination, source) { - for (var property in source) destination[property] = source[property]; - return destination; -}; - -/** - * Environment for Jasmine - * - * @constructor - */ -jasmine.Env = function() { - this.currentSpec = null; - this.currentSuite = null; - this.currentRunner_ = new jasmine.Runner(this); - - this.reporter = new jasmine.MultiReporter(); - - this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; - this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; - this.lastUpdate = 0; - this.specFilter = function() { - return true; - }; - - this.nextSpecId_ = 0; - this.nextSuiteId_ = 0; - this.equalityTesters_ = []; - - // wrap matchers - this.matchersClass = function() { - jasmine.Matchers.apply(this, arguments); - }; - jasmine.util.inherit(this.matchersClass, jasmine.Matchers); - - jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); -}; - - -jasmine.Env.prototype.setTimeout = jasmine.setTimeout; -jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; -jasmine.Env.prototype.setInterval = jasmine.setInterval; -jasmine.Env.prototype.clearInterval = jasmine.clearInterval; - -/** - * @returns an object containing jasmine version build info, if set. - */ -jasmine.Env.prototype.version = function () { - if (jasmine.version_) { - return jasmine.version_; - } else { - throw new Error('Version not set'); - } -}; - -/** - * @returns string containing jasmine version build info, if set. - */ -jasmine.Env.prototype.versionString = function() { - if (!jasmine.version_) { - return "version unknown"; - } - - var version = this.version(); - var versionString = version.major + "." + version.minor + "." + version.build; - if (version.release_candidate) { - versionString += ".rc" + version.release_candidate; - } - versionString += " revision " + version.revision; - return versionString; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSpecId = function () { - return this.nextSpecId_++; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSuiteId = function () { - return this.nextSuiteId_++; -}; - -/** - * Register a reporter to receive status updates from Jasmine. - * @param {jasmine.Reporter} reporter An object which will receive status updates. - */ -jasmine.Env.prototype.addReporter = function(reporter) { - this.reporter.addReporter(reporter); -}; - -jasmine.Env.prototype.execute = function() { - this.currentRunner_.execute(); -}; - -jasmine.Env.prototype.describe = function(description, specDefinitions) { - var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); - - var parentSuite = this.currentSuite; - if (parentSuite) { - parentSuite.add(suite); - } else { - this.currentRunner_.add(suite); - } - - this.currentSuite = suite; - - var declarationError = null; - try { - specDefinitions.call(suite); - } catch(e) { - declarationError = e; - } - - if (declarationError) { - this.it("encountered a declaration exception", function() { - throw declarationError; - }); - } - - this.currentSuite = parentSuite; - - return suite; -}; - -jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { - if (this.currentSuite) { - this.currentSuite.beforeEach(beforeEachFunction); - } else { - this.currentRunner_.beforeEach(beforeEachFunction); - } -}; - -jasmine.Env.prototype.currentRunner = function () { - return this.currentRunner_; -}; - -jasmine.Env.prototype.afterEach = function(afterEachFunction) { - if (this.currentSuite) { - this.currentSuite.afterEach(afterEachFunction); - } else { - this.currentRunner_.afterEach(afterEachFunction); - } - -}; - -jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { - return { - execute: function() { - } - }; -}; - -jasmine.Env.prototype.it = function(description, func) { - var spec = new jasmine.Spec(this, this.currentSuite, description); - this.currentSuite.add(spec); - this.currentSpec = spec; - - if (func) { - spec.runs(func); - } - - return spec; -}; - -jasmine.Env.prototype.xit = function(desc, func) { - return { - id: this.nextSpecId(), - runs: function() { - } - }; -}; - -jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { - return true; - } - - a.__Jasmine_been_here_before__ = b; - b.__Jasmine_been_here_before__ = a; - - var hasKey = function(obj, keyName) { - return obj !== null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in b) { - if (!hasKey(a, property) && hasKey(b, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - } - for (property in a) { - if (!hasKey(b, property) && hasKey(a, property)) { - mismatchKeys.push("expected missing key '" + property + "', but present in actual."); - } - } - for (property in b) { - if (property == '__Jasmine_been_here_before__') continue; - if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); - } - } - - if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { - mismatchValues.push("arrays were not the same length"); - } - - delete a.__Jasmine_been_here_before__; - delete b.__Jasmine_been_here_before__; - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - for (var i = 0; i < this.equalityTesters_.length; i++) { - var equalityTester = this.equalityTesters_[i]; - var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); - if (result !== jasmine.undefined) return result; - } - - if (a === b) return true; - - if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { - return (a == jasmine.undefined && b == jasmine.undefined); - } - - if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { - return a === b; - } - - if (a instanceof Date && b instanceof Date) { - return a.getTime() == b.getTime(); - } - - if (a.jasmineMatches) { - return a.jasmineMatches(b); - } - - if (b.jasmineMatches) { - return b.jasmineMatches(a); - } - - if (a instanceof jasmine.Matchers.ObjectContaining) { - return a.matches(b); - } - - if (b instanceof jasmine.Matchers.ObjectContaining) { - return b.matches(a); - } - - if (jasmine.isString_(a) && jasmine.isString_(b)) { - return (a == b); - } - - if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { - return (a == b); - } - - if (typeof a === "object" && typeof b === "object") { - return this.compareObjects_(a, b, mismatchKeys, mismatchValues); - } - - //Straight check - return (a === b); -}; - -jasmine.Env.prototype.contains_ = function(haystack, needle) { - if (jasmine.isArray_(haystack)) { - for (var i = 0; i < haystack.length; i++) { - if (this.equals_(haystack[i], needle)) return true; - } - return false; - } - return haystack.indexOf(needle) >= 0; -}; - -jasmine.Env.prototype.addEqualityTester = function(equalityTester) { - this.equalityTesters_.push(equalityTester); -}; -/** No-op base class for Jasmine reporters. - * - * @constructor - */ -jasmine.Reporter = function() { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerResults = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecStarting = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecResults = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.log = function(str) { -}; - -/** - * Blocks are functions with executable code that make up a spec. - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {jasmine.Spec} spec - */ -jasmine.Block = function(env, func, spec) { - this.env = env; - this.func = func; - this.spec = spec; -}; - -jasmine.Block.prototype.execute = function(onComplete) { - try { - this.func.apply(this.spec); - } catch (e) { - this.spec.fail(e); - } - onComplete(); -}; -/** JavaScript API reporter. - * - * @constructor - */ -jasmine.JsApiReporter = function() { - this.started = false; - this.finished = false; - this.suites_ = []; - this.results_ = {}; -}; - -jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { - this.started = true; - var suites = runner.topLevelSuites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - this.suites_.push(this.summarize_(suite)); - } -}; - -jasmine.JsApiReporter.prototype.suites = function() { - return this.suites_; -}; - -jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { - var isSuite = suiteOrSpec instanceof jasmine.Suite; - var summary = { - id: suiteOrSpec.id, - name: suiteOrSpec.description, - type: isSuite ? 'suite' : 'spec', - children: [] - }; - - if (isSuite) { - var children = suiteOrSpec.children(); - for (var i = 0; i < children.length; i++) { - summary.children.push(this.summarize_(children[i])); - } - } - return summary; -}; - -jasmine.JsApiReporter.prototype.results = function() { - return this.results_; -}; - -jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { - return this.results_[specId]; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { - this.finished = true; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { - this.results_[spec.id] = { - messages: spec.results().getItems(), - result: spec.results().failedCount > 0 ? "failed" : "passed" - }; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.log = function(str) { -}; - -jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ - var results = {}; - for (var i = 0; i < specIds.length; i++) { - var specId = specIds[i]; - results[specId] = this.summarizeResult_(this.results_[specId]); - } - return results; -}; - -jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ - var summaryMessages = []; - var messagesLength = result.messages.length; - for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { - var resultMessage = result.messages[messageIndex]; - summaryMessages.push({ - text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, - passed: resultMessage.passed ? resultMessage.passed() : true, - type: resultMessage.type, - message: resultMessage.message, - trace: { - stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined - } - }); - } - - return { - result : result.result, - messages : summaryMessages - }; -}; - -/** - * @constructor - * @param {jasmine.Env} env - * @param actual - * @param {jasmine.Spec} spec - */ -jasmine.Matchers = function(env, actual, spec, opt_isNot) { - this.env = env; - this.actual = actual; - this.spec = spec; - this.isNot = opt_isNot || false; - this.reportWasCalled_ = false; -}; - -// todo: @deprecated as of Jasmine 0.11, remove soon [xw] -jasmine.Matchers.pp = function(str) { - throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); -}; - -// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] -jasmine.Matchers.prototype.report = function(result, failing_message, details) { - throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); -}; - -jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { - for (var methodName in prototype) { - if (methodName == 'report') continue; - var orig = prototype[methodName]; - matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); - } -}; - -jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { - return function() { - var matcherArgs = jasmine.util.argsToArray(arguments); - var result = matcherFunction.apply(this, arguments); - - if (this.isNot) { - result = !result; - } - - if (this.reportWasCalled_) return result; - - var message; - if (!result) { - if (this.message) { - message = this.message.apply(this, arguments); - if (jasmine.isArray_(message)) { - message = message[this.isNot ? 1 : 0]; - } - } else { - var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); - message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; - if (matcherArgs.length > 0) { - for (var i = 0; i < matcherArgs.length; i++) { - if (i > 0) message += ","; - message += " " + jasmine.pp(matcherArgs[i]); - } - } - message += "."; - } - } - var expectationResult = new jasmine.ExpectationResult({ - matcherName: matcherName, - passed: result, - expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], - actual: this.actual, - message: message - }); - this.spec.addMatcherResult(expectationResult); - return jasmine.undefined; - }; -}; - - - - -/** - * toBe: compares the actual to the expected using === - * @param expected - */ -jasmine.Matchers.prototype.toBe = function(expected) { - return this.actual === expected; -}; - -/** - * toNotBe: compares the actual to the expected using !== - * @param expected - * @deprecated as of 1.0. Use not.toBe() instead. - */ -jasmine.Matchers.prototype.toNotBe = function(expected) { - return this.actual !== expected; -}; - -/** - * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. - * - * @param expected - */ -jasmine.Matchers.prototype.toEqual = function(expected) { - return this.env.equals_(this.actual, expected); -}; - -/** - * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual - * @param expected - * @deprecated as of 1.0. Use not.toEqual() instead. - */ -jasmine.Matchers.prototype.toNotEqual = function(expected) { - return !this.env.equals_(this.actual, expected); -}; - -/** - * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes - * a pattern or a String. - * - * @param expected - */ -jasmine.Matchers.prototype.toMatch = function(expected) { - return new RegExp(expected).test(this.actual); -}; - -/** - * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch - * @param expected - * @deprecated as of 1.0. Use not.toMatch() instead. - */ -jasmine.Matchers.prototype.toNotMatch = function(expected) { - return !(new RegExp(expected).test(this.actual)); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeDefined = function() { - return (this.actual !== jasmine.undefined); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeUndefined = function() { - return (this.actual === jasmine.undefined); -}; - -/** - * Matcher that compares the actual to null. - */ -jasmine.Matchers.prototype.toBeNull = function() { - return (this.actual === null); -}; - -/** - * Matcher that boolean not-nots the actual. - */ -jasmine.Matchers.prototype.toBeTruthy = function() { - return !!this.actual; -}; - - -/** - * Matcher that boolean nots the actual. - */ -jasmine.Matchers.prototype.toBeFalsy = function() { - return !this.actual; -}; - - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called. - */ -jasmine.Matchers.prototype.toHaveBeenCalled = function() { - if (arguments.length > 0) { - throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to have been called.", - "Expected spy " + this.actual.identity + " not to have been called." - ]; - }; - - return this.actual.wasCalled; -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ -jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was not called. - * - * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead - */ -jasmine.Matchers.prototype.wasNotCalled = function() { - if (arguments.length > 0) { - throw new Error('wasNotCalled does not take arguments'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to not have been called.", - "Expected spy " + this.actual.identity + " to have been called." - ]; - }; - - return !this.actual.wasCalled; -}; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. - * - * @example - * - */ -jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - this.message = function() { - if (this.actual.callCount === 0) { - // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] - return [ - "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", - "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." - ]; - } else { - return [ - "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), - "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) - ]; - } - }; - - return this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; - -/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasNotCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", - "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" - ]; - }; - - return !this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** - * Matcher that checks that the expected item is an element in the actual Array. - * - * @param {Object} expected - */ -jasmine.Matchers.prototype.toContain = function(expected) { - return this.env.contains_(this.actual, expected); -}; - -/** - * Matcher that checks that the expected item is NOT an element in the actual Array. - * - * @param {Object} expected - * @deprecated as of 1.0. Use not.toContain() instead. - */ -jasmine.Matchers.prototype.toNotContain = function(expected) { - return !this.env.contains_(this.actual, expected); -}; - -jasmine.Matchers.prototype.toBeLessThan = function(expected) { - return this.actual < expected; -}; - -jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { - return this.actual > expected; -}; - -/** - * Matcher that checks that the expected item is equal to the actual item - * up to a given level of decimal precision (default 2). - * - * @param {Number} expected - * @param {Number} precision - */ -jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { - if (!(precision === 0)) { - precision = precision || 2; - } - var multiplier = Math.pow(10, precision); - var actual = Math.round(this.actual * multiplier); - expected = Math.round(expected * multiplier); - return expected == actual; -}; - -/** - * Matcher that checks that the expected exception was thrown by the actual. - * - * @param {String} expected - */ -jasmine.Matchers.prototype.toThrow = function(expected) { - var result = false; - var exception; - if (typeof this.actual != 'function') { - throw new Error('Actual is not a function'); - } - try { - this.actual(); - } catch (e) { - exception = e; - } - if (exception) { - result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); - } - - var not = this.isNot ? "not " : ""; - - this.message = function() { - if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { - return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); - } else { - return "Expected function to throw an exception."; - } - }; - - return result; -}; - -jasmine.Matchers.Any = function(expectedClass) { - this.expectedClass = expectedClass; -}; - -jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { - if (this.expectedClass == String) { - return typeof other == 'string' || other instanceof String; - } - - if (this.expectedClass == Number) { - return typeof other == 'number' || other instanceof Number; - } - - if (this.expectedClass == Function) { - return typeof other == 'function' || other instanceof Function; - } - - if (this.expectedClass == Object) { - return typeof other == 'object'; - } - - return other instanceof this.expectedClass; -}; - -jasmine.Matchers.Any.prototype.jasmineToString = function() { - return ''; -}; - -jasmine.Matchers.ObjectContaining = function (sample) { - this.sample = sample; -}; - -jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - var env = jasmine.getEnv(); - - var hasKey = function(obj, keyName) { - return obj != null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in this.sample) { - if (!hasKey(other, property) && hasKey(this.sample, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); - } - } - - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { - return ""; -}; -// Mock setTimeout, clearTimeout -// Contributed by Pivotal Computer Systems, www.pivotalsf.com - -jasmine.FakeTimer = function() { - this.reset(); - - var self = this; - self.setTimeout = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); - return self.timeoutsMade; - }; - - self.setInterval = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); - return self.timeoutsMade; - }; - - self.clearTimeout = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - - self.clearInterval = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - -}; - -jasmine.FakeTimer.prototype.reset = function() { - this.timeoutsMade = 0; - this.scheduledFunctions = {}; - this.nowMillis = 0; -}; - -jasmine.FakeTimer.prototype.tick = function(millis) { - var oldMillis = this.nowMillis; - var newMillis = oldMillis + millis; - this.runFunctionsWithinRange(oldMillis, newMillis); - this.nowMillis = newMillis; -}; - -jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { - var scheduledFunc; - var funcsToRun = []; - for (var timeoutKey in this.scheduledFunctions) { - scheduledFunc = this.scheduledFunctions[timeoutKey]; - if (scheduledFunc != jasmine.undefined && - scheduledFunc.runAtMillis >= oldMillis && - scheduledFunc.runAtMillis <= nowMillis) { - funcsToRun.push(scheduledFunc); - this.scheduledFunctions[timeoutKey] = jasmine.undefined; - } - } - - if (funcsToRun.length > 0) { - funcsToRun.sort(function(a, b) { - return a.runAtMillis - b.runAtMillis; - }); - for (var i = 0; i < funcsToRun.length; ++i) { - try { - var funcToRun = funcsToRun[i]; - this.nowMillis = funcToRun.runAtMillis; - funcToRun.funcToCall(); - if (funcToRun.recurring) { - this.scheduleFunction(funcToRun.timeoutKey, - funcToRun.funcToCall, - funcToRun.millis, - true); - } - } catch(e) { - } - } - this.runFunctionsWithinRange(oldMillis, nowMillis); - } -}; - -jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { - this.scheduledFunctions[timeoutKey] = { - runAtMillis: this.nowMillis + millis, - funcToCall: funcToCall, - recurring: recurring, - timeoutKey: timeoutKey, - millis: millis - }; -}; - -/** - * @namespace - */ -jasmine.Clock = { - defaultFakeTimer: new jasmine.FakeTimer(), - - reset: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.reset(); - }, - - tick: function(millis) { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.tick(millis); - }, - - runFunctionsWithinRange: function(oldMillis, nowMillis) { - jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); - }, - - scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { - jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); - }, - - useMock: function() { - if (!jasmine.Clock.isInstalled()) { - var spec = jasmine.getEnv().currentSpec; - spec.after(jasmine.Clock.uninstallMock); - - jasmine.Clock.installMock(); - } - }, - - installMock: function() { - jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; - }, - - uninstallMock: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.installed = jasmine.Clock.real; - }, - - real: { - setTimeout: jasmine.getGlobal().setTimeout, - clearTimeout: jasmine.getGlobal().clearTimeout, - setInterval: jasmine.getGlobal().setInterval, - clearInterval: jasmine.getGlobal().clearInterval - }, - - assertInstalled: function() { - if (!jasmine.Clock.isInstalled()) { - throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); - } - }, - - isInstalled: function() { - return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; - }, - - installed: null -}; -jasmine.Clock.installed = jasmine.Clock.real; - -//else for IE support -jasmine.getGlobal().setTimeout = function(funcToCall, millis) { - if (jasmine.Clock.installed.setTimeout.apply) { - return jasmine.Clock.installed.setTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.setTimeout(funcToCall, millis); - } -}; - -jasmine.getGlobal().setInterval = function(funcToCall, millis) { - if (jasmine.Clock.installed.setInterval.apply) { - return jasmine.Clock.installed.setInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.setInterval(funcToCall, millis); - } -}; - -jasmine.getGlobal().clearTimeout = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearTimeout(timeoutKey); - } -}; - -jasmine.getGlobal().clearInterval = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearInterval(timeoutKey); - } -}; - -/** - * @constructor - */ -jasmine.MultiReporter = function() { - this.subReporters_ = []; -}; -jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); - -jasmine.MultiReporter.prototype.addReporter = function(reporter) { - this.subReporters_.push(reporter); -}; - -(function() { - var functionNames = [ - "reportRunnerStarting", - "reportRunnerResults", - "reportSuiteResults", - "reportSpecStarting", - "reportSpecResults", - "log" - ]; - for (var i = 0; i < functionNames.length; i++) { - var functionName = functionNames[i]; - jasmine.MultiReporter.prototype[functionName] = (function(functionName) { - return function() { - for (var j = 0; j < this.subReporters_.length; j++) { - var subReporter = this.subReporters_[j]; - if (subReporter[functionName]) { - subReporter[functionName].apply(subReporter, arguments); - } - } - }; - })(functionName); - } -})(); -/** - * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults - * - * @constructor - */ -jasmine.NestedResults = function() { - /** - * The total count of results - */ - this.totalCount = 0; - /** - * Number of passed results - */ - this.passedCount = 0; - /** - * Number of failed results - */ - this.failedCount = 0; - /** - * Was this suite/spec skipped? - */ - this.skipped = false; - /** - * @ignore - */ - this.items_ = []; -}; - -/** - * Roll up the result counts. - * - * @param result - */ -jasmine.NestedResults.prototype.rollupCounts = function(result) { - this.totalCount += result.totalCount; - this.passedCount += result.passedCount; - this.failedCount += result.failedCount; -}; - -/** - * Adds a log message. - * @param values Array of message parts which will be concatenated later. - */ -jasmine.NestedResults.prototype.log = function(values) { - this.items_.push(new jasmine.MessageResult(values)); -}; - -/** - * Getter for the results: message & results. - */ -jasmine.NestedResults.prototype.getItems = function() { - return this.items_; -}; - -/** - * Adds a result, tracking counts (total, passed, & failed) - * @param {jasmine.ExpectationResult|jasmine.NestedResults} result - */ -jasmine.NestedResults.prototype.addResult = function(result) { - if (result.type != 'log') { - if (result.items_) { - this.rollupCounts(result); - } else { - this.totalCount++; - if (result.passed()) { - this.passedCount++; - } else { - this.failedCount++; - } - } - } - this.items_.push(result); -}; - -/** - * @returns {Boolean} True if everything below passed - */ -jasmine.NestedResults.prototype.passed = function() { - return this.passedCount === this.totalCount; -}; -/** - * Base class for pretty printing for expectation results. - */ -jasmine.PrettyPrinter = function() { - this.ppNestLevel_ = 0; -}; - -/** - * Formats a value in a nice, human-readable string. - * - * @param value - */ -jasmine.PrettyPrinter.prototype.format = function(value) { - if (this.ppNestLevel_ > 40) { - throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); - } - - this.ppNestLevel_++; - try { - if (value === jasmine.undefined) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === jasmine.getGlobal()) { - this.emitScalar(''); - } else if (value.jasmineToString) { - this.emitScalar(value.jasmineToString()); - } else if (typeof value === 'string') { - this.emitString(value); - } else if (jasmine.isSpy(value)) { - this.emitScalar("spy on " + value.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (typeof value.nodeType === 'number') { - this.emitScalar('HTMLNode'); - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (value.__Jasmine_been_here_before__) { - this.emitScalar(''); - } else if (jasmine.isArray_(value) || typeof value == 'object') { - value.__Jasmine_been_here_before__ = true; - if (jasmine.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - delete value.__Jasmine_been_here_before__; - } else { - this.emitScalar(value.toString()); - } - } finally { - this.ppNestLevel_--; - } -}; - -jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { - for (var property in obj) { - if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && - obj.__lookupGetter__(property) !== null) : false); - } -}; - -jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; - -jasmine.StringPrettyPrinter = function() { - jasmine.PrettyPrinter.call(this); - - this.string = ''; -}; -jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); - -jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { - this.append(value); -}; - -jasmine.StringPrettyPrinter.prototype.emitString = function(value) { - this.append("'" + value + "'"); -}; - -jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { - this.append('[ '); - for (var i = 0; i < array.length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - this.append(' ]'); -}; - -jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { - var self = this; - this.append('{ '); - var first = true; - - this.iterateObject(obj, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.append(property); - self.append(' : '); - if (isGetter) { - self.append(''); - } else { - self.format(obj[property]); - } - }); - - this.append(' }'); -}; - -jasmine.StringPrettyPrinter.prototype.append = function(value) { - this.string += value; -}; -jasmine.Queue = function(env) { - this.env = env; - this.blocks = []; - this.running = false; - this.index = 0; - this.offset = 0; - this.abort = false; -}; - -jasmine.Queue.prototype.addBefore = function(block) { - this.blocks.unshift(block); -}; - -jasmine.Queue.prototype.add = function(block) { - this.blocks.push(block); -}; - -jasmine.Queue.prototype.insertNext = function(block) { - this.blocks.splice((this.index + this.offset + 1), 0, block); - this.offset++; -}; - -jasmine.Queue.prototype.start = function(onComplete) { - this.running = true; - this.onComplete = onComplete; - this.next_(); -}; - -jasmine.Queue.prototype.isRunning = function() { - return this.running; -}; - -jasmine.Queue.LOOP_DONT_RECURSE = true; - -jasmine.Queue.prototype.next_ = function() { - var self = this; - var goAgain = true; - - while (goAgain) { - goAgain = false; - - if (self.index < self.blocks.length && !this.abort) { - var calledSynchronously = true; - var completedSynchronously = false; - - var onComplete = function () { - if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { - completedSynchronously = true; - return; - } - - if (self.blocks[self.index].abort) { - self.abort = true; - } - - self.offset = 0; - self.index++; - - var now = new Date().getTime(); - if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { - self.env.lastUpdate = now; - self.env.setTimeout(function() { - self.next_(); - }, 0); - } else { - if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { - goAgain = true; - } else { - self.next_(); - } - } - }; - self.blocks[self.index].execute(onComplete); - - calledSynchronously = false; - if (completedSynchronously) { - onComplete(); - } - - } else { - self.running = false; - if (self.onComplete) { - self.onComplete(); - } - } - } -}; - -jasmine.Queue.prototype.results = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.blocks.length; i++) { - if (this.blocks[i].results) { - results.addResult(this.blocks[i].results()); - } - } - return results; -}; - - -/** - * Runner - * - * @constructor - * @param {jasmine.Env} env - */ -jasmine.Runner = function(env) { - var self = this; - self.env = env; - self.queue = new jasmine.Queue(env); - self.before_ = []; - self.after_ = []; - self.suites_ = []; -}; - -jasmine.Runner.prototype.execute = function() { - var self = this; - if (self.env.reporter.reportRunnerStarting) { - self.env.reporter.reportRunnerStarting(this); - } - self.queue.start(function () { - self.finishCallback(); - }); -}; - -jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.splice(0,0,beforeEachFunction); -}; - -jasmine.Runner.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.splice(0,0,afterEachFunction); -}; - - -jasmine.Runner.prototype.finishCallback = function() { - this.env.reporter.reportRunnerResults(this); -}; - -jasmine.Runner.prototype.addSuite = function(suite) { - this.suites_.push(suite); -}; - -jasmine.Runner.prototype.add = function(block) { - if (block instanceof jasmine.Suite) { - this.addSuite(block); - } - this.queue.add(block); -}; - -jasmine.Runner.prototype.specs = function () { - var suites = this.suites(); - var specs = []; - for (var i = 0; i < suites.length; i++) { - specs = specs.concat(suites[i].specs()); - } - return specs; -}; - -jasmine.Runner.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Runner.prototype.topLevelSuites = function() { - var topLevelSuites = []; - for (var i = 0; i < this.suites_.length; i++) { - if (!this.suites_[i].parentSuite) { - topLevelSuites.push(this.suites_[i]); - } - } - return topLevelSuites; -}; - -jasmine.Runner.prototype.results = function() { - return this.queue.results(); -}; -/** - * Internal representation of a Jasmine specification, or test. - * - * @constructor - * @param {jasmine.Env} env - * @param {jasmine.Suite} suite - * @param {String} description - */ -jasmine.Spec = function(env, suite, description) { - if (!env) { - throw new Error('jasmine.Env() required'); - } - if (!suite) { - throw new Error('jasmine.Suite() required'); - } - var spec = this; - spec.id = env.nextSpecId ? env.nextSpecId() : null; - spec.env = env; - spec.suite = suite; - spec.description = description; - spec.queue = new jasmine.Queue(env); - - spec.afterCallbacks = []; - spec.spies_ = []; - - spec.results_ = new jasmine.NestedResults(); - spec.results_.description = description; - spec.matchersClass = null; -}; - -jasmine.Spec.prototype.getFullName = function() { - return this.suite.getFullName() + ' ' + this.description + '.'; -}; - - -jasmine.Spec.prototype.results = function() { - return this.results_; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.Spec.prototype.log = function() { - return this.results_.log(arguments); -}; - -jasmine.Spec.prototype.runs = function (func) { - var block = new jasmine.Block(this.env, func, this); - this.addToQueue(block); - return this; -}; - -jasmine.Spec.prototype.addToQueue = function (block) { - if (this.queue.isRunning()) { - this.queue.insertNext(block); - } else { - this.queue.add(block); - } -}; - -/** - * @param {jasmine.ExpectationResult} result - */ -jasmine.Spec.prototype.addMatcherResult = function(result) { - this.results_.addResult(result); -}; - -jasmine.Spec.prototype.expect = function(actual) { - var positive = new (this.getMatchersClass_())(this.env, actual, this); - positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); - return positive; -}; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -jasmine.Spec.prototype.waits = function(timeout) { - var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); - this.addToQueue(waitsFunc); - return this; -}; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - var latchFunction_ = null; - var optional_timeoutMessage_ = null; - var optional_timeout_ = null; - - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i]; - switch (typeof arg) { - case 'function': - latchFunction_ = arg; - break; - case 'string': - optional_timeoutMessage_ = arg; - break; - case 'number': - optional_timeout_ = arg; - break; - } - } - - var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); - this.addToQueue(waitsForFunc); - return this; -}; - -jasmine.Spec.prototype.fail = function (e) { - var expectationResult = new jasmine.ExpectationResult({ - passed: false, - message: e ? jasmine.util.formatException(e) : 'Exception', - trace: { stack: e.stack } - }); - this.results_.addResult(expectationResult); -}; - -jasmine.Spec.prototype.getMatchersClass_ = function() { - return this.matchersClass || this.env.matchersClass; -}; - -jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { - var parent = this.getMatchersClass_(); - var newMatchersClass = function() { - parent.apply(this, arguments); - }; - jasmine.util.inherit(newMatchersClass, parent); - jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); - this.matchersClass = newMatchersClass; -}; - -jasmine.Spec.prototype.finishCallback = function() { - this.env.reporter.reportSpecResults(this); -}; - -jasmine.Spec.prototype.finish = function(onComplete) { - this.removeAllSpies(); - this.finishCallback(); - if (onComplete) { - onComplete(); - } -}; - -jasmine.Spec.prototype.after = function(doAfter) { - if (this.queue.isRunning()) { - this.queue.add(new jasmine.Block(this.env, doAfter, this)); - } else { - this.afterCallbacks.unshift(doAfter); - } -}; - -jasmine.Spec.prototype.execute = function(onComplete) { - var spec = this; - if (!spec.env.specFilter(spec)) { - spec.results_.skipped = true; - spec.finish(onComplete); - return; - } - - this.env.reporter.reportSpecStarting(this); - - spec.env.currentSpec = spec; - - spec.addBeforesAndAftersToQueue(); - - spec.queue.start(function () { - spec.finish(onComplete); - }); -}; - -jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { - var runner = this.env.currentRunner(); - var i; - - for (var suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); - } - } - for (i = 0; i < runner.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); - } - for (i = 0; i < this.afterCallbacks.length; i++) { - this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); - } - for (suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); - } - } - for (i = 0; i < runner.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); - } -}; - -jasmine.Spec.prototype.explodes = function() { - throw 'explodes function should not have been called'; -}; - -jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { - if (obj == jasmine.undefined) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; - } - - if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { - throw methodName + '() method does not exist'; - } - - if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { - throw new Error(methodName + ' has already been spied upon'); - } - - var spyObj = jasmine.createSpy(methodName); - - this.spies_.push(spyObj); - spyObj.baseObj = obj; - spyObj.methodName = methodName; - spyObj.originalValue = obj[methodName]; - - obj[methodName] = spyObj; - - return spyObj; -}; - -jasmine.Spec.prototype.removeAllSpies = function() { - for (var i = 0; i < this.spies_.length; i++) { - var spy = this.spies_[i]; - spy.baseObj[spy.methodName] = spy.originalValue; - } - this.spies_ = []; -}; - -/** - * Internal representation of a Jasmine suite. - * - * @constructor - * @param {jasmine.Env} env - * @param {String} description - * @param {Function} specDefinitions - * @param {jasmine.Suite} parentSuite - */ -jasmine.Suite = function(env, description, specDefinitions, parentSuite) { - var self = this; - self.id = env.nextSuiteId ? env.nextSuiteId() : null; - self.description = description; - self.queue = new jasmine.Queue(env); - self.parentSuite = parentSuite; - self.env = env; - self.before_ = []; - self.after_ = []; - self.children_ = []; - self.suites_ = []; - self.specs_ = []; -}; - -jasmine.Suite.prototype.getFullName = function() { - var fullName = this.description; - for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { - fullName = parentSuite.description + ' ' + fullName; - } - return fullName; -}; - -jasmine.Suite.prototype.finish = function(onComplete) { - this.env.reporter.reportSuiteResults(this); - this.finished = true; - if (typeof(onComplete) == 'function') { - onComplete(); - } -}; - -jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.unshift(beforeEachFunction); -}; - -jasmine.Suite.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.unshift(afterEachFunction); -}; - -jasmine.Suite.prototype.results = function() { - return this.queue.results(); -}; - -jasmine.Suite.prototype.add = function(suiteOrSpec) { - this.children_.push(suiteOrSpec); - if (suiteOrSpec instanceof jasmine.Suite) { - this.suites_.push(suiteOrSpec); - this.env.currentRunner().addSuite(suiteOrSpec); - } else { - this.specs_.push(suiteOrSpec); - } - this.queue.add(suiteOrSpec); -}; - -jasmine.Suite.prototype.specs = function() { - return this.specs_; -}; - -jasmine.Suite.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Suite.prototype.children = function() { - return this.children_; -}; - -jasmine.Suite.prototype.execute = function(onComplete) { - var self = this; - this.queue.start(function () { - self.finish(onComplete); - }); -}; -jasmine.WaitsBlock = function(env, timeout, spec) { - this.timeout = timeout; - jasmine.Block.call(this, env, null, spec); -}; - -jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); - -jasmine.WaitsBlock.prototype.execute = function (onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); - } - this.env.setTimeout(function () { - onComplete(); - }, this.timeout); -}; -/** - * A block which waits for some condition to become true, with timeout. - * - * @constructor - * @extends jasmine.Block - * @param {jasmine.Env} env The Jasmine environment. - * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. - * @param {Function} latchFunction A function which returns true when the desired condition has been met. - * @param {String} message The message to display if the desired condition hasn't been met within the given time period. - * @param {jasmine.Spec} spec The Jasmine spec. - */ -jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { - this.timeout = timeout || env.defaultTimeoutInterval; - this.latchFunction = latchFunction; - this.message = message; - this.totalTimeSpentWaitingForLatch = 0; - jasmine.Block.call(this, env, null, spec); -}; -jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); - -jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; - -jasmine.WaitsForBlock.prototype.execute = function(onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); - } - var latchFunctionResult; - try { - latchFunctionResult = this.latchFunction.apply(this.spec); - } catch (e) { - this.spec.fail(e); - onComplete(); - return; - } - - if (latchFunctionResult) { - onComplete(); - } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { - var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); - this.spec.fail({ - name: 'timeout', - message: message - }); - - this.abort = true; - onComplete(); - } else { - this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; - var self = this; - this.env.setTimeout(function() { - self.execute(onComplete); - }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); - } -}; - -jasmine.version_= { - "major": 1, - "minor": 2, - "build": 0, - "revision": 1337005947 -}; diff --git a/spec/lib/jasmine-promise.js b/spec/lib/jasmine-promise.js deleted file mode 100644 index 5ffa2564..00000000 --- a/spec/lib/jasmine-promise.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; - -jasmine.Block.prototype.execute = function (onComplete) { - var spec = this.spec; - var result; - try { - result = this.func.call(spec, onComplete); - - // It seems Jasmine likes to return the suite if you pass it anything. - // So make sure it's a promise first. - if (result && typeof result.then === "function") { - result.then(function () { - onComplete(); - }, function (error) { - spec.fail(error); - onComplete(); - }); - } else if (this.func.length === 0) { - onComplete(); - } - } catch (error) { - spec.fail(error); - onComplete(); - } -}; - diff --git a/spec/node-spec.js b/spec/node-spec.js deleted file mode 100644 index 6eae3bbf..00000000 --- a/spec/node-spec.js +++ /dev/null @@ -1,20 +0,0 @@ - -require("./lib/jasmine-promise"); -var URL = require("url"); -var Require = require("../node"); - -// Use async spec to cause Jasmine to wait until the real specs have been loaded -describe("Mr on node", function () { - it("must test on node", function () { - expect(typeof window).toBe("undefined"); - }); - it("loads", function () { - var location = Require.directoryPathToLocation(__dirname); - location = URL.resolve(location, "../"); - return Require.loadPackage(location) - .then(function (mr) { - return mr.async("spec/require-spec"); - }) - .thenResolve(); - }); -}); diff --git a/spec/phantom.js b/spec/phantom.js index db294275..6a0b5c57 100644 --- a/spec/phantom.js +++ b/spec/phantom.js @@ -1,13 +1,13 @@ var COVERAGE = !!process.env["npm_config_coverage"]; -var PATH = require("path"); +var Path = require("path"); var spawn = require("child_process").spawn; var util = require("util"); var Q = require("q"); var wd = require("wd"); -var joey = require("joey"); -var Apps = require("q-io/http-apps"); +var Http = require("q-io/http"); +var HttpApps = require("q-io/http-apps"); if (COVERAGE) { var IGNORE_RE = /spec|packages/; @@ -17,7 +17,7 @@ if (COVERAGE) { var instrumenter = new istanbul.Instrumenter(); var fileTree = function (path) { - return Apps.FileTree(path, { + return HttpApps.FileTree(path, { // use a custom file reader to instrument the code file: function (request, path, contentType, fs) { if (path.match(/.js$/) && !path.match(IGNORE_RE)) { @@ -45,12 +45,12 @@ if (COVERAGE) { } // otherwise just serve the file - return Apps.file(request, path, contentType, fs); + return HttpApps.file(request, path, contentType, fs); } }); }; } else { - var fileTree = Apps.FileTree; + var fileTree = HttpApps.FileTree; } var TESTS_FAILED = {}; @@ -62,15 +62,14 @@ var phantom = spawn("phantomjs", ["--webdriver=127.0.0.1:8910"], { var browser = wd.promiseRemote("127.0.0.1", 8910); -var server = joey -.error(true) -.app(fileTree(PATH.resolve(__dirname, ".."))) -.server(); +var app = fileTree(Path.resolve(__dirname, "..")); +var app = HttpApps.Error(app, true); +var server = new Http.Server(app); server.listen(0).done(); var testPagePort = server.node.address().port; -var testPageUrl = "http://127.0.0.1:" + testPagePort + "/spec/run.html"; +var testPageUrl = "http://127.0.0.1:" + testPagePort + "/spec/index.html"; console.log("Test page at " + testPageUrl); // wait for Ghost Driver to start running @@ -145,7 +144,7 @@ Q.delay(2000) .finally(function () { phantom.kill(); }) -.fail(function (err) { +.catch(function (err) { if (err === TESTS_FAILED) { process.exit(1); } diff --git a/spec/run.html b/spec/run.html deleted file mode 100644 index 7168d63e..00000000 --- a/spec/run.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - Jasmine Spec Runner - - - - - - - - - - - From aee5c3f13ab482c2800d4f0257b3dc3544fc560e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 25 Feb 2014 21:44:21 -0800 Subject: [PATCH 39/64] Ensure that build products are consistent Avoid changes due to random variations in load order. --- boot.js | 8868 +++++++++++++++++++-------------------- boot/boilerplate.js | 4 +- build.js | 29 +- jasminum.js | 9734 +++++++++++++++++++++---------------------- 4 files changed, 9329 insertions(+), 9306 deletions(-) diff --git a/boot.js b/boot.js index 0feb72a4..c0535bc1 100644 --- a/boot.js +++ b/boot.js @@ -10,8 +10,8 @@ global = this; for (var i = 0; i < modules.length; i++) { var module = modules[i]; modules[i] = new Module(module[0], module[1], module[2], module[3]); - bundle[module.name] = bundle[module.id] || {}; - bundle[module.name][module.id] = module; + bundle[module[0]] = bundle[module[1]] || {}; + bundle[module[0]][module[1]] = module; } function Module(name, id, map, factory) { @@ -43,7 +43,7 @@ global = this; Module.prototype.bundle = bundle; return modules[0].getExports(); -})((function (global){return[["mr","boot/require",{"../require":2,"url":3,"q":6,"./script-params":1},function (require, exports, module){ +})((function (global){return[["mr","boot/require",{"../require":10,"url":12,"q":14,"./script-params":9},function (require, exports, module){ // mr boot/require // --------------- @@ -95,5180 +95,5180 @@ function boot(preloaded, params) { } -}],["mr","boot/script-params",{"url":3},function (require, exports, module){ +}],["asap","asap",{"./queue":2},function (require, exports, module){ -// mr boot/script-params -// --------------------- - - -var URL = require("url"); - -module.exports = getParams; -function getParams(scriptName) { - var i, j, - match, - script, - location, - attr, - name, - re = new RegExp("^(.*)" + scriptName + "(?:[\\?\\.]|$)", "i"); - var params = {}; - // Find the + diff --git a/spec/index.js b/test/index.js similarity index 100% rename from spec/index.js rename to test/index.js diff --git a/spec/inject-dependency/node_modules/dependency/module.js b/test/inject-dependency/node_modules/dependency/module.js similarity index 100% rename from spec/inject-dependency/node_modules/dependency/module.js rename to test/inject-dependency/node_modules/dependency/module.js diff --git a/spec/inject-dependency/node_modules/dependency/package.json b/test/inject-dependency/node_modules/dependency/package.json similarity index 100% rename from spec/inject-dependency/node_modules/dependency/package.json rename to test/inject-dependency/node_modules/dependency/package.json diff --git a/spec/inject-dependency/package.json b/test/inject-dependency/package.json similarity index 100% rename from spec/inject-dependency/package.json rename to test/inject-dependency/package.json diff --git a/spec/inject-dependency/program.js b/test/inject-dependency/program.js similarity index 100% rename from spec/inject-dependency/program.js rename to test/inject-dependency/program.js diff --git a/spec/inject-mapping/mapping/module.js b/test/inject-mapping/mapping/module.js similarity index 100% rename from spec/inject-mapping/mapping/module.js rename to test/inject-mapping/mapping/module.js diff --git a/spec/inject-mapping/mapping/package.json b/test/inject-mapping/mapping/package.json similarity index 100% rename from spec/inject-mapping/mapping/package.json rename to test/inject-mapping/mapping/package.json diff --git a/spec/inject-mapping/package.json b/test/inject-mapping/package.json similarity index 100% rename from spec/inject-mapping/package.json rename to test/inject-mapping/package.json diff --git a/spec/inject-mapping/program.js b/test/inject-mapping/program.js similarity index 100% rename from spec/inject-mapping/program.js rename to test/inject-mapping/program.js diff --git a/spec/json/package.json b/test/json/package.json similarity index 100% rename from spec/json/package.json rename to test/json/package.json diff --git a/spec/json/program.js b/test/json/program.js similarity index 100% rename from spec/json/program.js rename to test/json/program.js diff --git a/spec/load-package-name/node_modules/a/a.js b/test/load-package-name/node_modules/a/a.js similarity index 100% rename from spec/load-package-name/node_modules/a/a.js rename to test/load-package-name/node_modules/a/a.js diff --git a/spec/load-package-name/node_modules/a/package.json b/test/load-package-name/node_modules/a/package.json similarity index 100% rename from spec/load-package-name/node_modules/a/package.json rename to test/load-package-name/node_modules/a/package.json diff --git a/spec/load-package-name/package.json b/test/load-package-name/package.json similarity index 100% rename from spec/load-package-name/package.json rename to test/load-package-name/package.json diff --git a/spec/load-package-name/program.js b/test/load-package-name/program.js similarity index 100% rename from spec/load-package-name/program.js rename to test/load-package-name/program.js diff --git a/spec/load-package/a/a.js b/test/load-package/a/a.js similarity index 100% rename from spec/load-package/a/a.js rename to test/load-package/a/a.js diff --git a/spec/load-package/a/package.json b/test/load-package/a/package.json similarity index 100% rename from spec/load-package/a/package.json rename to test/load-package/a/package.json diff --git a/spec/load-package/package.json b/test/load-package/package.json similarity index 100% rename from spec/load-package/package.json rename to test/load-package/package.json diff --git a/spec/load-package/program.js b/test/load-package/program.js similarity index 100% rename from spec/load-package/program.js rename to test/load-package/program.js diff --git a/spec/main-name/node_modules/dependency/dependency.js b/test/main-name/node_modules/dependency/dependency.js similarity index 100% rename from spec/main-name/node_modules/dependency/dependency.js rename to test/main-name/node_modules/dependency/dependency.js diff --git a/spec/main-name/node_modules/dependency/package.json b/test/main-name/node_modules/dependency/package.json similarity index 100% rename from spec/main-name/node_modules/dependency/package.json rename to test/main-name/node_modules/dependency/package.json diff --git a/spec/main-name/package.json b/test/main-name/package.json similarity index 100% rename from spec/main-name/package.json rename to test/main-name/package.json diff --git a/spec/main-name/program.js b/test/main-name/program.js similarity index 100% rename from spec/main-name/program.js rename to test/main-name/program.js diff --git a/spec/main/node_modules/dot-js-ext/a.min.js b/test/main/node_modules/dot-js-ext/a.min.js similarity index 100% rename from spec/main/node_modules/dot-js-ext/a.min.js rename to test/main/node_modules/dot-js-ext/a.min.js diff --git a/spec/main/node_modules/dot-js-ext/package.json b/test/main/node_modules/dot-js-ext/package.json similarity index 100% rename from spec/main/node_modules/dot-js-ext/package.json rename to test/main/node_modules/dot-js-ext/package.json diff --git a/spec/main/node_modules/dot-slash/a.js b/test/main/node_modules/dot-slash/a.js similarity index 100% rename from spec/main/node_modules/dot-slash/a.js rename to test/main/node_modules/dot-slash/a.js diff --git a/spec/main/node_modules/dot-slash/package.json b/test/main/node_modules/dot-slash/package.json similarity index 100% rename from spec/main/node_modules/dot-slash/package.json rename to test/main/node_modules/dot-slash/package.json diff --git a/spec/main/node_modules/js-ext/a.js b/test/main/node_modules/js-ext/a.js similarity index 100% rename from spec/main/node_modules/js-ext/a.js rename to test/main/node_modules/js-ext/a.js diff --git a/spec/main/node_modules/js-ext/package.json b/test/main/node_modules/js-ext/package.json similarity index 100% rename from spec/main/node_modules/js-ext/package.json rename to test/main/node_modules/js-ext/package.json diff --git a/spec/main/node_modules/no-ext/a.js b/test/main/node_modules/no-ext/a.js similarity index 100% rename from spec/main/node_modules/no-ext/a.js rename to test/main/node_modules/no-ext/a.js diff --git a/spec/main/node_modules/no-ext/package.json b/test/main/node_modules/no-ext/package.json similarity index 100% rename from spec/main/node_modules/no-ext/package.json rename to test/main/node_modules/no-ext/package.json diff --git a/spec/main/package.json b/test/main/package.json similarity index 100% rename from spec/main/package.json rename to test/main/package.json diff --git a/spec/main/program.js b/test/main/program.js similarity index 100% rename from spec/main/program.js rename to test/main/program.js diff --git a/spec/method/a.js b/test/method/a.js similarity index 100% rename from spec/method/a.js rename to test/method/a.js diff --git a/spec/method/package.json b/test/method/package.json similarity index 100% rename from spec/method/package.json rename to test/method/package.json diff --git a/spec/method/program.js b/test/method/program.js similarity index 100% rename from spec/method/program.js rename to test/method/program.js diff --git a/spec/missing/package.json b/test/missing/package.json similarity index 100% rename from spec/missing/package.json rename to test/missing/package.json diff --git a/spec/missing/program.js b/test/missing/program.js similarity index 100% rename from spec/missing/program.js rename to test/missing/program.js diff --git a/spec/module-exports/module-exports.js b/test/module-exports/module-exports.js similarity index 100% rename from spec/module-exports/module-exports.js rename to test/module-exports/module-exports.js diff --git a/spec/module-exports/package.json b/test/module-exports/package.json similarity index 100% rename from spec/module-exports/package.json rename to test/module-exports/package.json diff --git a/spec/module-exports/program.js b/test/module-exports/program.js similarity index 100% rename from spec/module-exports/program.js rename to test/module-exports/program.js diff --git a/spec/monkeys/a.js b/test/monkeys/a.js similarity index 100% rename from spec/monkeys/a.js rename to test/monkeys/a.js diff --git a/spec/monkeys/package.json b/test/monkeys/package.json similarity index 100% rename from spec/monkeys/package.json rename to test/monkeys/package.json diff --git a/spec/monkeys/program.js b/test/monkeys/program.js similarity index 100% rename from spec/monkeys/program.js rename to test/monkeys/program.js diff --git a/spec/named-mappings/node_modules/bar/package.json b/test/named-mappings/node_modules/bar/package.json similarity index 100% rename from spec/named-mappings/node_modules/bar/package.json rename to test/named-mappings/node_modules/bar/package.json diff --git a/spec/named-mappings/node_modules/foo/foo.js b/test/named-mappings/node_modules/foo/foo.js similarity index 100% rename from spec/named-mappings/node_modules/foo/foo.js rename to test/named-mappings/node_modules/foo/foo.js diff --git a/spec/named-mappings/node_modules/foo/package.json b/test/named-mappings/node_modules/foo/package.json similarity index 100% rename from spec/named-mappings/node_modules/foo/package.json rename to test/named-mappings/node_modules/foo/package.json diff --git a/spec/named-mappings/package.json b/test/named-mappings/package.json similarity index 100% rename from spec/named-mappings/package.json rename to test/named-mappings/package.json diff --git a/spec/named-mappings/program.js b/test/named-mappings/program.js similarity index 100% rename from spec/named-mappings/program.js rename to test/named-mappings/program.js diff --git a/spec/named-packages/node_modules/bar/package.json b/test/named-packages/node_modules/bar/package.json similarity index 100% rename from spec/named-packages/node_modules/bar/package.json rename to test/named-packages/node_modules/bar/package.json diff --git a/spec/named-packages/node_modules/foo/foo.js b/test/named-packages/node_modules/foo/foo.js similarity index 100% rename from spec/named-packages/node_modules/foo/foo.js rename to test/named-packages/node_modules/foo/foo.js diff --git a/spec/named-packages/node_modules/foo/package.json b/test/named-packages/node_modules/foo/package.json similarity index 100% rename from spec/named-packages/node_modules/foo/package.json rename to test/named-packages/node_modules/foo/package.json diff --git a/spec/named-packages/package.json b/test/named-packages/package.json similarity index 100% rename from spec/named-packages/package.json rename to test/named-packages/package.json diff --git a/spec/named-packages/program.js b/test/named-packages/program.js similarity index 100% rename from spec/named-packages/program.js rename to test/named-packages/program.js diff --git a/spec/named-parent-package/node_modules/child-package/child-module.js b/test/named-parent-package/node_modules/child-package/child-module.js similarity index 100% rename from spec/named-parent-package/node_modules/child-package/child-module.js rename to test/named-parent-package/node_modules/child-package/child-module.js diff --git a/spec/named-parent-package/node_modules/child-package/package.json b/test/named-parent-package/node_modules/child-package/package.json similarity index 100% rename from spec/named-parent-package/node_modules/child-package/package.json rename to test/named-parent-package/node_modules/child-package/package.json diff --git a/spec/named-parent-package/package.json b/test/named-parent-package/package.json similarity index 100% rename from spec/named-parent-package/package.json rename to test/named-parent-package/package.json diff --git a/spec/named-parent-package/parent-module.js b/test/named-parent-package/parent-module.js similarity index 100% rename from spec/named-parent-package/parent-module.js rename to test/named-parent-package/parent-module.js diff --git a/spec/named-parent-package/program.js b/test/named-parent-package/program.js similarity index 100% rename from spec/named-parent-package/program.js rename to test/named-parent-package/program.js diff --git a/spec/nested/a/b/c/d.js b/test/nested/a/b/c/d.js similarity index 100% rename from spec/nested/a/b/c/d.js rename to test/nested/a/b/c/d.js diff --git a/spec/nested/package.json b/test/nested/package.json similarity index 100% rename from spec/nested/package.json rename to test/nested/package.json diff --git a/spec/nested/program.js b/test/nested/program.js similarity index 100% rename from spec/nested/program.js rename to test/nested/program.js diff --git a/spec/not-found/package.json b/test/not-found/package.json similarity index 100% rename from spec/not-found/package.json rename to test/not-found/package.json diff --git a/spec/not-found/program.js b/test/not-found/program.js similarity index 100% rename from spec/not-found/program.js rename to test/not-found/program.js diff --git a/spec/optimizer/hello.js b/test/optimizer/hello.js similarity index 100% rename from spec/optimizer/hello.js rename to test/optimizer/hello.js diff --git a/spec/optimizer/js-optimizer.js b/test/optimizer/js-optimizer.js similarity index 100% rename from spec/optimizer/js-optimizer.js rename to test/optimizer/js-optimizer.js diff --git a/spec/optimizer/package.json b/test/optimizer/package.json similarity index 100% rename from spec/optimizer/package.json rename to test/optimizer/package.json diff --git a/spec/optimizer/program.js b/test/optimizer/program.js similarity index 100% rename from spec/optimizer/program.js rename to test/optimizer/program.js diff --git a/spec/phantom.js b/test/phantom.js similarity index 100% rename from spec/phantom.js rename to test/phantom.js diff --git a/spec/production/node_modules/dev-dependency/dev-dependency.js b/test/production/node_modules/dev-dependency/dev-dependency.js similarity index 100% rename from spec/production/node_modules/dev-dependency/dev-dependency.js rename to test/production/node_modules/dev-dependency/dev-dependency.js diff --git a/spec/production/node_modules/dev-dependency/package.json b/test/production/node_modules/dev-dependency/package.json similarity index 100% rename from spec/production/node_modules/dev-dependency/package.json rename to test/production/node_modules/dev-dependency/package.json diff --git a/spec/production/package.json b/test/production/package.json similarity index 100% rename from spec/production/package.json rename to test/production/package.json diff --git a/spec/production/program.js b/test/production/program.js similarity index 100% rename from spec/production/program.js rename to test/production/program.js diff --git a/spec/read/package.json b/test/read/package.json similarity index 100% rename from spec/read/package.json rename to test/read/package.json diff --git a/spec/read/program.js b/test/read/program.js similarity index 100% rename from spec/read/program.js rename to test/read/program.js diff --git a/spec/redirect-patterns/foo-bar.js b/test/redirect-patterns/foo-bar.js similarity index 100% rename from spec/redirect-patterns/foo-bar.js rename to test/redirect-patterns/foo-bar.js diff --git a/spec/redirect-patterns/node_modules/mr-foo/package.json b/test/redirect-patterns/node_modules/mr-foo/package.json similarity index 100% rename from spec/redirect-patterns/node_modules/mr-foo/package.json rename to test/redirect-patterns/node_modules/mr-foo/package.json diff --git a/spec/redirect-patterns/package.json b/test/redirect-patterns/package.json similarity index 100% rename from spec/redirect-patterns/package.json rename to test/redirect-patterns/package.json diff --git a/spec/redirect-patterns/program.js b/test/redirect-patterns/program.js similarity index 100% rename from spec/redirect-patterns/program.js rename to test/redirect-patterns/program.js diff --git a/spec/redirects-package/node_modules/foo/barz.js b/test/redirects-package/node_modules/foo/barz.js similarity index 100% rename from spec/redirects-package/node_modules/foo/barz.js rename to test/redirects-package/node_modules/foo/barz.js diff --git a/spec/redirects-package/node_modules/foo/package.json b/test/redirects-package/node_modules/foo/package.json similarity index 100% rename from spec/redirects-package/node_modules/foo/package.json rename to test/redirects-package/node_modules/foo/package.json diff --git a/spec/redirects-package/package.json b/test/redirects-package/package.json similarity index 100% rename from spec/redirects-package/package.json rename to test/redirects-package/package.json diff --git a/spec/redirects-package/program.js b/test/redirects-package/program.js similarity index 100% rename from spec/redirects-package/program.js rename to test/redirects-package/program.js diff --git a/spec/redirects/barz.js b/test/redirects/barz.js similarity index 100% rename from spec/redirects/barz.js rename to test/redirects/barz.js diff --git a/spec/redirects/package.json b/test/redirects/package.json similarity index 100% rename from spec/redirects/package.json rename to test/redirects/package.json diff --git a/spec/redirects/program.js b/test/redirects/program.js similarity index 100% rename from spec/redirects/program.js rename to test/redirects/program.js diff --git a/spec/relative/package.json b/test/relative/package.json similarity index 100% rename from spec/relative/package.json rename to test/relative/package.json diff --git a/spec/relative/program.js b/test/relative/program.js similarity index 100% rename from spec/relative/program.js rename to test/relative/program.js diff --git a/spec/relative/submodule/a.js b/test/relative/submodule/a.js similarity index 100% rename from spec/relative/submodule/a.js rename to test/relative/submodule/a.js diff --git a/spec/relative/submodule/b.js b/test/relative/submodule/b.js similarity index 100% rename from spec/relative/submodule/b.js rename to test/relative/submodule/b.js diff --git a/spec/require-spec.js b/test/require-spec.js similarity index 100% rename from spec/require-spec.js rename to test/require-spec.js diff --git a/spec/return/package.json b/test/return/package.json similarity index 100% rename from spec/return/package.json rename to test/return/package.json diff --git a/spec/return/program.js b/test/return/program.js similarity index 100% rename from spec/return/program.js rename to test/return/program.js diff --git a/spec/return/returns.js b/test/return/returns.js similarity index 100% rename from spec/return/returns.js rename to test/return/returns.js diff --git a/spec/run.js b/test/run.js similarity index 100% rename from spec/run.js rename to test/run.js diff --git a/spec/sandbox/a.js b/test/sandbox/a.js similarity index 100% rename from spec/sandbox/a.js rename to test/sandbox/a.js diff --git a/spec/sandbox/b.js b/test/sandbox/b.js similarity index 100% rename from spec/sandbox/b.js rename to test/sandbox/b.js diff --git a/spec/sandbox/node_modules/dependency/c.js b/test/sandbox/node_modules/dependency/c.js similarity index 100% rename from spec/sandbox/node_modules/dependency/c.js rename to test/sandbox/node_modules/dependency/c.js diff --git a/spec/sandbox/node_modules/dependency/main.js b/test/sandbox/node_modules/dependency/main.js similarity index 100% rename from spec/sandbox/node_modules/dependency/main.js rename to test/sandbox/node_modules/dependency/main.js diff --git a/spec/sandbox/node_modules/dependency/other.js b/test/sandbox/node_modules/dependency/other.js similarity index 100% rename from spec/sandbox/node_modules/dependency/other.js rename to test/sandbox/node_modules/dependency/other.js diff --git a/spec/sandbox/node_modules/dependency/package.json b/test/sandbox/node_modules/dependency/package.json similarity index 100% rename from spec/sandbox/node_modules/dependency/package.json rename to test/sandbox/node_modules/dependency/package.json diff --git a/spec/sandbox/package.json b/test/sandbox/package.json similarity index 100% rename from spec/sandbox/package.json rename to test/sandbox/package.json diff --git a/spec/sandbox/program.js b/test/sandbox/program.js similarity index 100% rename from spec/sandbox/program.js rename to test/sandbox/program.js diff --git a/spec/script-injection-dep/node_modules/dependency/main.load.js b/test/script-injection-dep/node_modules/dependency/main.load.js similarity index 100% rename from spec/script-injection-dep/node_modules/dependency/main.load.js rename to test/script-injection-dep/node_modules/dependency/main.load.js diff --git a/spec/script-injection-dep/node_modules/dependency/package.json b/test/script-injection-dep/node_modules/dependency/package.json similarity index 100% rename from spec/script-injection-dep/node_modules/dependency/package.json rename to test/script-injection-dep/node_modules/dependency/package.json diff --git a/spec/script-injection-dep/node_modules/dependency/second.load.js b/test/script-injection-dep/node_modules/dependency/second.load.js similarity index 100% rename from spec/script-injection-dep/node_modules/dependency/second.load.js rename to test/script-injection-dep/node_modules/dependency/second.load.js diff --git a/spec/script-injection-dep/package.json b/test/script-injection-dep/package.json similarity index 100% rename from spec/script-injection-dep/package.json rename to test/script-injection-dep/package.json diff --git a/spec/script-injection-dep/program.js b/test/script-injection-dep/program.js similarity index 100% rename from spec/script-injection-dep/program.js rename to test/script-injection-dep/program.js diff --git a/spec/script-injection/package.json b/test/script-injection/package.json similarity index 100% rename from spec/script-injection/package.json rename to test/script-injection/package.json diff --git a/spec/script-injection/packages/dependency/main.load.js b/test/script-injection/packages/dependency/main.load.js similarity index 100% rename from spec/script-injection/packages/dependency/main.load.js rename to test/script-injection/packages/dependency/main.load.js diff --git a/spec/script-injection/packages/dependency/package.json.load.js b/test/script-injection/packages/dependency/package.json.load.js similarity index 100% rename from spec/script-injection/packages/dependency/package.json.load.js rename to test/script-injection/packages/dependency/package.json.load.js diff --git a/spec/script-injection/program.js b/test/script-injection/program.js similarity index 100% rename from spec/script-injection/program.js rename to test/script-injection/program.js diff --git a/spec/top-level/b.js b/test/top-level/b.js similarity index 100% rename from spec/top-level/b.js rename to test/top-level/b.js diff --git a/spec/top-level/package.json b/test/top-level/package.json similarity index 100% rename from spec/top-level/package.json rename to test/top-level/package.json diff --git a/spec/top-level/program.js b/test/top-level/program.js similarity index 100% rename from spec/top-level/program.js rename to test/top-level/program.js diff --git a/spec/top-level/submodule/a.js b/test/top-level/submodule/a.js similarity index 100% rename from spec/top-level/submodule/a.js rename to test/top-level/submodule/a.js diff --git a/spec/transitive/a.js b/test/transitive/a.js similarity index 100% rename from spec/transitive/a.js rename to test/transitive/a.js diff --git a/spec/transitive/b.js b/test/transitive/b.js similarity index 100% rename from spec/transitive/b.js rename to test/transitive/b.js diff --git a/spec/transitive/c.js b/test/transitive/c.js similarity index 100% rename from spec/transitive/c.js rename to test/transitive/c.js diff --git a/spec/transitive/package.json b/test/transitive/package.json similarity index 100% rename from spec/transitive/package.json rename to test/transitive/package.json diff --git a/spec/transitive/program.js b/test/transitive/program.js similarity index 100% rename from spec/transitive/program.js rename to test/transitive/program.js diff --git a/spec/translator-package/hello.text b/test/translator-package/hello.text similarity index 100% rename from spec/translator-package/hello.text rename to test/translator-package/hello.text diff --git a/spec/translator-package/node_modules/mr-text/mr-text.js b/test/translator-package/node_modules/mr-text/mr-text.js similarity index 100% rename from spec/translator-package/node_modules/mr-text/mr-text.js rename to test/translator-package/node_modules/mr-text/mr-text.js diff --git a/spec/translator-package/node_modules/mr-text/package.json b/test/translator-package/node_modules/mr-text/package.json similarity index 100% rename from spec/translator-package/node_modules/mr-text/package.json rename to test/translator-package/node_modules/mr-text/package.json diff --git a/spec/translator-package/package.json b/test/translator-package/package.json similarity index 100% rename from spec/translator-package/package.json rename to test/translator-package/package.json diff --git a/spec/translator-package/program.js b/test/translator-package/program.js similarity index 100% rename from spec/translator-package/program.js rename to test/translator-package/program.js diff --git a/spec/translator-package/test.js b/test/translator-package/test.js similarity index 100% rename from spec/translator-package/test.js rename to test/translator-package/test.js diff --git a/spec/translator/hello.text b/test/translator/hello.text similarity index 100% rename from spec/translator/hello.text rename to test/translator/hello.text diff --git a/spec/translator/package.json b/test/translator/package.json similarity index 100% rename from spec/translator/package.json rename to test/translator/package.json diff --git a/spec/translator/program.js b/test/translator/program.js similarity index 100% rename from spec/translator/program.js rename to test/translator/program.js diff --git a/spec/translator/text-translator.js b/test/translator/text-translator.js similarity index 100% rename from spec/translator/text-translator.js rename to test/translator/text-translator.js From 3eb22597ee391886937ee1ac37c332c0d989da90 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:04:22 -0700 Subject: [PATCH 41/64] Remove old generated scripts --- boot/preload-boilerplate.js | 3497 ------------------ jasminum.js | 6865 ----------------------------------- 2 files changed, 10362 deletions(-) delete mode 100644 boot/preload-boilerplate.js delete mode 100644 jasminum.js diff --git a/boot/preload-boilerplate.js b/boot/preload-boilerplate.js deleted file mode 100644 index e65c96e9..00000000 --- a/boot/preload-boilerplate.js +++ /dev/null @@ -1,3497 +0,0 @@ -(function (modules) { - - // unpack module tuples into module objects - for (var i = 0; i < modules.length; i++) { - modules[i] = new Module(modules[i][0], modules[i][1]); - } - - function Module(dependencies, factory) { - this.dependencies = dependencies; - this.factory = factory; - } - - Module.prototype.getExports = function () { - var module = this; - if (!module.exports) { - module.exports = {}; - var require = function (id) { - var index = module.dependencies[id]; - var dependency = modules[index]; - if (!dependency) - throw new Error("Bundle is missing a dependency: " + id); - return dependency.getExports(); - } - module.exports = module.factory(require, module.exports, module) || module.exports; - } - return module.exports; - }; - - return modules[0].getExports(); -})((function (global){return[[{"./browser":1,"./preload":2,"./script-params":3},function (require, exports, module){ - -// mr boot/preload-entry -// --------------------- - - -var boot = require("./browser"); -var preload = require("./preload"); -var getParams = require("./script-params"); - -module.exports = function bootstrapPreload(plan) { - var params = getParams(); - return boot(preload(plan, params), params); -}; - -}],[{"../browser":4,"url":5,"q":8,"./script-params":3},function (require, exports, module){ - -// mr boot/browser -// --------------- - -"use strict"; - -var Require = require("../browser"); -var URL = require("url"); -var Q = require("q"); -var getParams = require("./script-params"); - -module.exports = boot; -function boot(preloaded, params) { - params = params || getParams("boot.js"); - - var config = {preloaded: preloaded}; - var applicationLocation = URL.resolve(window.location, params.package || "."); - var moduleId = params.module || ""; - - if ("autoPackage" in params) { - Require.injectPackageDescription(applicationLocation, {}); - } - - return Require.loadPackage({ - location: applicationLocation, - hash: params.applicationHash - }) - .then(function (applicationRequire) { - return applicationRequire.loadPackage({ - location: params.location, - hash: params.mrHash - }, config) - .then(function (mrRequire) { - return mrRequire.loadPackage({ - name: "q", - location: params.qLocation, - hash: params.qHash - }) - .then(function (qRequire) { - qRequire.inject("q", Q); - mrRequire.inject("mini-url", URL); - mrRequire.inject("require", Require); - return applicationRequire.async(moduleId); - }); - }); - }); - -} - -}],[{"../script":6,"q":8},function (require, exports, module){ - -// mr boot/preload -// --------------- - - -var load = require("../script"); -var Q = require("q"); - -module.exports = function preload(plan, params) { - - // Each bundle ends with a bundleLoaded(name) call. We use these hooks to - // synchronize the preloader. - var bundleHooks = {}; - var getHook = function (name) { - return bundleHooks[name] = - bundleHooks[name] || - Q.defer(); - }; - global.bundleLoaded = function (name) { - getHook(name).resolve(); - }; - - // preload bundles sequentially - var preloaded = plan.reduce(function (previous, bundleLocations) { - return previous.then(function () { - return Q.all(bundleLocations.map(function (bundleLocation) { - load(resolve(params.location, bundleLocation)); - return getHook(bundleLocation).promise; - })); - }); - }, Q()) - .then(function () { - // remove evidence of the evil we have done to the global scope - delete global.bundleLoaded; - }); - - return preloaded; -}; - -}],[{"url":5},function (require, exports, module){ - -// mr boot/script-params -// --------------------- - - -var URL = require("url"); - -module.exports = getParams; -function getParams(scriptName) { - var i, j, - match, - script, - location, - attr, - name, - re = new RegExp("^(.*)" + scriptName + "(?:[\\?\\.]|$)", "i"); - var params = {}; - // Find the + + + + diff --git a/phantom/index.js b/phantom/index.js new file mode 100644 index 00000000..7552a649 --- /dev/null +++ b/phantom/index.js @@ -0,0 +1,33 @@ + +var Q = require("q"); +var URL = require("url"); +var QS = require("qs"); +var Require = require("../require"); +require("./console"); + +var location = URL.resolve(window.location, "/"); +var query = QS.parse(window.location.search.slice(1)); +global.isTTY = !!query.isTTY; + +Require.loadPackage(location, { + overlays: ["browser"] +}) +.then(function (package) { + var loaded = Q(); + var executed = Q(); + query.modules.forEach(function (moduleId) { + loaded = loaded.then(function () { + return package.deepLoad(moduleId); + }); + executed = loaded.then(function () { + var module = package.getModuleDescriptor(moduleId); + module.args = query.args; + return package(moduleId); + }); + }); + return executed; +}) +.done(function () { + alert(0); +}); + diff --git a/phantom/script.js b/phantom/script.js new file mode 100644 index 00000000..aa5d2d66 --- /dev/null +++ b/phantom/script.js @@ -0,0 +1,36 @@ + +var system = require("system"); +var Page = require("webpage"); +var page = Page.create(); + +page.onConsoleMessage = function (message) { + system.stdout.writeLine(message); +}; + +page.onAlert = function (message) { + var code = parseInt(message, 10); + if (code === code) { // !NaN + phantom.exit(code); + } +}; + +page.onError = function (message, trace) { + system.stderr.writeLine(message); + trace.forEach(function (frame) { + system.stderr.writeLine(frame.file + ":" + frame.line, frame.function); + }); + phantom.exit(-1); +}; + +page.onResourceError = function (resourceError) { + system.stderr.writeLine("Resource error: " + resourceError.url); + phantom.exit(-1); +}; + +page.open(system.args[1], function (status) { + if (status !== "success") { + system.stderr.writeLine("Can't load " + system.args[1]); + phantom.exit(-1); + } +}); + diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 124f84d9..00000000 --- a/test/index.js +++ /dev/null @@ -1,118 +0,0 @@ -/* -Copyright (c) 2012, Motorola Mobility LLC. -All Rights Reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Motorola Mobility LLC nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - */ - -var Require = require("../require"); - -var location = __dirname; -if (Require.directoryPathToLocation) { - location = Require.directoryPathToLocation(location); -} - -describe("require", function () { - [ - "cyclic", - "determinism", - "exactExports", - "hasOwnProperty", - "method", - "missing", - "monkeys", - "nested", - "relative", - "top-level", - "transitive", - "module-exports", - "return", - {name: "named-packages", node: false}, - {name: "named-mappings", node: false}, - "named-parent-package", - {name: "load-package", node: false}, - {name: "load-package-name", node: false}, - {name: "not-found", node: false}, - "redirects", - "redirects-package", - "comments", - "identify", - "dev-dependencies", - "production", - "case-sensitive", - "inject-dependency", - "inject-mapping", - {name: "script-injection-dep", node: false}, - {name: "script-injection", node: false}, - "read", - "main-name", - "main", - "sandbox", - "compiler", - "translator", - "compiler-package", - "translator-package", - "redirect-patterns", - "main" - ].forEach(function (test) { - if (typeof test === "object") { - if (test.node === false && typeof process !== "undefined") { - return; - } - test = test.name; - } - it(test, function () { - var spec = this; - var done; - var message; - - //console.log(test + ":", "START"); - - return Require.loadPackage(location + test + "/", {}) - .then(function (pkg) { - pkg.inject("test", { - print: function (_message, level) { - //console.log(test + ":", _message); - if (_message === "DONE") { - message = _message; - } - }, - assert: function (guard, message) { - //console.log(test + ":", guard ? "PASS" : "FAIL", message); - expect(!!guard).toBe(true); - } - }); - - return pkg.async("program"); - }) - .then(function () { - expect(message).toBe("DONE"); - }); - }); - }); -}); - diff --git a/test/phantom.js b/test/phantom.js deleted file mode 100644 index 6a0b5c57..00000000 --- a/test/phantom.js +++ /dev/null @@ -1,188 +0,0 @@ -var COVERAGE = !!process.env["npm_config_coverage"]; - -var Path = require("path"); -var spawn = require("child_process").spawn; -var util = require("util"); - -var Q = require("q"); -var wd = require("wd"); -var Http = require("q-io/http"); -var HttpApps = require("q-io/http-apps"); - -if (COVERAGE) { - var IGNORE_RE = /spec|packages/; - - var FS = require("q-io/fs"); - var istanbul = require("istanbul"); - var instrumenter = new istanbul.Instrumenter(); - - var fileTree = function (path) { - return HttpApps.FileTree(path, { - // use a custom file reader to instrument the code - file: function (request, path, contentType, fs) { - if (path.match(/.js$/) && !path.match(IGNORE_RE)) { - // instrument JS files - return FS.read(path, "r", "utf8").then(function (original) { - var response = Q.defer(); - instrumenter.instrument(original, path, function (err, instrumented) { - if (err) { - response.reject(err); - return; - } - - response.resolve({ - status: 200, - headers: { - "content-type": "application/javascript", - "content-length": instrumented.length - }, - body: [instrumented], - file: path - }); - }); - return response.promise; - }); - } - - // otherwise just serve the file - return HttpApps.file(request, path, contentType, fs); - } - }); - }; -} else { - var fileTree = HttpApps.FileTree; -} - -var TESTS_FAILED = {}; -var POLL_TIME = 250; - -var phantom = spawn("phantomjs", ["--webdriver=127.0.0.1:8910"], { - stdio: "inherit" -}); - -var browser = wd.promiseRemote("127.0.0.1", 8910); - -var app = fileTree(Path.resolve(__dirname, "..")); -var app = HttpApps.Error(app, true); -var server = new Http.Server(app); - -server.listen(0).done(); - -var testPagePort = server.node.address().port; -var testPageUrl = "http://127.0.0.1:" + testPagePort + "/spec/index.html"; -console.log("Test page at " + testPageUrl); - -// wait for Ghost Driver to start running -Q.delay(2000) -.then(function () { - return browser.init(); -}) -.then(function () { - return browser.get(testPageUrl); -}) -.then(function () { - var done = Q.defer(); - - var poll = function() { - browser.execute("return typeof jsApiReporter !== 'undefined' ? jsApiReporter.finished : false").then(function (isFinished) { - if (isFinished) { - done.resolve(); - } else { - setTimeout(poll, POLL_TIME); - } - }, done.reject); - }; - poll(); - - return done.promise; -}) -.then(function () { - return browser.execute("return [jsApiReporter.suites(), jsApiReporter.results()]"); -}) -.spread(function (suites, results) { - var info = log(suites, results); - - if (info.failures.length) { - console.log("\nFailures:\n"); - console.log(info.failures.join("\n\n")); - } - - var msg = ''; - msg += info.specsCount + ' test' + ((info.specsCount === 1) ? '' : 's') + ', '; - msg += info.totalCount + ' assertion' + ((info.totalCount === 1) ? '' : 's') + ', '; - msg += info.failedCount + ' failure' + ((info.failedCount === 1) ? '' : 's'); - - console.log(); - console.log(msg); - - if (info.failures.length) { - throw TESTS_FAILED; - } -}) -.then(function () { - if (!COVERAGE) { - return; - } - - return browser.execute("return window.__coverage__") - .then(function (coverage) { - var reporter = istanbul.Report.create("lcov"); - var collector = new istanbul.Collector(); - - collector.add(coverage); - - console.log("Writing coverage reports."); - reporter.writeReport(collector); - }); -}) -.finally(function () { - server.stop(); -}) -.finally(function () { - return browser.quit(); -}) -.finally(function () { - phantom.kill(); -}) -.catch(function (err) { - if (err === TESTS_FAILED) { - process.exit(1); - } - throw err; -}) -.done(); - -function log(suites, results, name, info) { - name = name || ""; - info = info || {specsCount: 0, totalCount: 0, failedCount: 0, failures: []}; - - for (var i = 0, len = suites.length; i < len; i++) { - var suite = suites[i]; - if (suite.type === "spec") { - var result = results[suite.id]; - - info.specsCount++; - info.totalCount += result.messages.length; - if (result.result === "passed") { - util.print("."); - } else { - util.print("F"); - var msg = suite.name + "\n"; - for (var j = 0; j < result.messages.length; j++) { - var message = result.messages[j]; - if (message.passed_) continue; - info.failedCount++; - msg += "\t" + message.message + "\n"; - } - info.failures.push(msg); - } - } - - if (suite.children.length) { - log(suite.children, results, name + suite.name + " ", info); - } - } - - return info; -} - diff --git a/test/require-spec.js b/test/require-spec.js index d67aa3d6..124f84d9 100644 --- a/test/require-spec.js +++ b/test/require-spec.js @@ -29,7 +29,14 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -describe("Require", function () { +var Require = require("../require"); + +var location = __dirname; +if (Require.directoryPathToLocation) { + location = Require.directoryPathToLocation(location); +} + +describe("require", function () { [ "cyclic", "determinism", @@ -70,47 +77,32 @@ describe("Require", function () { "compiler-package", "translator-package", "redirect-patterns", - "main", - "json", - "optimizer", - {name: "optimizer", optimize: false} + "main" ].forEach(function (test) { - - if (typeof test === "string") test = {name: test}; - - if (test.optimize === undefined) test.optimize = true; - if (test.node === undefined) test.node = true; - if (test.browser === undefined) test.browser = true; - - if (!test.node && typeof process !== "undefined") return; - if (!test.browser && typeof window !== "undefined") return; - - it(test.name, function () { + if (typeof test === "object") { + if (test.node === false && typeof process !== "undefined") { + return; + } + test = test.name; + } + it(test, function () { var spec = this; var done; var message; - //console.log(test.name + ":", "START", test.optimize); + //console.log(test + ":", "START"); - return require.loadPackage(module.directory + test.name + "/", {}) - .then(function (preprocessorPackage) { - return require.loadPackage(module.directory + test.name + "/", { - preprocessorPackage: test.optimize ? preprocessorPackage : null - }) - }) + return Require.loadPackage(location + test + "/", {}) .then(function (pkg) { pkg.inject("test", { print: function (_message, level) { - //console.log(test.name + ":", _message); + //console.log(test + ":", _message); if (_message === "DONE") { message = _message; } }, assert: function (guard, message) { - //console.log(test.name + ":", guard ? "PASS" : "FAIL", message); - if (!guard && message) { - console.error("FAIL", message); - } + //console.log(test + ":", guard ? "PASS" : "FAIL", message); expect(!!guard).toBe(true); } }); @@ -119,10 +111,7 @@ describe("Require", function () { }) .then(function () { expect(message).toBe("DONE"); - }, function (reason) { - spec.fail(reason); }); - }); }); }); diff --git a/test/run.js b/test/run.js deleted file mode 100644 index 0ce40a4d..00000000 --- a/test/run.js +++ /dev/null @@ -1,27 +0,0 @@ - -require("./require-spec"); - -var jasmineEnv = jasmine.getEnv(); -jasmineEnv.updateInterval = 1000; - -var htmlReporter = new jasmine.HtmlReporter(); -this.jsApiReporter = new jasmine.JsApiReporter(); - -jasmineEnv.addReporter(htmlReporter); -jasmineEnv.addReporter(this.jsApiReporter); - -jasmineEnv.specFilter = function(spec) { - return htmlReporter.specFilter(spec); -}; - -var currentWindowOnload = window.onload; - -if (currentWindowOnload) { - currentWindowOnload(); -} -execJasmine(); - -function execJasmine() { - jasmineEnv.execute(); -} - From 89f7d606b19d3406ae7a1466f5af413cb53b0038 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:24:26 -0700 Subject: [PATCH 46/64] Fix adhoc boot --- adhoc.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adhoc.html b/adhoc.html index 82468731..76d64fda 100644 --- a/adhoc.html +++ b/adhoc.html @@ -1,7 +1,7 @@ - +

Open your browser's console.

From f9034f0e39877ef07c26066f279f7dd956b29f87 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:25:18 -0700 Subject: [PATCH 47/64] Move bin specific code into bin/mr --- bin/mr.js | 31 +++++++++++++++++++++++++++++-- require.js | 21 --------------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/bin/mr.js b/bin/mr.js index 9416c8f4..2d1fb839 100755 --- a/bin/mr.js +++ b/bin/mr.js @@ -1,2 +1,29 @@ -#!/usr/bin/env node --harmony_weakmaps --harmony_proxies -require("../node").boot().done(); +#!/usr/bin/env node --harmony + +var Require = require("../require"); +var Optimist = require("optimist"); +var URL = require("url"); +var Q = require("q"); +var FS = require("q-io/fs"); + +var argv = Optimist.argv; +var program = argv._.shift(); + +Require.findPackageLocationAndModuleId(program) +.then(function (info) { + return Require.loadPackage(info.location, { + }) + .invoke("async", info.id); +}, function (error) { + var location = Require.filePathToLocation(program); + var directory = URL.resolve(location, "./"); + var file = FS.relativeFromDirectory(directory, location); + var descriptions = {}; + descriptions[directory] = Q({}); + return Require.loadPackage(directory, { + descriptions: descriptions + }) + .invoke("async", file); +}) +.done(); + diff --git a/require.js b/require.js index aeac25f6..aa2230b3 100644 --- a/require.js +++ b/require.js @@ -19,27 +19,6 @@ module.exports = Require; Require.overlays = ["node"]; -Require.boot = function () { - var command = process.argv.slice(0, 3); - var args = process.argv.slice(2); - var program = args.shift(); - return Require.findPackageLocationAndModuleId(program) - .then(function (info) { - return Require.loadPackage(info.location) - .invoke("async", info.id); - }, function (error) { - var location = Require.filePathToLocation(program); - var directory = URL.resolve(location, "./"); - var file = Path.relative(directory, location); - var descriptions = {}; - descriptions[directory] = Q({}); - return Require.loadPackage(directory, { - descriptions: descriptions - }) - .invoke("async", file); - }); -}; - Require.getLocation = function getLocation() { return URL.resolve("file:///", process.cwd() + "/"); }; From a6194078eacf283aa0986023a564ebeea2b5c9b1 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:26:54 -0700 Subject: [PATCH 48/64] Misc bug fixes - Missing preload module. - Incorrect require id for preload. - Script name not explicit in params reader. --- boot/preload-entry.js | 2 +- boot/preload.js | 3 ++- boot/require.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/boot/preload-entry.js b/boot/preload-entry.js index 4bb3afcf..53c8eae7 100644 --- a/boot/preload-entry.js +++ b/boot/preload-entry.js @@ -1,5 +1,5 @@ -var boot = require("./browser"); +var boot = require("./require"); var preload = require("./preload"); var getParams = require("./script-params"); diff --git a/boot/preload.js b/boot/preload.js index 3c75edc6..878ef042 100644 --- a/boot/preload.js +++ b/boot/preload.js @@ -1,5 +1,6 @@ var load = require("../script"); +var URL = require("url"); var Q = require("q"); module.exports = function preload(plan, params) { @@ -20,7 +21,7 @@ module.exports = function preload(plan, params) { var preloaded = plan.reduce(function (previous, bundleLocations) { return previous.then(function () { return Q.all(bundleLocations.map(function (bundleLocation) { - load(resolve(params.location, bundleLocation)); + load(URL.resolve(params.location, bundleLocation)); return getHook(bundleLocation).promise; })); }); diff --git a/boot/require.js b/boot/require.js index b22d3702..bf7b74e2 100644 --- a/boot/require.js +++ b/boot/require.js @@ -7,7 +7,7 @@ var getParams = require("./script-params"); module.exports = boot; function boot(preloaded, params) { - params = params || getParams(scriptName); + params = params || getParams("boot.js"); var config = {preloaded: preloaded}; var applicationLocation = URL.resolve(window.location, params.package || "."); From 627c8fcf9111e23d992e1d29f8dfb8575b4f26f2 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:28:26 -0700 Subject: [PATCH 49/64] Improve display name of module factories The whole URL is cumbersome. Just use package name and module identifier. Take care to ensure that the display name is a valid JavaScript identifier, particularly if the package name starts with a number. --- browser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser.js b/browser.js index 96b32167..4d288545 100644 --- a/browser.js +++ b/browser.js @@ -85,7 +85,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { } var __FILE__String = "__FILE__", - DoubleUnderscoreString = "__", + Underscore = "_", globalEvalConstantA = "(function ", globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; @@ -108,7 +108,7 @@ Require.Compiler = function (config) { // TODO: investigate why this isn't working in Firebug. // 3. set displayName property on the factory function (Safari, Chrome) - var displayName = __FILE__String+module.location.replace(/\.\w+$|\W/g, DoubleUnderscoreString); + var displayName = (module.require.config.name + Underscore + module.id).replace(/[^\w\d]|^\d/g, Underscore); try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); From 7bbaf2f6d5ff4ed3ca8d8e25b14cba348be30943 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:30:50 -0700 Subject: [PATCH 50/64] Style dependencies reading code To break long line --- common.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common.js b/common.js index 8eb31c65..ac91e87c 100644 --- a/common.js +++ b/common.js @@ -720,7 +720,12 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; // dependencies, devDependencies if not in production, if not installed by NPM - [description.dependencies, description._id || description.production ? null : description.devDependencies] + [ + description.dependencies, + description._id || description.production ? + null : + description.devDependencies + ] .forEach(function (dependencies) { if (!dependencies) { return; From 4d2efd467edab5ca14bb457fa6f44660a84fc708 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 17:31:46 -0700 Subject: [PATCH 51/64] Expand informative properties of package.json Contributors, license, repository --- package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package.json b/package.json index 3b2adf9a..1a85cc81 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,18 @@ "modules", "loader" ], + "contributors": [ + "Tom Robinson", + "Kris Kowal (https://github.com/kriskowal)", + "Stuart Knightly" + ], + "license": { + "type": "BSD 3-clause" + }, + "repository": { + "type": "git", + "url": "git://github.com/montagejs/mr.git" + }, "main": "./require", "browser": { "require": "./browser", From 4cc684e8f5ae699b61356d3f6c1a48d2c58b58cd Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 23:43:58 -0700 Subject: [PATCH 52/64] Version 2.0.0 There be dragons. --- boot.js | 314 +++++++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 219 insertions(+), 97 deletions(-) diff --git a/boot.js b/boot.js index c0535bc1..6c6900ed 100644 --- a/boot.js +++ b/boot.js @@ -43,7 +43,7 @@ global = this; Module.prototype.bundle = bundle; return modules[0].getExports(); -})((function (global){return[["mr","boot/require",{"../require":10,"url":12,"q":14,"./script-params":9},function (require, exports, module){ +})((function (global){return[["mr","boot/require",{"../require":12,"url":14,"q":16,"./script-params":11},function (require, exports, module){ // mr boot/require // --------------- @@ -57,7 +57,7 @@ var getParams = require("./script-params"); module.exports = boot; function boot(preloaded, params) { - params = params || getParams(scriptName); + params = params || getParams("boot.js"); var config = {preloaded: preloaded}; var applicationLocation = URL.resolve(window.location, params.package || "."); @@ -350,7 +350,7 @@ function pow2AtLeast(n) { return n + 1; } -}],["collections","generic-collection",{"./shim-array":6},function (require, exports, module){ +}],["collections","generic-collection",{"./shim-array":7},function (require, exports, module){ // collections generic-collection // ------------------------------ @@ -461,7 +461,7 @@ GenericCollection.prototype.filter = function (callback /*, thisp*/) { var result = this.constructClone(); this.reduce(function (undefined, value, key, object, depth) { if (callback.call(thisp, value, key, object, depth)) { - result.add(value); + result.add(value, key); } }, undefined); return result; @@ -616,7 +616,7 @@ GenericCollection.prototype.only = function () { require("./shim-array"); -}],["collections","generic-order",{"./shim-object":8},function (require, exports, module){ +}],["collections","generic-order",{"./shim-object":9},function (require, exports, module){ // collections generic-order // ------------------------- @@ -676,7 +676,7 @@ GenericOrder.prototype.compare = function (that, compare) { return comparison; }; -}],["collections","iterator",{"./weak-map":15,"./generic-collection":3},function (require, exports, module){ +}],["collections","iterator",{"./weak-map":17,"./generic-collection":3},function (require, exports, module){ // collections iterator // -------------------- @@ -1224,7 +1224,18 @@ Iterator.Iteration = Iteration; Iterator.DoneIteration = DoneIteration; Iterator.done = new DoneIteration(); -}],["collections","shim-array",{"./shim-function":7,"./generic-collection":3,"./generic-order":4,"./iterator":5,"weak-map":15},function (require, exports, module){ +}],["collections","shim",{"./shim-array":7,"./shim-object":9,"./shim-function":8,"./shim-regexp":10},function (require, exports, module){ + +// collections shim +// ---------------- + + +var Array = require("./shim-array"); +var Object = require("./shim-object"); +var Function = require("./shim-function"); +var RegExp = require("./shim-regexp"); + +}],["collections","shim-array",{"./shim-function":8,"./generic-collection":3,"./generic-order":4,"./iterator":5,"weak-map":17},function (require, exports, module){ // collections shim-array // ---------------------- @@ -1330,8 +1341,14 @@ define("get", function (index, defaultValue) { }); define("set", function (index, value) { - this.splice(index, 1, value); - return true; + if (index < this.length) { + this.splice(index, 1, value); + } else { + // Must use swap instead of splice, dispite the unfortunate array + // argument, because splice would truncate index to length. + this.swap(index, 1, [value]); + } + return this; }); define("add", function (value) { @@ -1370,48 +1387,101 @@ define("findLastValue", function (value, equals) { return -1; }); -define("swap", function (start, length, plus) { - var args, plusLength, i, j, returnValue; - if (typeof plus !== "undefined") { - args = [start, length]; +define("swap", function (start, minusLength, plus) { + // Unrolled implementation into JavaScript for a couple reasons. + // Calling splice can cause large stack sizes for large swaps. Also, + // splice cannot handle array holes. + if (plus) { if (!Array.isArray(plus)) { plus = array_slice.call(plus); } - i = 0; - plusLength = plus.length; - // 1000 is a magic number, presumed to be smaller than the remaining - // stack length. For swaps this small, we take the fast path and just - // use the underlying Array splice. We could measure the exact size of - // the remaining stack using a try/catch around an unbounded recursive - // function, but this would defeat the purpose of short-circuiting in - // the common case. - if (plusLength < 1000) { - for (i; i < plusLength; i++) { - args[i+2] = plus[i]; + } else { + plus = Array.empty; + } + + if (start < 0) { + start = this.length + start; + } else if (start > this.length) { + this.length = start; + } + + if (start + minusLength > this.length) { + // Truncate minus length if it extends beyond the length + minusLength = this.length - start; + } else if (minusLength < 0) { + // It is the JavaScript way. + minusLength = 0; + } + + var diff = plus.length - minusLength; + var oldLength = this.length; + var newLength = this.length + diff; + + if (diff > 0) { + // Head Tail Plus Minus + // H H H H M M T T T T + // H H H H P P P P T T T T + // ^ start + // ^-^ minus.length + // ^ --> diff + // ^-----^ plus.length + // ^------^ tail before + // ^------^ tail after + // ^ start iteration + // ^ start iteration offset + // ^ end iteration + // ^ end iteration offset + // ^ start + minus.length + // ^ length + // ^ length - 1 + for (var index = oldLength - 1; index >= start + minusLength; index--) { + var offset = index + diff; + if (index in this) { + this[offset] = this[index]; + } else { + // Oddly, PhantomJS complains about deleting array + // properties, unless you assign undefined first. + this[offset] = void 0; + delete this[offset]; } - return array_splice.apply(this, args); + } + } + for (var index = 0; index < plus.length; index++) { + if (index in plus) { + this[start + index] = plus[index]; } else { - // Avoid maximum call stack error. - // First delete the desired entries. - returnValue = array_splice.apply(this, args); - // Second batch in 1000s. - for (i; i < plusLength;) { - args = [start+i, 0]; - for (j = 2; j < 1002 && i < plusLength; j++, i++) { - args[j] = plus[i]; - } - array_splice.apply(this, args); + this[start + index] = void 0; + delete this[start + index]; + } + } + if (diff < 0) { + // Head Tail Plus Minus + // H H H H M M M M T T T T + // H H H H P P T T T T + // ^ start + // ^-----^ length + // ^-^ plus.length + // ^ start iteration + // ^ offset start iteration + // ^ end + // ^ offset end + // ^ start + minus.length - plus.length + // ^ start - diff + // ^------^ tail before + // ^------^ tail after + // ^ length - diff + // ^ newLength + for (var index = start + plus.length; index < oldLength - diff; index++) { + var offset = index - diff; + if (offset in this) { + this[index] = this[offset]; + } else { + this[index] = void 0; + delete this[index]; } - return returnValue; } - // using call rather than apply to cut down on transient objects - } else if (typeof length !== "undefined") { - return array_splice.call(this, start, length); - } else if (typeof start !== "undefined") { - return array_splice.call(this, start); - } else { - return []; } + this.length = newLength; }); define("peek", function () { @@ -1608,7 +1678,7 @@ Function.get = function (key) { }; }; -}],["collections","shim-object",{"weak-map":15},function (require, exports, module){ +}],["collections","shim-object",{"weak-map":17},function (require, exports, module){ // collections shim-object // ----------------------- @@ -1962,9 +2032,9 @@ Object.equals = function (a, b, equals, memo) { if (Object.isObject(a)) { memo = memo || new WeakMap(); if (memo.has(a)) { - return memo.get(a) === b; + return true; } - memo.set(a, b); + memo.set(a, true); } if (Object.isObject(a) && typeof a.equals === "function") { return a.equals(b, equals, memo); @@ -2047,19 +2117,15 @@ Object.compare = function (a, b) { return 0; var aType = typeof a; var bType = typeof b; - if (aType !== bType) - return 0; - if (a == null) - return b == null ? 0 : -1; - if (aType === "number") + if (aType === "number" && bType === "number") return a - b; - if (aType === "string") - return a < b ? -1 : 1; + if (aType === "string" && bType === "string") + return a < b ? -Infinity : Infinity; // the possibility of equality elimiated above - if (typeof a.compare === "function") + if (a && typeof a.compare === "function") return a.compare(b); // not commutative, the relationship is reversed - if (typeof b.compare === "function") + if (b && typeof b.compare === "function") return -b.compare(a); return 0; }; @@ -2089,7 +2155,9 @@ Object.clone = function (value, depth, memo) { } else if (depth === 0) { return value; } - if (Object.isObject(value)) { + if (typeof value === "function") { + return value; + } else if (Object.isObject(value)) { if (!memo.has(value)) { if (value && typeof value.clone === "function") { memo.set(value, value.clone(depth, memo)); @@ -2132,7 +2200,26 @@ Object.clear = function (object) { return object; }; -}],["mr","boot/script-params",{"url":12},function (require, exports, module){ +}],["collections","shim-regexp",{},function (require, exports, module){ + +// collections shim-regexp +// ----------------------- + + +/** + accepts a string; returns the string with regex metacharacters escaped. + the returned string can safely be used within a regex to match a literal + string. escaped characters are [, ], {, }, (, ), -, *, +, ?, ., \, ^, $, + |, #, [comma], and whitespace. +*/ +if (!RegExp.escape) { + var special = /[-[\]{}()*+?.\\^$|,#\s]/g; + RegExp.escape = function (string) { + return string.replace(special, "\\$&"); + }; +} + +}],["mr","boot/script-params",{"url":14},function (require, exports, module){ // mr boot/script-params // --------------------- @@ -2202,7 +2289,7 @@ function getParams(scriptName) { return params; } -}],["mr","browser",{"./common":11,"url":12,"q":14,"./script":13},function (require, exports, module){ +}],["mr","browser",{"./common":13,"url":14,"q":16,"./script":15},function (require, exports, module){ // mr browser // ---------- @@ -2294,7 +2381,7 @@ if (global.navigator && global.navigator.userAgent.indexOf("Firefox") >= 0) { } var __FILE__String = "__FILE__", - DoubleUnderscoreString = "__", + Underscore = "_", globalEvalConstantA = "(function ", globalEvalConstantB = "(require, exports, module, __filename, __dirname) {", globalEvalConstantC = "//*/\n})\n//@ sourceURL="; @@ -2317,7 +2404,7 @@ Require.Compiler = function (config) { // TODO: investigate why this isn't working in Firebug. // 3. set displayName property on the factory function (Safari, Chrome) - var displayName = __FILE__String+module.location.replace(/\.\w+$|\W/g, DoubleUnderscoreString); + var displayName = (module.require.config.name + Underscore + module.id).replace(/[^\w\d]|^\d/g, Underscore); try { module.factory = globalEval(globalEvalConstantA+displayName+globalEvalConstantB+module.text+globalEvalConstantC+module.location); @@ -2440,7 +2527,7 @@ function loadIfNotPreloaded(location, definition, preloaded) { } } -}],["mr","common",{"q":14,"url":12},function (require, exports, module){ +}],["mr","common",{"q":16,"url":14},function (require, exports, module){ // mr common // --------- @@ -3167,7 +3254,12 @@ function configurePackage(location, description, parent) { // mappings, link this package to other packages. var mappings = description.mappings || {}; // dependencies, devDependencies if not in production, if not installed by NPM - [description.dependencies, description._id || description.production ? null : description.devDependencies] + [ + description.dependencies, + description._id || description.production ? + null : + description.devDependencies + ] .forEach(function (dependencies) { if (!dependencies) { return; @@ -3551,9 +3643,9 @@ function load(location) { }; script.defer = true; head.appendChild(script); -}; +} -}],["q","q",{"asap":1,"collections/weak-map":15,"collections/iterator":5},function (require, exports, module){ +}],["q","q",{"collections/shim":6,"collections/weak-map":17,"collections/iterator":5,"asap":1},function (require, exports, module){ // q q // --- @@ -3600,9 +3692,10 @@ try { var qStartingLine = captureLine(); var qFileName; -var asap = require("asap"); +require("collections/shim"); var WeakMap = require("collections/weak-map"); var Iterator = require("collections/iterator"); +var asap = require("asap"); function isObject(value) { return value === Object(value); @@ -3636,6 +3729,9 @@ function makeStackTraceLong(error, promise) { } function filterStackString(stackString) { + if (Q.isIntrospective) { + return stackString; + } var lines = stackString.split("\n"); var desiredLines = []; for (var i = 0; i < lines.length; ++i) { @@ -4087,15 +4183,22 @@ Q.promised = function Q_promised(callback) { }; }; -// XXX experimental. This method is a way to denote that a local value is -// serializable and should be immediately dispatched to a remote upon request, -// instead of passing a reference. -Q.passByCopy = function Q_passByCopy(object) { - //freeze(object); - //passByCopies.set(object, true); - return object; +/** + */ +Q.passByCopy = // TODO XXX experimental +Q.push = function (value) { + if (Object(value) === value && !Q_isPromise(value)) { + passByCopies.set(value, true); + } + return value; }; +Q.isPortable = function (value) { + return Object(value) === value && passByCopies.has(value); +}; + +var passByCopies = new WeakMap(); + /** * The async function is a decorator for generator functions, turning * them into asynchronous generators. Although generators are only @@ -4128,16 +4231,16 @@ function Q_async(makeGenerator) { // when verb is "send", arg is a value // when verb is "throw", arg is an exception function continuer(verb, arg) { - var result; + var iteration; try { - result = generator[verb](arg); + iteration = generator[verb](arg); } catch (exception) { return Q_reject(exception); } - if (result.done) { - return result.value; + if (iteration.done) { + return Q(iteration.value); } else { - return Q(result.value).then(callback, errback); + return Q(iteration.value).then(callback, errback); } } var generator = makeGenerator.apply(this, arguments); @@ -4473,14 +4576,17 @@ Promise.prototype.catch = function Promise_catch(rejected) { * TODO */ Promise.prototype.finally = function Promise_finally(callback, ms) { + if (!callback) { + return this; + } callback = Q(callback); return this.then(function (value) { - return callback.call().then(function () { + return callback.call().then(function Promise_finally_fulfilled() { return value; }); }, function (reason) { // TODO attempt to recycle the rejection with "this". - return callback.call().then(function () { + return callback.call().then(function Promise_finally_rejected() { throw reason; }); }, ms); @@ -4506,11 +4612,17 @@ Promise.prototype.getEstimate = function Promise_getEstimate() { */ Promise.prototype.dispatch = function Promise_dispatch(op, args) { var deferred = defer(); + this.rawDispatch(deferred.resolve, op, args); + return deferred.promise; +}; + +/** + */ +Promise.prototype.rawDispatch = function Promise_rawDispatch(resolve, op, args) { var self = this; asap(function Promise_dispatch_task() { - Q_inspect(self).dispatch(deferred.resolve, op, args); + Q_inspect(self).dispatch(resolve, op, args); }); - return deferred.promise; }; /** @@ -4635,22 +4747,11 @@ Promise.prototype.delay = function Promise_delay(ms) { }, null, ms); }; -/** - * TODO - */ -Promise.prototype.push = function Promise_push() { -}; - /** * TODO */ Promise.prototype.pull = function Promise_pull() { -}; - -/** - * TODO - */ -Promise.prototype.defend = function Promise_defend() { + return this.dispatch("pull", []); }; @@ -4665,8 +4766,15 @@ function Deferred(promise) { // different promise (as it is in a Queue), but the intrinsic promise does // not change. promises.set(this, promise); - this.resolve = this.resolve.bind(this); - this.reject = this.reject.bind(this); + var self = this; + var resolve = this.resolve; + this.resolve = function (value) { + resolve.call(self, value); + }; + var reject = this.reject; + this.reject = function (error) { + reject.call(self, error); + }; } /** @@ -4736,7 +4844,8 @@ Fulfilled.prototype.dispatch = function Fulfilled_dispatch( op === "call" || op === "invoke" || op === "keys" || - op === "iterate" + op === "iterate" || + op === "pull" ) { try { result = this[op].apply(this, operands); @@ -4782,6 +4891,19 @@ Fulfilled.prototype.iterate = function Fulfilled_iterate() { return new Iterator(this.value); }; +Fulfilled.prototype.pull = function Fulfilled_pull() { + var result; + if (Object(this.value) === this.value) { + result = Array.isArray(this.value) ? [] : {}; + for (var name in this.value) { + result[name] = this.value[name]; + } + } else { + result = this.value; + } + return Q.push(result); +}; + function Rejected(reason) { this.reason = reason; diff --git a/package.json b/package.json index 1a85cc81..00b68f03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mr", - "version": "0.13.4", + "version": "2.0.0", "description": "A refresh-only CommonJS module system for browsers, used in Montage", "keywords": [ "montage", From e6d317a4f20d3572be9709a0d9f82b3a040ccf54 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 19 Mar 2014 23:53:57 -0700 Subject: [PATCH 53/64] Fix Jasminum dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 00b68f03..9e7e7b2f 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "joey": "^2.0.0", "q-io": "^2.0.0", "jshint": "^2.3.0", - "jasminum": "^0.0.0", + "jasminum": "^2.0.0", "websocket.io": "^0.2.1", "mop-integration": "git://github.com/montagejs/mop-integration.git#master" }, From 8749c5f8c61d50708b33c0c9b1423357217a849d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 20 Mar 2014 13:05:03 -0700 Subject: [PATCH 54/64] Version 2.0.1 Synchronize dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9e7e7b2f..a15ba53e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mr", - "version": "2.0.0", + "version": "2.0.1", "description": "A refresh-only CommonJS module system for browsers, used in Montage", "keywords": [ "montage", @@ -41,7 +41,7 @@ "mrs": "bin/mrs.js" }, "dependencies": { - "q": "^2.0.0", + "q": "^2.0.1", "optimist": "^0.6.1", "util": "^0.10.3", "colors": "^0.6.2", @@ -50,10 +50,10 @@ "devDependencies": { "qs": "^0.6.5", "wd": "^0.1.5", - "joey": "^2.0.0", - "q-io": "^2.0.0", + "joey": "^2.0.1", + "q-io": "^2.0.2", "jshint": "^2.3.0", - "jasminum": "^2.0.0", + "jasminum": "^2.0.1", "websocket.io": "^0.2.1", "mop-integration": "git://github.com/montagejs/mop-integration.git#master" }, From e55c4730f08c47b4305936e6843e8e14d139a892 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 18 May 2014 20:48:24 -0700 Subject: [PATCH 55/64] Fix Mr/Jasminum/PhantomJS tests By upgrading to latest Jasminum and accounting for the cyclic dependency of Jasminum's PhantomJS runner upon Mr. --- .gitignore | 18 +++++++++++++++++- boot.js | 29 ++++++++++++++++++++++++++--- common.js | 6 +++--- mini-url.js | 23 +++++++++++++++++++++++ node_modules/mr | 1 + package.json | 29 ++++++++++++++++------------- require.js | 4 ++++ 7 files changed, 90 insertions(+), 20 deletions(-) create mode 120000 node_modules/mr diff --git a/.gitignore b/.gitignore index 3328b3fb..94f770da 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,20 @@ .DS_Store lcov.info lcov-report/ -/node_modules +# It is necessary to explicate the ignorable node_modules because +# node_modules/mr is a checked-in symlink, necessary for resolving the +# Jasminum/PhantomJS cyclic dependency upon Mr. +/node_modules/.bin +/node_modules/colors +/node_modules/istanbul +/node_modules/jasminum +/node_modules/joey +/node_modules/jshint +/node_modules/mop-integration +/node_modules/optimist +/node_modules/q-io +/node_modules/q +/node_modules/qs +/node_modules/util +/node_modules/wd +/node_modules/websocket.io diff --git a/boot.js b/boot.js index 6c6900ed..89570ad7 100644 --- a/boot.js +++ b/boot.js @@ -3089,7 +3089,7 @@ Require.loadPackage = function (dependency, config) { loadedPackages[location] = pkg; return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { var dependency = subconfig.mappings[prefix]; - return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + return config.loadPackage(dependency, subconfig, loading); })) .then(function () { postConfigurePackage(subconfig, packageDescription); @@ -3239,7 +3239,7 @@ function configurePackage(location, description, parent) { } - //Deal with redirects + // Deal with redirects var redirects = description.redirects; if (redirects !== void 0) { Object.keys(redirects).forEach(function (name) { @@ -3278,7 +3278,7 @@ function configurePackage(location, description, parent) { }); // mappings Object.keys(mappings).forEach(function (name) { - var mapping = mappings[name] = normalizeDependency( + mappings[name] = normalizeDependency( mappings[name], config, name @@ -3596,6 +3596,29 @@ function memoize(callback, cache) { // ----------- +// This is the browser implementation for "mr/url", +// redirected from "url" within the Mr package by the Montage Require +// loader because of the "browser" redirects in package.json. + +// This is a very small subset of the Node.js URL module, suitable only for +// resolving relative module identifiers relative to fully qualified base +// URL’s. +// Because Montage Require only needs this part of the URL module, a +// very compact implementation is possible, teasing the necessary behavior out +// of the browser's own URL resolution mechanism, even though at time of +// writing, browsers do not provide an explicit JavaScript interface. + +// The implementation takes advantage of the "href" getter/setter on an "a" +// (anchor) tag in the presence of a "base" tag on the document. +// We either use an existing "base" tag or temporarily introduce a fake +// "base" tag into the header of the page. +// We then temporarily modify the "href" of the base tag to be the base URL +// for the duration of a call to URL.resolve, to be the base URL argument. +// We then apply the relative URL to the "href" setter of an anchor tag, +// and read back the absolute URL from the "href" getter. +// The browser guarantees that the "href" property will report the fully +// qualified URL relative to the page's location, albeit its "base" location. + var head = document.querySelector("head"), baseElement = document.createElement("base"), relativeElement = document.createElement("a"); diff --git a/common.js b/common.js index ac91e87c..1708e518 100644 --- a/common.js +++ b/common.js @@ -555,7 +555,7 @@ Require.loadPackage = function (dependency, config) { loadedPackages[location] = pkg; return Q.all(Object.keys(subconfig.mappings).map(function (prefix) { var dependency = subconfig.mappings[prefix]; - return config.loadPackage(subconfig.mappings[prefix], subconfig, loading); + return config.loadPackage(dependency, subconfig, loading); })) .then(function () { postConfigurePackage(subconfig, packageDescription); @@ -705,7 +705,7 @@ function configurePackage(location, description, parent) { } - //Deal with redirects + // Deal with redirects var redirects = description.redirects; if (redirects !== void 0) { Object.keys(redirects).forEach(function (name) { @@ -744,7 +744,7 @@ function configurePackage(location, description, parent) { }); // mappings Object.keys(mappings).forEach(function (name) { - var mapping = mappings[name] = normalizeDependency( + mappings[name] = normalizeDependency( mappings[name], config, name diff --git a/mini-url.js b/mini-url.js index 88992ea6..c3dedbec 100644 --- a/mini-url.js +++ b/mini-url.js @@ -1,4 +1,27 @@ +// This is the browser implementation for "mr/url", +// redirected from "url" within the Mr package by the Montage Require +// loader because of the "browser" redirects in package.json. + +// This is a very small subset of the Node.js URL module, suitable only for +// resolving relative module identifiers relative to fully qualified base +// URL’s. +// Because Montage Require only needs this part of the URL module, a +// very compact implementation is possible, teasing the necessary behavior out +// of the browser's own URL resolution mechanism, even though at time of +// writing, browsers do not provide an explicit JavaScript interface. + +// The implementation takes advantage of the "href" getter/setter on an "a" +// (anchor) tag in the presence of a "base" tag on the document. +// We either use an existing "base" tag or temporarily introduce a fake +// "base" tag into the header of the page. +// We then temporarily modify the "href" of the base tag to be the base URL +// for the duration of a call to URL.resolve, to be the base URL argument. +// We then apply the relative URL to the "href" setter of an anchor tag, +// and read back the absolute URL from the "href" getter. +// The browser guarantees that the "href" property will report the fully +// qualified URL relative to the page's location, albeit its "base" location. + var head = document.querySelector("head"), baseElement = document.createElement("base"), relativeElement = document.createElement("a"); diff --git a/node_modules/mr b/node_modules/mr new file mode 120000 index 00000000..a96aa0ea --- /dev/null +++ b/node_modules/mr @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/package.json b/package.json index a15ba53e..e7682bf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "mr", "version": "2.0.1", + "publishConfig": { + "tag": "future" + }, "description": "A refresh-only CommonJS module system for browsers, used in Montage", "keywords": [ "montage", @@ -31,7 +34,7 @@ "build": "node bin/mrs.js boot/require.js -e '().done()' > boot.js", "test": "npm run build && npm run test:node && npm run test:phantom && npm run lint", "test:node": "jasminum test", - "test:phantom": "jasminum-phantom test", + "test:phantom": "(cd node_modules/mr && jasminum-phantom test)", "lint": "jshint .", "integration": "MOP_VERSION=^0.13 MR_VERSION=. mop-integration" }, @@ -41,20 +44,20 @@ "mrs": "bin/mrs.js" }, "dependencies": { - "q": "^2.0.1", - "optimist": "^0.6.1", - "util": "^0.10.3", - "colors": "^0.6.2", - "istanbul": "^0.1.34" + "q": ">=2.0.1 <3.0.0", + "optimist": ">=0.6.1 <0.7.0", + "util": ">=0.10.3 <0.11.0", + "colors": ">=0.6.2 <0.7.0", + "istanbul": ">=0.1.34 <0.2.0" }, "devDependencies": { - "qs": "^0.6.5", - "wd": "^0.1.5", - "joey": "^2.0.1", - "q-io": "^2.0.2", - "jshint": "^2.3.0", - "jasminum": "^2.0.1", - "websocket.io": "^0.2.1", + "qs": "0.6.6", + "wd": ">=0.1.5 < 0.2.0", + "joey": ">=2.0.1 <3.0.0", + "q-io": ">=2.0.2 <3.0.0", + "jshint": ">=2.3.0 <3.0.0", + "jasminum": ">=2.0.4 <3.0.0", + "websocket.io": ">=0.2.1 <0.3.0", "mop-integration": "git://github.com/montagejs/mop-integration.git#master" }, "exclude": [ diff --git a/require.js b/require.js index aa2230b3..57c54b99 100644 --- a/require.js +++ b/require.js @@ -7,6 +7,10 @@ /*global -URL */ /*jshint node:true */ +// This is the Node.js implementation for "mr". +// For browsers, this module identifier is redirected to browser.js by +// package.json. + var Require = require("./common"); var Q = require("q"); var FS = require("fs"); From aa2e0fc9ef0ee1ea9d9cb60a7d5053db8c546463 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 18 May 2014 20:51:58 -0700 Subject: [PATCH 56/64] 2.0.2 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e7682bf6..9a86036f 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "mr", - "version": "2.0.1", + "version": "2.0.2", "publishConfig": { - "tag": "future" + "tag": "future" }, "description": "A refresh-only CommonJS module system for browsers, used in Montage", "keywords": [ From bf7549238e35820b68ce996aae1fbaf1587105d9 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 18 May 2014 21:26:41 -0700 Subject: [PATCH 57/64] Fixes for booting Addresses problems with finding "mr", just as "q", when they are dependencies of the application package. --- boot.js | 5 +++-- boot/require.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/boot.js b/boot.js index 89570ad7..f7f60ef5 100644 --- a/boot.js +++ b/boot.js @@ -75,9 +75,10 @@ function boot(preloaded, params) { }) .then(function (applicationRequire) { return applicationRequire.loadPackage({ - location: params.location, + name: "mr", + location: params.mrLocation, hash: params.mrHash - }, config) + }) .then(function (mrRequire) { return mrRequire.loadPackage({ name: "q", diff --git a/boot/require.js b/boot/require.js index bf7b74e2..3191e337 100644 --- a/boot/require.js +++ b/boot/require.js @@ -25,9 +25,10 @@ function boot(preloaded, params) { }) .then(function (applicationRequire) { return applicationRequire.loadPackage({ - location: params.location, + name: "mr", + location: params.mrLocation, hash: params.mrHash - }, config) + }) .then(function (mrRequire) { return mrRequire.loadPackage({ name: "q", From 48621783fd6a667c83e9844864d9c1a875775788 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 18 May 2014 21:27:32 -0700 Subject: [PATCH 58/64] 2.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a86036f..3fa7f1cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mr", - "version": "2.0.2", + "version": "2.0.3", "publishConfig": { "tag": "future" }, From a8f6e635550fbbddd2ea050ac914890efa7ba655 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 5 Aug 2014 09:13:49 -0700 Subject: [PATCH 59/64] Eliminate shared module cache for builder And use strict mode for builder --- build.js | 32 +++++++++++++++----------------- common.js | 5 +++-- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/build.js b/build.js index ed84b2e9..520f68c7 100644 --- a/build.js +++ b/build.js @@ -1,3 +1,4 @@ +//"use strict"; var Q = require("q"); var FS = require("fs"); @@ -10,37 +11,34 @@ module.exports = build; function build(path) { return Require.findPackageLocationAndModuleId(path) .then(function (arg) { - var cache = {}; return Q.try(function () { return Require.loadPackage(arg.location, { overlays: ["node"], - cache: cache, production: false }); }) .then(function (preprocessorPackage) { return Require.loadPackage(arg.location, { overlays: ["browser"], - cache: cache, production: true, preprocessorPackage: preprocessorPackage }); }) - .then(function (package) { - return package.deepLoad(arg.id) - .thenResolve(package); + .then(function (pkg) { + return pkg.deepLoad(arg.id) + .thenResolve(pkg); }); }) - .then(function (package) { + .then(function (pkg) { var bundle = []; - var packages = package.packages; + var pkgs = pkg.packages; // Ensure that the entry point comes first in the bundle - for (var location in packages) { - if (Object.prototype.hasOwnProperty.call(packages, location)) { - package = packages[location]; - var modules = package.modules; + for (var location in pkgs) { + if (Object.prototype.hasOwnProperty.call(pkgs, location)) { + pkg = pkgs[location]; + var modules = pkg.modules; for (var id in modules) { if (Object.prototype.hasOwnProperty.call(modules, id)) { var module = modules[id]; @@ -57,13 +55,13 @@ function build(path) { // Otherwise, ensure that the modules are in lexicographic order to // ensure that each build from the same sources is consistent. - Object.keys(packages).sort(function (a, b) { - a = packages[a].config.name || a; - b = packages[b].config.name || b; + Object.keys(pkgs).sort(function (a, b) { + a = pkgs[a].config.name || a; + b = pkgs[b].config.name || b; return a === b ? 0 : a < b ? -1 : 1; }).forEach(function (location) { - var package = packages[location]; - var modules = package.modules; + var pkg = pkgs[location]; + var modules = pkg.modules; Object.keys(modules).sort().forEach(function (id) { var module = modules[id]; if (module.error) { diff --git a/common.js b/common.js index 1708e518..8e1e49b2 100644 --- a/common.js +++ b/common.js @@ -63,13 +63,14 @@ Require.makeRequire = function (config) { } else { type = "js"; } - modules[lookupId] = { + var module = { id: id, extension: extension, type: type, display: (config.name || config.location) + "#" + id, require: makeRequire(id) }; + modules[lookupId] = module; } return modules[lookupId]; } @@ -100,7 +101,7 @@ Require.makeRequire = function (config) { module.exports === void 0 && module.redirect === void 0 ) { - return Q(config.load).call(void 0, topId, module); + return config.load(topId, module); } }) .then(function () { From bbd989184960a83deb3e68c822b1832a96b797ab Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 5 Aug 2014 09:14:40 -0700 Subject: [PATCH 60/64] 2.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fa7f1cc..65c7adad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mr", - "version": "2.0.3", + "version": "2.0.4", "publishConfig": { "tag": "future" }, From 975b7940a8ce786b023e1a27eddf42d94aa73e1f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 5 Sep 2014 11:06:02 -0400 Subject: [PATCH 61/64] Bind viaId on require.lookup --- common.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common.js b/common.js index 8e1e49b2..6139343e 100644 --- a/common.js +++ b/common.js @@ -360,7 +360,7 @@ Require.makeRequire = function (config) { // Asynchronous "require.async()" which ensures async executation // (even with synchronous loaders) - require.async = function(id) { + require.async = function (id) { var topId = resolve(id, viaId); var module = getModuleDescriptor(id); return deepLoad(topId, viaId) @@ -369,13 +369,16 @@ Require.makeRequire = function (config) { }); }; + require.lookup = function (id) { + return lookup(id, viaId); + }; + require.resolve = function (id) { return normalize(resolve(id, viaId)); }; require.getModule = getModuleDescriptor; // XXX deprecated, use: require.getModuleDescriptor = getModuleDescriptor; - require.lookup = lookup; require.load = load; require.deepLoad = deepLoad; From cc2aa08354de6eb8c25a7794f162998e0f85153d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 5 Sep 2014 11:35:00 -0400 Subject: [PATCH 62/64] Factor merge and identifier modules --- boot.js | 627 +++++++++++++++++++++++--------------------------- common.js | 128 +++-------- identifier.js | 45 ++++ merge.js | 23 ++ 4 files changed, 391 insertions(+), 432 deletions(-) create mode 100644 identifier.js create mode 100644 merge.js diff --git a/boot.js b/boot.js index f7f60ef5..c9f3e1f5 100644 --- a/boot.js +++ b/boot.js @@ -43,7 +43,7 @@ global = this; Module.prototype.bundle = bundle; return modules[0].getExports(); -})((function (global){return[["mr","boot/require",{"../require":12,"url":14,"q":16,"./script-params":11},function (require, exports, module){ +})((function (global){return[["mr","boot/require",{"../require":11,"url":15,"q":17,"./script-params":10},function (require, exports, module){ // mr boot/require // --------------- @@ -96,130 +96,100 @@ function boot(preloaded, params) { } -}],["asap","asap",{"./queue":2},function (require, exports, module){ +}],["asap","asap",{},function (require, exports, module){ // asap asap // --------- -"use strict"; // Use the fastest possible means to execute a task in a future turn // of the event loop. -// Queue is a circular buffer with good locality of reference and doesn't -// allocate new memory unless there are more than `InitialCapacity` parallel -// tasks in which case it will resize itself generously to x8 more capacity. -// The use case of asap should require no or few amount of resizes during -// runtime. -// Calling a task frees a slot immediately so if the calling -// has a side effect of queuing itself again, it can be sustained -// without additional memory -// Queue specifically uses -// http://en.wikipedia.org/wiki/Circular_buffer#Use_a_Fill_Count -// Because: -// 1. We need fast .length operation, since queue -// could have changed after every iteration -// 2. Modulus can be negated by using power-of-two -// capacities and replacing it with bitwise AND -// 3. It will not be used in a multi-threaded situation. - -var Queue = require("./queue"); - -//1024 = InitialCapacity -var queue = new Queue(1024); +// linked list of tasks (single, with head node) +var head = {task: void 0, next: null}; +var tail = head; var flushing = false; var requestFlush = void 0; -var hasSetImmediate = typeof setImmediate === "function"; -var domain; - -// Avoid shims from browserify. -// The existence of `global` in browsers is guaranteed by browserify. -var process = global.process; - -// Note that some fake-Node environments, -// like the Mocha test runner, introduce a `process` global. -var isNodeJS = !!process && ({}).toString.call(process) === "[object process]"; +var isNodeJS = false; function flush() { /* jshint loopfunc: true */ - while (queue.length > 0) { - var task = queue.shift(); + while (head.next) { + head = head.next; + var task = head.task; + head.task = void 0; + var domain = head.domain; + + if (domain) { + head.domain = void 0; + domain.enter(); + } try { - task.call(); + task(); } catch (e) { if (isNodeJS) { // In node, uncaught exceptions are considered fatal errors. - // Re-throw them to interrupt flushing! + // Re-throw them synchronously to interrupt flushing! - // Ensure continuation if an uncaught exception is suppressed - // listening process.on("uncaughtException") or domain("error"). - requestFlush(); + // Ensure continuation if the uncaught exception is suppressed + // listening "uncaughtException" events (as domains does). + // Continue in next event to avoid tick recursion. + if (domain) { + domain.exit(); + } + setTimeout(flush, 0); + if (domain) { + domain.enter(); + } throw e; } else { // In browsers, uncaught exceptions are not fatal. // Re-throw them asynchronously to avoid slow-downs. - setTimeout(function () { - throw e; + setTimeout(function() { + throw e; }, 0); } } + + if (domain) { + domain.exit(); + } } flushing = false; } -if (isNodeJS) { - // Node.js - requestFlush = function () { - // Ensure flushing is not bound to any domain. - var currentDomain = process.domain; - if (currentDomain) { - domain = domain || (1,require)("domain"); - domain.active = process.domain = null; - } +if (typeof process !== "undefined" && process.nextTick) { + // Node.js before 0.9. Note that some fake-Node environments, like the + // Mocha test runner, introduce a `process` global without a `nextTick`. + isNodeJS = true; - // Avoid tick recursion - use setImmediate if it exists. - if (flushing && hasSetImmediate) { - setImmediate(flush); - } else { - process.nextTick(flush); - } - - if (currentDomain) { - domain.active = process.domain = currentDomain; - } - }; - -} else if (hasSetImmediate) { - // In IE10, or https://github.com/NobleJS/setImmediate requestFlush = function () { - setImmediate(flush); + process.nextTick(flush); }; +} else if (typeof setImmediate === "function") { + // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate + if (typeof window !== "undefined") { + requestFlush = setImmediate.bind(window, flush); + } else { + requestFlush = function () { + setImmediate(flush); + }; + } + } else if (typeof MessageChannel !== "undefined") { // modern browsers // http://www.nonblocking.io/2011/06/windownexttick.html var channel = new MessageChannel(); - // At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create - // working message ports the first time a page loads. - channel.port1.onmessage = function () { - requestFlush = requestPortFlush; - channel.port1.onmessage = flush; - flush(); - }; - var requestPortFlush = function () { - // Opera requires us to provide a message payload, regardless of - // whether we use it. - channel.port2.postMessage(0); - }; + channel.port1.onmessage = flush; requestFlush = function () { - setTimeout(flush, 0); - requestPortFlush(); + channel.port2.postMessage(0); }; } else { @@ -230,128 +200,21 @@ if (isNodeJS) { } function asap(task) { - if (isNodeJS && process.domain) { - task = process.domain.bind(task); - } - - queue.push(task); + tail = tail.next = { + task: task, + domain: isNodeJS && process.domain, + next: null + }; if (!flushing) { - requestFlush(); flushing = true; + requestFlush(); } }; module.exports = asap; -}],["asap","queue",{},function (require, exports, module){ - -// asap queue -// ---------- - -"use strict"; - -module.exports = Queue; -function Queue(capacity) { - this.capacity = this.snap(capacity); - this.length = 0; - this.front = 0; - this.initialize(); -} - -Queue.prototype.push = function (value) { - var length = this.length; - if (this.capacity <= length) { - this.grow(this.snap(this.capacity * this.growFactor)); - } - var index = (this.front + length) & (this.capacity - 1); - this[index] = value; - this.length = length + 1; -}; - -Queue.prototype.shift = function () { - var front = this.front; - var result = this[front]; - - this[front] = void 0; - this.front = (front + 1) & (this.capacity - 1); - this.length--; - return result; -}; - -Queue.prototype.grow = function (capacity) { - var oldFront = this.front; - var oldCapacity = this.capacity; - var oldQueue = new Array(oldCapacity); - var length = this.length; - - copy(this, 0, oldQueue, 0, oldCapacity); - this.capacity = capacity; - this.initialize(); - this.front = 0; - if (oldFront + length <= oldCapacity) { - // Can perform direct linear copy - copy(oldQueue, oldFront, this, 0, length); - } else { - // Cannot perform copy directly, perform as much as possible at the - // end, and then copy the rest to the beginning of the buffer - var lengthBeforeWrapping = - length - ((oldFront + length) & (oldCapacity - 1)); - copy( - oldQueue, - oldFront, - this, - 0, - lengthBeforeWrapping - ); - copy( - oldQueue, - 0, - this, - lengthBeforeWrapping, - length - lengthBeforeWrapping - ); - } -}; - -Queue.prototype.initialize = function () { - var length = this.capacity; - for (var i = 0; i < length; ++i) { - this[i] = void 0; - } -}; - -Queue.prototype.snap = function (capacity) { - if (typeof capacity !== "number") { - return this.minCapacity; - } - return pow2AtLeast( - Math.min(this.maxCapacity, Math.max(this.minCapacity, capacity)) - ); -}; - -Queue.prototype.maxCapacity = (1 << 30) | 0; -Queue.prototype.minCapacity = 16; -Queue.prototype.growFactor = 8; - -function copy(source, sourceIndex, target, targetIndex, length) { - for (var index = 0; index < length; ++index) { - target[index + targetIndex] = source[index + sourceIndex]; - } -} - -function pow2AtLeast(n) { - n = n >>> 0; - n = n - 1; - n = n | (n >> 1); - n = n | (n >> 2); - n = n | (n >> 4); - n = n | (n >> 8); - n = n | (n >> 16); - return n + 1; -} - -}],["collections","generic-collection",{"./shim-array":7},function (require, exports, module){ +}],["collections","generic-collection",{"./shim-array":6},function (require, exports, module){ // collections generic-collection // ------------------------------ @@ -617,7 +480,7 @@ GenericCollection.prototype.only = function () { require("./shim-array"); -}],["collections","generic-order",{"./shim-object":9},function (require, exports, module){ +}],["collections","generic-order",{"./shim-object":8},function (require, exports, module){ // collections generic-order // ------------------------- @@ -677,7 +540,7 @@ GenericOrder.prototype.compare = function (that, compare) { return comparison; }; -}],["collections","iterator",{"./weak-map":17,"./generic-collection":3},function (require, exports, module){ +}],["collections","iterator",{"./weak-map":18,"./generic-collection":2},function (require, exports, module){ // collections iterator // -------------------- @@ -1225,7 +1088,7 @@ Iterator.Iteration = Iteration; Iterator.DoneIteration = DoneIteration; Iterator.done = new DoneIteration(); -}],["collections","shim",{"./shim-array":7,"./shim-object":9,"./shim-function":8,"./shim-regexp":10},function (require, exports, module){ +}],["collections","shim",{"./shim-array":6,"./shim-object":8,"./shim-function":7,"./shim-regexp":9},function (require, exports, module){ // collections shim // ---------------- @@ -1236,7 +1099,7 @@ var Object = require("./shim-object"); var Function = require("./shim-function"); var RegExp = require("./shim-regexp"); -}],["collections","shim-array",{"./shim-function":8,"./generic-collection":3,"./generic-order":4,"./iterator":5,"weak-map":17},function (require, exports, module){ +}],["collections","shim-array",{"./shim-function":7,"./generic-collection":2,"./generic-order":3,"./iterator":4,"weak-map":18},function (require, exports, module){ // collections shim-array // ---------------------- @@ -1679,7 +1542,7 @@ Function.get = function (key) { }; }; -}],["collections","shim-object",{"weak-map":17},function (require, exports, module){ +}],["collections","shim-object",{"weak-map":18},function (require, exports, module){ // collections shim-object // ----------------------- @@ -2220,7 +2083,7 @@ if (!RegExp.escape) { }; } -}],["mr","boot/script-params",{"url":14},function (require, exports, module){ +}],["mr","boot/script-params",{"url":15},function (require, exports, module){ // mr boot/script-params // --------------------- @@ -2290,7 +2153,7 @@ function getParams(scriptName) { return params; } -}],["mr","browser",{"./common":13,"url":14,"q":16,"./script":15},function (require, exports, module){ +}],["mr","browser",{"./common":12,"url":15,"q":17,"./script":16},function (require, exports, module){ // mr browser // ---------- @@ -2528,7 +2391,7 @@ function loadIfNotPreloaded(location, definition, preloaded) { } } -}],["mr","common",{"q":16,"url":14},function (require, exports, module){ +}],["mr","common",{"q":17,"url":15,"./merge":14,"./identifier":13},function (require, exports, module){ // mr common // --------- @@ -2545,6 +2408,8 @@ function loadIfNotPreloaded(location, definition, preloaded) { var Require = exports; var Q = require("q"); var URL = require("url"); +var merge = require("./merge"); +var Identifier = require("./identifier"); if (!this) { throw new Error("Require does not work in strict mode."); @@ -2584,27 +2449,28 @@ Require.makeRequire = function (config) { // ``module`` free variable inside the corresponding module. function getModuleDescriptor(id) { var lookupId = id.toLowerCase(); - if (!has(modules, lookupId)) { - var extension = Require.extension(id); + if (!has.call(modules, lookupId)) { + var extension = Identifier.extension(id); var type; if ( extension && ( - has(config.optimizers, extension) || - has(config.translators, extension) || - has(config.compilers, extension) + has.call(config.optimizers, extension) || + has.call(config.translators, extension) || + has.call(config.compilers, extension) ) ) { type = extension; } else { type = "js"; } - modules[lookupId] = { + var module = { id: id, extension: extension, type: type, display: (config.name || config.location) + "#" + id, require: makeRequire(id) }; + modules[lookupId] = module; } return modules[lookupId]; } @@ -2635,13 +2501,13 @@ Require.makeRequire = function (config) { module.exports === void 0 && module.redirect === void 0 ) { - return Q(config.load).call(void 0, topId, module); + return config.load(topId, module); } }) .then(function () { // Translate (to JavaScript, optionally provide dependency analysis // services). - if (module.type !== "js" && has(config.translators, module.type)) { + if (module.type !== "js" && has.call(config.translators, module.type)) { var translatorId = config.translators[module.type]; return Q.try(function () { // The use of a preprocessor package is optional for @@ -2671,7 +2537,7 @@ Require.makeRequire = function (config) { // Run optional optimizers. // {text, type} to {text', type') - if (config.hasPreprocessorPackage && has(config.optimizers, module.type)) { + if (config.hasPreprocessorPackage && has.call(config.optimizers, module.type)) { var optimizerId = config.optimizers[module.type]; return config.loadPreprocessorPackage() .invoke("async", optimizerId) @@ -2688,7 +2554,7 @@ Require.makeRequire = function (config) { ) { // Then apply configured compilers. module {text, type} to // {dependencies, factory || exports || redirect} - if (has(config.compilers, module.type)) { + if (has.call(config.compilers, module.type)) { var compilerId = config.compilers[module.type]; return deepLoad(compilerId, "", loading) .then(function () { @@ -2720,7 +2586,7 @@ Require.makeRequire = function (config) { // data-lock on a cycle of dependencies. loading = loading || {}; // has this all happened before? will it happen again? - if (has(loading, topId)) { + if (has.call(loading, topId)) { return; // break the cycle of violence. } loading[topId] = true; // this has happened before @@ -2730,7 +2596,7 @@ Require.makeRequire = function (config) { // recursion. var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { - depId = resolve(depId, topId); + depId = Identifier.resolve(depId, topId); // create dependees set, purely for debug purposes var module = getModuleDescriptor(depId); var dependees = module.dependees = module.dependees || {}; @@ -2743,7 +2609,7 @@ Require.makeRequire = function (config) { } function lookup(topId, viaId) { - topId = resolve(topId, viaId); + topId = Identifier.resolve(topId, viaId); var module = getModuleDescriptor(topId); // check for consistent case convention @@ -2851,7 +2717,7 @@ Require.makeRequire = function (config) { var internal = !!seen; seen = seen || {}; - if (has(seen, location)) { + if (has.call(seen, location)) { return null; // break the cycle of violence. } seen[location] = true; @@ -2888,14 +2754,14 @@ Require.makeRequire = function (config) { // Main synchronously executing "require()" function var require = function(id) { - var topId = resolve(id, viaId); + var topId = Identifier.resolve(id, viaId); return getExports(topId, viaId); }; // Asynchronous "require.async()" which ensures async executation // (even with synchronous loaders) - require.async = function(id) { - var topId = resolve(id, viaId); + require.async = function (id) { + var topId = Identifier.resolve(id, viaId); var module = getModuleDescriptor(id); return deepLoad(topId, viaId) .then(function () { @@ -2903,13 +2769,16 @@ Require.makeRequire = function (config) { }); }; + require.lookup = function (id) { + return lookup(id, viaId); + }; + require.resolve = function (id) { - return normalize(resolve(id, viaId)); + return Identifier.normalize(Identifier.resolve(id, viaId)); }; require.getModule = getModuleDescriptor; // XXX deprecated, use: require.getModuleDescriptor = getModuleDescriptor; - require.lookup = lookup; require.load = load; require.deepLoad = deepLoad; @@ -3234,7 +3103,7 @@ function configurePackage(location, description, parent) { // loaded definition from the given path. modules[""] = { id: "", - redirect: normalize(resolve(description.main, "")), + redirect: Identifier.normalize(Identifier.resolve(description.main, "")), location: config.location }; @@ -3246,7 +3115,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: normalize(resolve(redirects[name], "")), + redirect: Identifier.normalize(Identifier.resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); @@ -3343,7 +3212,7 @@ function postConfigurePackage(config, description) { if (description["redirect-patterns"]) { var describedPatterns = description["redirect-patterns"]; for (var pattern in describedPatterns) { - if (has(describedPatterns, pattern)) { + if (has.call(describedPatterns, pattern)) { redirectTable.push([ new RegExp(pattern), describedPatterns[pattern] @@ -3353,25 +3222,6 @@ function postConfigurePackage(config, description) { } } -function merge(target, source) { - for (var name in source) { - if (has(source, name)) { - var sourceValue = source[name]; - var targetValue = target[name]; - if (sourceValue === null) { - delete target[name]; - } else if ( - typeof sourceValue === "object" && !Array.isArray(sourceValue) && - typeof targetValue === "object" && !Array.isArray(targetValue) - ) { - merge(targetValue, sourceValue); - } else { - target[name] = source[name]; - } - } - } -} - Require.exposedConfigs = [ "location", "packageDescription", @@ -3448,9 +3298,6 @@ Require.MappingsLoader = function(config, load) { var prefixes = Object.keys(mappings); var length = prefixes.length; - if (Require.isAbsolute(id)) { - return load(id, module); - } var i, prefix; for (i = 0; i < length; i++) { prefix = prefixes[i]; @@ -3496,9 +3343,9 @@ Require.LocationLoader = function (config, load) { var base = id; var extension = module.extension; if ( - !has(config.optimizers, extension) && - !has(config.translators, extension) && - !has(config.compilers, extension) && + !has.call(config.optimizers, extension) && + !has.call(config.translators, extension) && + !has.call(config.compilers, extension) && extension !== "js" && extension !== "json" ) { @@ -3516,8 +3363,47 @@ Require.MemoizedLoader = function (config, load) { // Helper functions: +Require.resolve = Identifier.resolve; +Require.normalize = Identifier.normalize; +Require.extension = Identifier.extension; + +// Tests whether the URL is a absolute. +Require.isAbsolute = isAbsolute; +function isAbsolute(location) { + return (/^[\w\-]+:/).test(location); +} + +// Extracts dependencies by parsing code and looking for "require" (currently +// using a regexp) +Require.parseDependencies = parseDependencies; +function parseDependencies(text) { + var dependsUpon = {}; + String(text).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { + dependsUpon[id] = true; + }); + return Object.keys(dependsUpon); +} + +var has = Object.prototype.hasOwnProperty; + +function memoize(callback, cache) { + cache = cache || {}; + return function (key, arg) { + if (!has.call(cache, key)) { + cache[key] = Q(callback).call(void 0, key, arg); + } + return cache[key]; + }; +} + +}],["mr","identifier",{},function (require, exports, module){ + +// mr identifier +// ------------- + + // Resolves CommonJS module IDs (not paths) -Require.resolve = resolve; +exports.resolve = resolve; function resolve(id, baseId) { id = String(id); var source = id.split("/"); @@ -3543,7 +3429,7 @@ function resolve(id, baseId) { return target.join("/"); } -Require.normalize = normalize; +exports.normalize = normalize; function normalize(id) { var match = /^(.*)\.js$/.exec(id); if (match) { @@ -3552,7 +3438,7 @@ function normalize(id) { return id; } -Require.extension = extension; +exports.extension = extension; function extension(location) { var match = /\.([^\/\.]+)$/.exec(location); if (match) { @@ -3560,37 +3446,34 @@ function extension(location) { } } -// Tests whether the location or URL is a absolute. -Require.isAbsolute = isAbsolute; -function isAbsolute(location) { - return (/^[\w\-]+:/).test(location); -} +}],["mr","merge",{},function (require, exports, module){ -// Extracts dependencies by parsing code and looking for "require" (currently -// using a simple regexp) -Require.parseDependencies = parseDependencies; -function parseDependencies(text) { - var o = {}; - String(text).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { - o[id] = true; - }); - return Object.keys(o); -} +// mr merge +// -------- -function has(object, property) { - return Object.prototype.hasOwnProperty.call(object, property); -} -function memoize(callback, cache) { - cache = cache || {}; - return function (key, arg) { - if (!has(cache, key)) { - cache[key] = Q(callback).call(void 0, key, arg); +module.exports = merge; +function merge(target, source) { + for (var name in source) { + if (has.call(source, name)) { + var sourceValue = source[name]; + var targetValue = target[name]; + if (sourceValue === null) { + delete target[name]; + } else if ( + typeof sourceValue === "object" && !Array.isArray(sourceValue) && + typeof targetValue === "object" && !Array.isArray(targetValue) + ) { + merge(targetValue, sourceValue); + } else { + target[name] = source[name]; + } } - return cache[key]; - }; + } } +var has = Object.prototype.hasOwnProperty; + }],["mr","mini-url",{},function (require, exports, module){ // mr mini-url @@ -3669,12 +3552,12 @@ function load(location) { head.appendChild(script); } -}],["q","q",{"collections/shim":6,"collections/weak-map":17,"collections/iterator":5,"asap":1},function (require, exports, module){ +}],["q","q",{"collections/shim":5,"collections/weak-map":18,"collections/iterator":4,"asap":1},function (require, exports, module){ // q q // --- -// vim:ts=4:sts=4:sw=4: +/* vim:ts=4:sts=4:sw=4: */ /*! * * Copyright 2009-2013 Kris Kowal under the terms of the MIT @@ -3857,7 +3740,7 @@ function deprecate(callback, name, alternative) { var handlers = new WeakMap(); -function Q_inspect(promise) { +function Q_getHandler(promise) { var handler = handlers.get(promise); if (!handler || !handler.became) { return handler; @@ -3878,7 +3761,7 @@ function follow(handler) { var theViciousCycleError = new Error("Can't resolve a promise with itself"); var theViciousCycleRejection = Q_reject(theViciousCycleError); -var theViciousCycle = Q_inspect(theViciousCycleRejection); +var theViciousCycle = Q_getHandler(theViciousCycleRejection); var thenables = new WeakMap(); @@ -4000,7 +3883,7 @@ function Q_all(questions) { var handler; if ( Q_isPromise(promise) && - (handler = Q_inspect(promise)).state === "fulfilled" + (handler = Q_getHandler(promise)).state === "fulfilled" ) { answers[index] = handler.value; } else { @@ -4300,7 +4183,7 @@ function Promise(handler) { if (typeof handler === "function") { var setup = handler; var deferred = defer(); - handler = Q_inspect(deferred.promise); + handler = Q_getHandler(deferred.promise); try { setup(deferred.resolve, deferred.reject, deferred.setEstimate); } catch (error) { @@ -4381,14 +4264,14 @@ Promise.prototype.inspect = function Promise_inspect() { // the second layer captures only the relevant "state" properties of the // handler to prevent leaking the capability to access or alter the // handler. - return Q_inspect(this).inspect(); + return Q_getHandler(this).inspect(); }; /** * @returns {boolean} whether the promise is waiting for a result. */ Promise.prototype.isPending = function Promise_isPending() { - return Q_inspect(this).state === "pending"; + return Q_getHandler(this).state === "pending"; }; /** @@ -4396,7 +4279,7 @@ Promise.prototype.isPending = function Promise_isPending() { * fulfillment value. */ Promise.prototype.isFulfilled = function Promise_isFulfilled() { - return Q_inspect(this).state === "fulfilled"; + return Q_getHandler(this).state === "fulfilled"; }; /** @@ -4404,7 +4287,14 @@ Promise.prototype.isFulfilled = function Promise_isFulfilled() { * its rejection. */ Promise.prototype.isRejected = function Promise_isRejected() { - return Q_inspect(this).state === "rejected"; + return Q_getHandler(this).state === "rejected"; +}; + +/** + * TODO + */ +Promise.prototype.toBePassed = function Promise_toBePassed() { + return Q_getHandler(this).state === "passed"; }; /** @@ -4538,7 +4428,7 @@ Promise.prototype.done = function Promise_done(fulfilled, rejected) { _rejected = process.domain.bind(_rejected); } - Q_inspect(self).dispatch(_fulfilled, "then", [_rejected]); + Q_getHandler(self).dispatch(_fulfilled, "then", [_rejected]); }); }; @@ -4620,7 +4510,7 @@ Promise.prototype.finally = function Promise_finally(callback, ms) { * TODO */ Promise.prototype.observeEstimate = function Promise_observeEstimate(emit) { - this.dispatch("estimate", [emit]); + this.rawDispatch(null, "estimate", [emit]); return this; }; @@ -4628,7 +4518,7 @@ Promise.prototype.observeEstimate = function Promise_observeEstimate(emit) { * TODO */ Promise.prototype.getEstimate = function Promise_getEstimate() { - return Q_inspect(this).estimate; + return Q_getHandler(this).estimate; }; /** @@ -4645,15 +4535,15 @@ Promise.prototype.dispatch = function Promise_dispatch(op, args) { Promise.prototype.rawDispatch = function Promise_rawDispatch(resolve, op, args) { var self = this; asap(function Promise_dispatch_task() { - Q_inspect(self).dispatch(resolve, op, args); + Q_getHandler(self).dispatch(resolve, op, args); }); }; /** * TODO */ -Promise.prototype.get = function Promise_get(key) { - return this.dispatch("get", [key]); +Promise.prototype.get = function Promise_get(name) { + return this.dispatch("get", [name]); }; /** @@ -4778,6 +4668,17 @@ Promise.prototype.pull = function Promise_pull() { return this.dispatch("pull", []); }; +/** + * TODO + */ +Promise.prototype.pass = function Promise_pass() { + if (!this.toBePassed()) { + return new Promise(new Passed(this)); + } else { + return this; + } +}; + // Thus begins the portion dedicated to the deferred @@ -4805,7 +4706,7 @@ function Deferred(promise) { * TODO */ Deferred.prototype.resolve = function Deferred_resolve(value) { - var handler = Q_inspect(promises.get(this)); + var handler = Q_getHandler(promises.get(this)); if (!handler.messages) { return; } @@ -4816,7 +4717,7 @@ Deferred.prototype.resolve = function Deferred_resolve(value) { * TODO */ Deferred.prototype.reject = function Deferred_reject(reason) { - var handler = Q_inspect(promises.get(this)); + var handler = Q_getHandler(promises.get(this)); if (!handler.messages) { return; } @@ -4834,7 +4735,7 @@ Deferred.prototype.setEstimate = function Deferred_setEstimate(estimate) { if (estimate < 1e12 && estimate !== -Infinity) { throw new Error("Estimate values should be a number of miliseconds in the future"); } - var handler = Q_inspect(promises.get(this)); + var handler = Q_getHandler(promises.get(this)); // TODO There is a bit of capability leakage going on here. The Deferred // should only be able to set the estimate for its original // Pending, not for any handler that promise subsequently became. @@ -4897,14 +4798,36 @@ Fulfilled.prototype.get = function Fulfilled_get(name) { return this.value[name]; }; -Fulfilled.prototype.invoke = function Fulfilled_invoke( - name, args -) { - return this.value[name].apply(this.value, args); +Fulfilled.prototype.call = function Fulfilled_call(args, thisp) { + return this.callInvoke(this.value, args, thisp); }; -Fulfilled.prototype.call = function Fulfilled_call(args, thisp) { - return this.value.apply(thisp, args); +Fulfilled.prototype.invoke = function Fulfilled_invoke(name, args) { + return this.callInvoke(this.value[name], args, this.value); +}; + +Fulfilled.prototype.callInvoke = function Fulfilled_callInvoke(callback, args, thisp) { + var waitToBePassed; + for (var index = 0; index < args.length; index++) { + if (Q_isPromise(args[index]) && args[index].toBePassed()) { + waitToBePassed = waitToBePassed || []; + waitToBePassed.push(args[index]); + } + } + if (waitToBePassed) { + var self = this; + return Q_all(waitToBePassed).then(function () { + return self.callInvoke(callback, args.map(function (arg) { + if (Q_isPromise(arg) && arg.toBePassed()) { + return arg.inspect().value; + } else { + return arg; + } + }), thisp); + }); + } else { + return callback.apply(thisp, args); + } }; Fulfilled.prototype.keys = function Fulfilled_keys() { @@ -4992,7 +4915,7 @@ Pending.prototype.dispatch = function Pending_dispatch(resolve, op, operands) { Pending.prototype.become = function Pending_become(promise) { this.became = theViciousCycle; - var handler = Q_inspect(promise); + var handler = Q_getHandler(promise); this.became = handler; handlers.set(promise, handler); @@ -5002,7 +4925,7 @@ Pending.prototype.become = function Pending_become(promise) { // makeQ does not have this asap call, so it must be queueing events // downstream. TODO look at makeQ to ascertain asap(function Pending_become_eachMessage_task() { - var handler = Q_inspect(promise); + var handler = Q_getHandler(promise); handler.dispatch.apply(handler, message); }); }); @@ -5046,7 +4969,7 @@ Thenable.prototype.cast = function Thenable_cast() { deferred.reject(exception); } }); - this.became = Q_inspect(deferred.promise); + this.became = Q_getHandler(deferred.promise); } return this.became; }; @@ -5056,6 +4979,21 @@ Thenable.prototype.dispatch = function Thenable_dispatch(resolve, op, args) { }; +function Passed(promise) { + this.promise = promise; +} + +Passed.prototype.state = "passed"; + +Passed.prototype.inspect = function Passed_inspect() { + return this.promise.inspect(); +}; + +Passed.prototype.dispatch = function Passed_dispatch(resolve, op, args) { + return this.promise.rawDispatch(resolve, op, args); +}; + + // Thus begins the Q Node.js bridge /** @@ -5074,11 +5012,22 @@ Q.ninvoke = function Q_ninvoke(object, name /*...args*/) { args[index - 2] = arguments[index]; } var deferred = Q.defer(); - args[index - 2] = makeNodebackResolver(deferred.resolve); + args[index - 2] = deferred.makeNodeResolver(); Q(object).dispatch("invoke", [name, args]).catch(deferred.reject); return deferred.promise; }; +Promise.prototype.ninvoke = function Promise_ninvoke(name /*...args*/) { + var args = new Array(arguments.length); + for (var index = 1; index < arguments.length; index++) { + args[index - 1] = arguments[index]; + } + var deferred = Q.defer(); + args[index - 1] = deferred.makeNodeResolver(); + this.dispatch("invoke", [name, args]).catch(deferred.reject); + return deferred.promise; +}; + /** * Wraps a Node.js continuation passing function and returns an equivalent * version that returns a promise. @@ -5095,7 +5044,7 @@ Q.denodeify = function Q_denodeify(callback, pattern) { args[index] = arguments[index]; } var deferred = Q.defer(); - args[index] = makeNodebackResolver(deferred.resolve, pattern); + args[index] = deferred.makeNodeResolver(pattern); Q(callback).apply(this, args).catch(deferred.reject); return deferred.promise; }; @@ -5104,12 +5053,17 @@ Q.denodeify = function Q_denodeify(callback, pattern) { /** * Creates a Node.js-style callback that will resolve or reject the deferred * promise. - * TODO + * @param unpack `true` means that the Node.js-style-callback accepts a + * fixed or variable number of arguments and that the deferred should be resolved + * with an array of these value arguments, or rejected with the error argument. + * An array of names means that the Node.js-style-callback accepts a fixed + * number of arguments, and that the resolution should be an object with + * properties corresponding to the given names and respective value arguments. * @returns a nodeback - * @private */ -function makeNodebackResolver(resolve, names) { - if (names === true) { +Deferred.prototype.makeNodeResolver = function (unpack) { + var resolve = this.resolve; + if (unpack === true) { return function variadicNodebackToResolver(error) { if (error) { resolve(Q_reject(error)); @@ -5121,14 +5075,14 @@ function makeNodebackResolver(resolve, names) { resolve(value); } }; - } else if (names) { + } else if (unpack) { return function namedArgumentNodebackToResolver(error) { if (error) { resolve(Q_reject(error)); } else { var value = {}; - for (var index in names) { - value[names[index]] = arguments[index + 1]; + for (var index in unpack) { + value[unpack[index]] = arguments[index + 1]; } resolve(value); } @@ -5142,7 +5096,7 @@ function makeNodebackResolver(resolve, names) { } }; } -} +}; /** * TODO @@ -5329,7 +5283,7 @@ Promise.prototype.passByCopy = deprecate(function (value) { Q.nfapply = deprecate(function (callback, args) { var deferred = Q.defer(); var nodeArgs = Array.prototype.slice.call(args); - nodeArgs.push(makeNodebackResolver(deferred.resolve)); + nodeArgs.push(deferred.makeNodeResolver()); Q(callback).apply(this, nodeArgs).catch(deferred.reject); return deferred.promise; }, "nfapply"); @@ -5356,7 +5310,7 @@ Q.nfbind = deprecate(function (callback /*...args*/) { return function () { var nodeArgs = baseArgs.concat(Array.prototype.slice.call(arguments)); var deferred = Q.defer(); - nodeArgs.push(makeNodebackResolver(deferred.resolve)); + nodeArgs.push(deferred.makeNodeResolver()); Q(callback).apply(this, nodeArgs).catch(deferred.reject); return deferred.promise; }; @@ -5375,7 +5329,7 @@ Q.nbind = deprecate(function (callback, thisp /*...args*/) { return function () { var nodeArgs = baseArgs.concat(Array.prototype.slice.call(arguments)); var deferred = Q.defer(); - nodeArgs.push(makeNodebackResolver(deferred.resolve)); + nodeArgs.push(deferred.makeNodeResolver()); function bound() { return callback.apply(thisp, arguments); } @@ -5386,7 +5340,7 @@ Q.nbind = deprecate(function (callback, thisp /*...args*/) { Q.npost = deprecate(function (object, name, nodeArgs) { var deferred = Q.defer(); - nodeArgs.push(makeNodebackResolver(deferred.resolve)); + nodeArgs.push(deferred.makeNodeResolver()); Q(object).dispatch("invoke", [name, nodeArgs]).catch(deferred.reject); return deferred.promise; }, "npost", "ninvoke (with spread arguments)"); @@ -5395,16 +5349,6 @@ Promise.prototype.npost = deprecate(function (name, args) { return Q.npost(this, name, args); }, "npost", "Q.ninvoke (with caveats)"); -Q.makeNodeResolver = deprecate(makeNodebackResolver, "makeNodeResolver"); - -Promise.prototype.ninvoke = deprecate(function (name) { - var args = new Array(arguments.length - 1); - for (var index = 1; index < arguments.length; index++) { - args[index - 1] = arguments[index]; - } - return Q.npost(this, name, args); -}, "ninvoke", "Q.ninvoke"); - Q.nmapply = deprecate(Q.nmapply, "nmapply", "q/node nmapply"); Promise.prototype.nmapply = deprecate(Promise.prototype.npost, "nmapply", "Q.nmapply"); @@ -5761,13 +5705,22 @@ var qEndingLine = captureLine(); // Object.prototype might not be frozen and // Object.create(null) might not be reliable. - defProp(key, HIDDEN_NAME, { - value: hiddenRecord, - writable: false, - enumerable: false, - configurable: false - }); - return hiddenRecord; + try { + defProp(key, HIDDEN_NAME, { + value: hiddenRecord, + writable: false, + enumerable: false, + configurable: false + }); + return hiddenRecord; + } catch (error) { + // Under some circumstances, isExtensible seems to misreport whether + // the HIDDEN_NAME can be defined. + // The circumstances have not been isolated, but at least affect + // Node.js v0.10.26 on TravisCI / Linux, but not the same version of + // Node.js on OS X. + return void 0; + } } /** diff --git a/common.js b/common.js index 6139343e..0b249850 100644 --- a/common.js +++ b/common.js @@ -10,6 +10,8 @@ var Require = exports; var Q = require("q"); var URL = require("url"); +var merge = require("./merge"); +var Identifier = require("./identifier"); if (!this) { throw new Error("Require does not work in strict mode."); @@ -49,14 +51,14 @@ Require.makeRequire = function (config) { // ``module`` free variable inside the corresponding module. function getModuleDescriptor(id) { var lookupId = id.toLowerCase(); - if (!has(modules, lookupId)) { - var extension = Require.extension(id); + if (!has.call(modules, lookupId)) { + var extension = Identifier.extension(id); var type; if ( extension && ( - has(config.optimizers, extension) || - has(config.translators, extension) || - has(config.compilers, extension) + has.call(config.optimizers, extension) || + has.call(config.translators, extension) || + has.call(config.compilers, extension) ) ) { type = extension; @@ -107,7 +109,7 @@ Require.makeRequire = function (config) { .then(function () { // Translate (to JavaScript, optionally provide dependency analysis // services). - if (module.type !== "js" && has(config.translators, module.type)) { + if (module.type !== "js" && has.call(config.translators, module.type)) { var translatorId = config.translators[module.type]; return Q.try(function () { // The use of a preprocessor package is optional for @@ -137,7 +139,7 @@ Require.makeRequire = function (config) { // Run optional optimizers. // {text, type} to {text', type') - if (config.hasPreprocessorPackage && has(config.optimizers, module.type)) { + if (config.hasPreprocessorPackage && has.call(config.optimizers, module.type)) { var optimizerId = config.optimizers[module.type]; return config.loadPreprocessorPackage() .invoke("async", optimizerId) @@ -154,7 +156,7 @@ Require.makeRequire = function (config) { ) { // Then apply configured compilers. module {text, type} to // {dependencies, factory || exports || redirect} - if (has(config.compilers, module.type)) { + if (has.call(config.compilers, module.type)) { var compilerId = config.compilers[module.type]; return deepLoad(compilerId, "", loading) .then(function () { @@ -186,7 +188,7 @@ Require.makeRequire = function (config) { // data-lock on a cycle of dependencies. loading = loading || {}; // has this all happened before? will it happen again? - if (has(loading, topId)) { + if (has.call(loading, topId)) { return; // break the cycle of violence. } loading[topId] = true; // this has happened before @@ -196,7 +198,7 @@ Require.makeRequire = function (config) { // recursion. var dependencies = module.dependencies = module.dependencies || []; return Q.all(module.dependencies.map(function (depId) { - depId = resolve(depId, topId); + depId = Identifier.resolve(depId, topId); // create dependees set, purely for debug purposes var module = getModuleDescriptor(depId); var dependees = module.dependees = module.dependees || {}; @@ -209,7 +211,7 @@ Require.makeRequire = function (config) { } function lookup(topId, viaId) { - topId = resolve(topId, viaId); + topId = Identifier.resolve(topId, viaId); var module = getModuleDescriptor(topId); // check for consistent case convention @@ -317,7 +319,7 @@ Require.makeRequire = function (config) { var internal = !!seen; seen = seen || {}; - if (has(seen, location)) { + if (has.call(seen, location)) { return null; // break the cycle of violence. } seen[location] = true; @@ -354,14 +356,14 @@ Require.makeRequire = function (config) { // Main synchronously executing "require()" function var require = function(id) { - var topId = resolve(id, viaId); + var topId = Identifier.resolve(id, viaId); return getExports(topId, viaId); }; // Asynchronous "require.async()" which ensures async executation // (even with synchronous loaders) require.async = function (id) { - var topId = resolve(id, viaId); + var topId = Identifier.resolve(id, viaId); var module = getModuleDescriptor(id); return deepLoad(topId, viaId) .then(function () { @@ -374,7 +376,7 @@ Require.makeRequire = function (config) { }; require.resolve = function (id) { - return normalize(resolve(id, viaId)); + return Identifier.normalize(Identifier.resolve(id, viaId)); }; require.getModule = getModuleDescriptor; // XXX deprecated, use: @@ -703,7 +705,7 @@ function configurePackage(location, description, parent) { // loaded definition from the given path. modules[""] = { id: "", - redirect: normalize(resolve(description.main, "")), + redirect: Identifier.normalize(Identifier.resolve(description.main, "")), location: config.location }; @@ -715,7 +717,7 @@ function configurePackage(location, description, parent) { Object.keys(redirects).forEach(function (name) { modules[name] = { id: name, - redirect: normalize(resolve(redirects[name], "")), + redirect: Identifier.normalize(Identifier.resolve(redirects[name], "")), location: URL.resolve(location, name) }; }); @@ -812,7 +814,7 @@ function postConfigurePackage(config, description) { if (description["redirect-patterns"]) { var describedPatterns = description["redirect-patterns"]; for (var pattern in describedPatterns) { - if (has(describedPatterns, pattern)) { + if (has.call(describedPatterns, pattern)) { redirectTable.push([ new RegExp(pattern), describedPatterns[pattern] @@ -822,25 +824,6 @@ function postConfigurePackage(config, description) { } } -function merge(target, source) { - for (var name in source) { - if (has(source, name)) { - var sourceValue = source[name]; - var targetValue = target[name]; - if (sourceValue === null) { - delete target[name]; - } else if ( - typeof sourceValue === "object" && !Array.isArray(sourceValue) && - typeof targetValue === "object" && !Array.isArray(targetValue) - ) { - merge(targetValue, sourceValue); - } else { - target[name] = source[name]; - } - } - } -} - Require.exposedConfigs = [ "location", "packageDescription", @@ -917,9 +900,6 @@ Require.MappingsLoader = function(config, load) { var prefixes = Object.keys(mappings); var length = prefixes.length; - if (Require.isAbsolute(id)) { - return load(id, module); - } var i, prefix; for (i = 0; i < length; i++) { prefix = prefixes[i]; @@ -965,9 +945,9 @@ Require.LocationLoader = function (config, load) { var base = id; var extension = module.extension; if ( - !has(config.optimizers, extension) && - !has(config.translators, extension) && - !has(config.compilers, extension) && + !has.call(config.optimizers, extension) && + !has.call(config.translators, extension) && + !has.call(config.compilers, extension) && extension !== "js" && extension !== "json" ) { @@ -985,75 +965,33 @@ Require.MemoizedLoader = function (config, load) { // Helper functions: -// Resolves CommonJS module IDs (not paths) -Require.resolve = resolve; -function resolve(id, baseId) { - id = String(id); - var source = id.split("/"); - var target = []; - if (source.length && source[0] === "." || source[0] === "..") { - var parts = baseId.split("/"); - parts.pop(); - source.unshift.apply(source, parts); - } - for (var i = 0, ii = source.length; i < ii; i++) { - /*jshint -W035 */ - var part = source[i]; - if (part === "" || part === ".") { - } else if (part === "..") { - if (target.length) { - target.pop(); - } - } else { - target.push(part); - } - /*jshint +W035 */ - } - return target.join("/"); -} - -Require.normalize = normalize; -function normalize(id) { - var match = /^(.*)\.js$/.exec(id); - if (match) { - id = match[1]; - } - return id; -} +Require.resolve = Identifier.resolve; +Require.normalize = Identifier.normalize; +Require.extension = Identifier.extension; -Require.extension = extension; -function extension(location) { - var match = /\.([^\/\.]+)$/.exec(location); - if (match) { - return match[1]; - } -} - -// Tests whether the location or URL is a absolute. +// Tests whether the URL is a absolute. Require.isAbsolute = isAbsolute; function isAbsolute(location) { return (/^[\w\-]+:/).test(location); } // Extracts dependencies by parsing code and looking for "require" (currently -// using a simple regexp) +// using a regexp) Require.parseDependencies = parseDependencies; function parseDependencies(text) { - var o = {}; + var dependsUpon = {}; String(text).replace(/(?:^|[^\w\$_.])require\s*\(\s*["']([^"']*)["']\s*\)/g, function(_, id) { - o[id] = true; + dependsUpon[id] = true; }); - return Object.keys(o); + return Object.keys(dependsUpon); } -function has(object, property) { - return Object.prototype.hasOwnProperty.call(object, property); -} +var has = Object.prototype.hasOwnProperty; function memoize(callback, cache) { cache = cache || {}; return function (key, arg) { - if (!has(cache, key)) { + if (!has.call(cache, key)) { cache[key] = Q(callback).call(void 0, key, arg); } return cache[key]; diff --git a/identifier.js b/identifier.js new file mode 100644 index 00000000..a634e57f --- /dev/null +++ b/identifier.js @@ -0,0 +1,45 @@ + +// Resolves CommonJS module IDs (not paths) +exports.resolve = resolve; +function resolve(id, baseId) { + id = String(id); + var source = id.split("/"); + var target = []; + if (source.length && source[0] === "." || source[0] === "..") { + var parts = baseId.split("/"); + parts.pop(); + source.unshift.apply(source, parts); + } + for (var i = 0, ii = source.length; i < ii; i++) { + /*jshint -W035 */ + var part = source[i]; + if (part === "" || part === ".") { + } else if (part === "..") { + if (target.length) { + target.pop(); + } + } else { + target.push(part); + } + /*jshint +W035 */ + } + return target.join("/"); +} + +exports.normalize = normalize; +function normalize(id) { + var match = /^(.*)\.js$/.exec(id); + if (match) { + id = match[1]; + } + return id; +} + +exports.extension = extension; +function extension(location) { + var match = /\.([^\/\.]+)$/.exec(location); + if (match) { + return match[1]; + } +} + diff --git a/merge.js b/merge.js new file mode 100644 index 00000000..adf98b4a --- /dev/null +++ b/merge.js @@ -0,0 +1,23 @@ + +module.exports = merge; +function merge(target, source) { + for (var name in source) { + if (has.call(source, name)) { + var sourceValue = source[name]; + var targetValue = target[name]; + if (sourceValue === null) { + delete target[name]; + } else if ( + typeof sourceValue === "object" && !Array.isArray(sourceValue) && + typeof targetValue === "object" && !Array.isArray(targetValue) + ) { + merge(targetValue, sourceValue); + } else { + target[name] = source[name]; + } + } + } +} + +var has = Object.prototype.hasOwnProperty; + From 60910b88b7a68e5ab1119157598b6a0d4d8ebc8a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 5 Feb 2015 23:59:26 -0800 Subject: [PATCH 63/64] Resolve compiler identifiers --- common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.js b/common.js index 0b249850..17d32d1f 100644 --- a/common.js +++ b/common.js @@ -157,7 +157,7 @@ Require.makeRequire = function (config) { // Then apply configured compilers. module {text, type} to // {dependencies, factory || exports || redirect} if (has.call(config.compilers, module.type)) { - var compilerId = config.compilers[module.type]; + var compilerId = resolve(config.compilers[module.type], ""); return deepLoad(compilerId, "", loading) .then(function () { var compile = require(compilerId); From b20f88977fd4886c1eaaf4981d421c53b5b1e05d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 6 Feb 2015 00:01:10 -0800 Subject: [PATCH 64/64] Switch sourceURL to new style --- require.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/require.js b/require.js index 57c54b99..09ff5760 100644 --- a/require.js +++ b/require.js @@ -74,7 +74,7 @@ Require.Compiler = function Compiler(config) { var factory = globalEval( "(function(" + names.join(",") + "){" + module.text + - "\n//*/\n})\n//@ sourceURL=" + module.location + "\n//*/\n})\n//# sourceURL=" + module.location ); module.factory = function (require, exports, module) { Array.prototype.push.apply(arguments, scopeNames.map(function (name) {