Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/rewire.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJohannes <mail@johannesewald.de>2012-06-12 01:29:10 +0400
committerJohannes <mail@johannesewald.de>2012-06-12 01:29:10 +0400
commit0af1dcb6b2ccbc7ec0ae757549688e666cf569a7 (patch)
tree4b3854b937ff1f05bf8677c8fc1547fa733e6feb /lib
parent75f6a7765a09344dab54a4a2ca99ac12f547a75d (diff)
- changed APIv0.2.0
- introduced __set__ and __get__ to rewired modules
Diffstat (limited to 'lib')
-rw-r--r--lib/__get__.js17
-rw-r--r--lib/__set__.js41
-rw-r--r--lib/getImportGlobalsSrc.js28
-rw-r--r--lib/getInjectionSrc.js37
-rw-r--r--lib/getLeakingSrc.js29
-rw-r--r--lib/index.js4
-rw-r--r--lib/rewire.js83
7 files changed, 136 insertions, 103 deletions
diff --git a/lib/__get__.js b/lib/__get__.js
new file mode 100644
index 0000000..64c467d
--- /dev/null
+++ b/lib/__get__.js
@@ -0,0 +1,17 @@
+"use strict"; // run code in ES5 strict mode
+
+/**
+ * This function will be stringified and then injected into every rewired module.
+ * Then you can leak private variables by calling myModule.__get__("myPrivateVar");
+ *
+ * @param {!String} name name of the variable to retrieve
+ * @throws {TypeError}
+ * @return {*}
+ */
+module.exports = function __get__(name) {
+ if (typeof name !== "string" || name == false) {
+ throw new TypeError("__get__ expects a non-empty string");
+ }
+
+ return eval(name);
+}; \ No newline at end of file
diff --git a/lib/__set__.js b/lib/__set__.js
new file mode 100644
index 0000000..7de8eec
--- /dev/null
+++ b/lib/__set__.js
@@ -0,0 +1,41 @@
+"use strict"; // run code in ES5 strict mode
+
+/**
+ * This function will be stringified and then injected into every rewired module.
+ * Then you can set private variables by calling myModule.__set__("myPrivateVar", newValue);
+ *
+ * @param {!String|!Object} varName name of the variable to set
+ * @param {String} varValue new value
+ * @throws {TypeError}
+ * @return {*}
+ */
+module.exports = function __set__(varName, varValue) {
+ var key,
+ env,
+ src = "";
+
+ function checkExistsSrc(varName) {
+ return "if (typeof " + varName + " === 'undefined') { throw new ReferenceError('" + varName + " is not defined');} ";
+ }
+
+ if (typeof varName === "object") {
+ env = varName;
+ if (!env || Array.isArray(env)) {
+ throw new TypeError("__set__ expects an object as env");
+ }
+ for (key in env) {
+ if (env.hasOwnProperty(key)) {
+ src += checkExistsSrc(key) + key + " = env." + key + ";";
+ }
+ }
+ } else if (typeof varName === "string") {
+ if (!varName) {
+ throw new TypeError("__set__ expects a non-empty string as a variable name");
+ }
+ src = checkExistsSrc(varName) + varName + " = varValue;"
+ } else {
+ throw new TypeError("__set__ expects an environment object or a non-empty string as a variable name");
+ }
+
+ eval(src);
+}; \ No newline at end of file
diff --git a/lib/getImportGlobalsSrc.js b/lib/getImportGlobalsSrc.js
new file mode 100644
index 0000000..1bcfed4
--- /dev/null
+++ b/lib/getImportGlobalsSrc.js
@@ -0,0 +1,28 @@
+"use strict"; // run code in ES5 strict mode
+
+/**
+ * Declares all globals with a var and assigns the global object. Thus you're able to
+ * override globals without changing the global object itself.
+ *
+ * Returns something like
+ * "var console = console; var process = process; ..."
+ *
+ * @return {String}
+ */
+function getImportGlobalsSrc() {
+ var key,
+ value,
+ src = "";
+
+ for (key in global) {
+ if (global.hasOwnProperty(key) && key !== "global") {
+ value = global[key];
+ src += "var " + key + " = global." + key + "; ";
+ }
+ }
+
+
+ return src;
+}
+
+module.exports = getImportGlobalsSrc; \ No newline at end of file
diff --git a/lib/getInjectionSrc.js b/lib/getInjectionSrc.js
deleted file mode 100644
index 74174d0..0000000
--- a/lib/getInjectionSrc.js
+++ /dev/null
@@ -1,37 +0,0 @@
-"use strict"; // run code in ES5 strict mode
-
-var toSrc = require("toSrc");
-
-/**
- * Returns the source code for injecting vars.
- *
- * e.g.:
- * "var console=123;"
- *
- * @param {Object} obj
- * @return {String}
- */
-function getInjectionSrc(obj) {
- function walkObj(obj, level) {
- var key,
- value,
- src = "";
-
- for (key in obj) {
- if (obj.hasOwnProperty(key)) {
- value = obj[key];
- if (level === 0) {
- src += "var "; // on the top level we need a var statement
- }
- src += key + "=" + toSrc(value, 9999) + ";";
- }
- }
-
-
- return src;
- }
-
- return walkObj(obj, 0);
-}
-
-module.exports = getInjectionSrc; \ No newline at end of file
diff --git a/lib/getLeakingSrc.js b/lib/getLeakingSrc.js
deleted file mode 100644
index 882300b..0000000
--- a/lib/getLeakingSrc.js
+++ /dev/null
@@ -1,29 +0,0 @@
-"use strict"; // run code in ES5 strict mode
-
-/**
- * Returns the source code that will leak private vars.
- *
- * e.g.:
- * "module.exports.__ = {myPrivateVar: myPrivateVar};"
- *
- * @param {Array<String>} leaks
- * @return {String}
- */
-function getLeakingSrc(leaks) {
- var src = "module.exports.__ = {",
- varName,
- i;
-
- for (i = 0; i < leaks.length; i++) {
- varName = leaks[i];
- src += (varName + ":" + varName + ",");
- }
- if (i > 0) {
- src = src.slice(0, -1); // trim last comma
- }
- src += "};";
-
- return src;
-}
-
-module.exports = getLeakingSrc;
diff --git a/lib/index.js b/lib/index.js
index 8be8897..a6dbcd6 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -13,14 +13,14 @@ var rewireModule = require("./rewire.js");
* @param {Boolean} cache Indicates whether the rewired module should be cached by node so subsequent calls of require() will return the rewired module. Subsequent calls of rewire() will always overwrite the cache.
* @return {*} the rewired module
*/
-function rewire(request, mocks, injections, leaks, cache) {
+function rewire(request, cache) {
delete require.cache[__filename]; // deleting self from module cache so the parent module is always up to date
if (cache === undefined) {
cache = true;
}
- return rewireModule(module.parent, request, mocks, injections, leaks, cache);
+ return rewireModule(module.parent, request, cache);
}
rewire.reset = rewireModule.reset;
diff --git a/lib/rewire.js b/lib/rewire.js
index 33f5d22..ae23fb1 100644
--- a/lib/rewire.js
+++ b/lib/rewire.js
@@ -1,32 +1,38 @@
"use strict"; // run code in ES5 strict mode
var Module = require("module"),
- nodeWrapper0 = Module.wrapper[0], // caching original wrapper
- nodeWrapper1 = Module.wrapper[1],
- getLeakingSrc = require("./getLeakingSrc.js"),
- getInjectionSrc = require("./getInjectionSrc.js"),
- rewiredModules = [];
+ __get__ = require("./__get__.js"),
+ __set__ = require("./__set__.js"),
+ getImportGlobalsSrc = require("./getImportGlobalsSrc.js"),
+
+ moduleWrapper0 = Module.wrapper[0], // caching original wrapper
+ moduleWrapper1 = Module.wrapper[1], // caching original wrapper
+ rewiredModules = []; // cache for all rewired modules so it can be reset anytime
function restoreOriginalWrappers() {
- Module.wrapper[1] = nodeWrapper1;
+ Module.wrapper[0] = moduleWrapper0;
+ Module.wrapper[1] = moduleWrapper1;
}
/**
* Does actual rewiring the module. For further documentation @see index.js
*/
-function rewire(parentModule, filename, mocks, injections, leaks, cache) {
+function rewire(parentModule, filename, cache) {
var testModule,
nodeRequire,
- wrapperExtensions = "";
-
- function requireMock(path) {
+ prepend,
+ append;
+
+ /**
+ * Proxies the first require call in order to draw back all changes.
+ * Thus our changes don't influence other modules
+ *
+ * @param {!String} path
+ */
+ function requireProxy(path) {
restoreOriginalWrappers(); // we need to restore the wrappers now so we don't influence other modules
-
- if (mocks && mocks.hasOwnProperty(path)) {
- return mocks[path];
- } else {
- return nodeRequire.call(testModule, path); // node's require only works when "this" points to the module
- }
+ testModule.require = nodeRequire; // restoring original nodeRequire
+ return nodeRequire.call(testModule, path); // node's require only works when "this" points to the module
}
// Checking params
@@ -34,8 +40,8 @@ function rewire(parentModule, filename, mocks, injections, leaks, cache) {
throw new TypeError("Filename must be a string");
}
- // Init vars
- filename = Module._resolveFilename(filename, parentModule); // resolve full filename relative to the parent module
+ // Resolve full filename relative to the parent module
+ filename = Module._resolveFilename(filename, parentModule);
// Special support for older node versions that returned an array on Module._resolveFilename
// @see https://github.com/joyent/node/blob/865b077819a9271a29f982faaef99dc635b57fbc/lib/module.js#L319
@@ -43,37 +49,44 @@ function rewire(parentModule, filename, mocks, injections, leaks, cache) {
filename = filename[1];
}
+ // Create testModule as it would be created by require()
testModule = new Module(filename, parentModule);
- nodeRequire = testModule.require; // caching original node require
- // Prepare module for injection
- if (typeof injections === "object") {
- wrapperExtensions += getInjectionSrc(injections);
- } else if (typeof injections === "string") {
- wrapperExtensions += injections;
- }
+ // Patching requireProxy
+ nodeRequire = testModule.require;
+ testModule.require = requireProxy;
- // Prepare module for leaking private vars
- if (Array.isArray(leaks)) {
- wrapperExtensions += getLeakingSrc(leaks);
- }
- Module.wrapper[1] = wrapperExtensions + nodeWrapper1;
+ // We prepend a list of all globals declared with var so they can be overridden (without changing original globals)
+ prepend = getImportGlobalsSrc();
+
+ // We append our special setter and getter.
+ append = "module.exports.__set__ = " + __set__.toString() + "; ";
+ append += "module.exports.__get__ = " + __get__.toString() + "; ";
- // Mocking module.require-function
- testModule.require = requireMock;
- // Loading module
+ // Apply prepend and append
+ Module.wrapper[0] = moduleWrapper0 + prepend;
+ Module.wrapper[1] = append + moduleWrapper1;
+
+ //console.log(Module.wrapper);
+
+ // Let the show begin
testModule.load(testModule.id);
+ // Store the rewired module in the cache when enabled
if (cache) {
- require.cache[filename] = testModule;
rewiredModules.push(filename); // save in private cache for .reset()
+ require.cache[filename] = testModule;
}
- restoreOriginalWrappers(); // this is only necessary if nothing has been required within the module
+ // This is only necessary if nothing has been required within the module
+ restoreOriginalWrappers();
return testModule.exports;
}
+/**
+ * Deletes all rewired modules from the cache
+ */
rewire.reset = function () {
var i;