From 2c8949bffba28b5d0e6953d804e9fbb6725fb38e Mon Sep 17 00:00:00 2001 From: Johannes Date: Sat, 23 Jun 2012 16:07:15 +0200 Subject: added comments --- lib/__get__.js | 14 +++-- lib/__set__.js | 14 +++-- lib/browserify/browserifyMiddleware.js | 19 +----- lib/browserify/browserifyRewire.js | 31 ++++++++-- lib/detectStrictMode.js | 7 +++ lib/index.js | 16 ++--- lib/internalRewire.js | 108 +++++++++++++++++++++++++++++++++ lib/rewire.js | 108 --------------------------------- 8 files changed, 170 insertions(+), 147 deletions(-) create mode 100644 lib/internalRewire.js delete mode 100644 lib/rewire.js (limited to 'lib') diff --git a/lib/__get__.js b/lib/__get__.js index 47bfb66..d8cdfe7 100644 --- a/lib/__get__.js +++ b/lib/__get__.js @@ -2,14 +2,20 @@ * This function will be stringified and then injected into every rewired module. * Then you can leak private variables by calling myModule.__get__("myPrivateVar"); * + * All variables within this function are namespaced in the arguments array because every + * var declaration could possibly clash with a variable in the module scope. + * * @param {!String} name name of the variable to retrieve * @throws {TypeError} * @return {*} */ -module.exports = function __get__(name) { - if (typeof name !== "string" || name.length === 0) { +function __get__() { + arguments.varName = arguments[0]; + if (typeof arguments.varName !== "string" || arguments.varName.length === 0) { throw new TypeError("__get__ expects a non-empty string"); } - return eval(name); -}; \ No newline at end of file + return eval(arguments.varName); +} + +module.exports = __get__; \ No newline at end of file diff --git a/lib/__set__.js b/lib/__set__.js index 49e4494..79175ed 100644 --- a/lib/__set__.js +++ b/lib/__set__.js @@ -8,14 +8,16 @@ * @param {!String|!Object} varName name of the variable to set * @param {String} varValue new value * @throws {TypeError} + * @throws {ReferenceError} When the variable is unknown * @return {*} */ -module.exports = function __set__() { +function __set__() { arguments.varName = arguments[0]; arguments.varValue = arguments[1]; arguments.src = ""; - arguments.checkExistsSrc = function (varName) { - return "if (typeof " + varName + " === 'undefined') { throw new ReferenceError('Cannot __set__(): " + varName + " is not declared within the module.');} "; + arguments.checkExistsSrc = function (varName, varValue) { + return "if (typeof " + varName + " === 'undefined') { throw new ReferenceError('Cannot __set__(" + varName + ", " + varValue + "): " + + varName + " is not declared within the module.'); } "; }; if (typeof arguments[0] === "object") { @@ -32,10 +34,12 @@ module.exports = function __set__() { if (!arguments.varName) { throw new TypeError("__set__ expects a non-empty string as a variable name"); } - arguments.src = arguments.checkExistsSrc(arguments.varName) + arguments.varName + " = arguments.varValue;"; + arguments.src = arguments.checkExistsSrc(arguments.varName, arguments.varValue) + arguments.varName + " = arguments.varValue;"; } else { throw new TypeError("__set__ expects an environment object or a non-empty string as a variable name"); } eval(arguments.src); -}; \ No newline at end of file +} + +module.exports = __set__; \ No newline at end of file diff --git a/lib/browserify/browserifyMiddleware.js b/lib/browserify/browserifyMiddleware.js index 619049c..d62352e 100644 --- a/lib/browserify/browserifyMiddleware.js +++ b/lib/browserify/browserifyMiddleware.js @@ -27,16 +27,6 @@ function getInjectionSrc() { 'require = window.browserifyRequire.getProxy(require, __filename);'; } -function wrapCodeInDecorativeComments(filename, src) { - var topLine = "", - bottomLine = "", - lineLength = 80; - - while (topLine.length <= lineLength) { - - } -} - function browserifyMiddleware(b) { function injectRewire(src, filename) { var rewireRequires, @@ -68,16 +58,11 @@ function browserifyMiddleware(b) { if (filename.indexOf("/rewire/lib") === -1) { src = strictMode + // either '' or ' "use strict"; ' + "/* this line was injected by rewire() */" + "var global = window; " + // window is our new global object importGlobalsSrc + injectionSrc + "\n" + - // For a better debugging experience we're adding a comment with the filename - "//// " + filename + " /////////////////////////////////////////////////////////////////////////////////////////////////////////////\n" + - "\n" + - src + "\n" + - "\n" + - "/////" + filename.replace(/./g, "/") + "//////////////////////////////////////////////////////////////////////////////////////////////////////////////\n" + - "//@ sourceURL=" + filename + "\n"; + src; } return src; diff --git a/lib/browserify/browserifyRewire.js b/lib/browserify/browserifyRewire.js index 59ba8cf..d70f31e 100644 --- a/lib/browserify/browserifyRewire.js +++ b/lib/browserify/browserifyRewire.js @@ -1,14 +1,26 @@ var pathUtil = require("path"), browserifyRequire = window.browserifyRequire; +// Saves all setters and getters for every module according to its filename var registry = {}, - rewiredModules = []; // cache for all rewired modules so it can be reset anytime +// Cache for all rewired modules so it can be reset anytime + rewiredModules = []; -function rewire(parentModulePath, targetPath, cache) { +/** + * Executes the given module and adds a special setter and getter that allow you to set and get private variables. + * The parentModulePath is usually set by the requireProxy. + * + * @param {!String} parentModulePath __filename of the module, that wants to rewire() another module. + * @param {!String} targetPath path to the module that shall be rewired + * @param {Boolean=true} cache indicates whether the rewired module should be cached or not + * @return {Object} + */ +function browserifyRewire(parentModulePath, targetPath, cache) { var originalModule, rewiredModule = {}, registeredTargetModule; + // Default cache to true if (cache === undefined) { cache = true; } @@ -29,12 +41,14 @@ function rewire(parentModulePath, targetPath, cache) { // @see https://github.com/substack/node-browserify/issues/132#issuecomment-5281470 originalModule = (require)(targetPath); + // Copy all exported values to our rewired module for (var key in originalModule) { if (originalModule.hasOwnProperty(key)) { rewiredModule[key] = originalModule[key]; } } + // If caching is enabled we store the rewiredModule in the cache if (cache) { browserifyRequire.modules[targetPath]._cached = rewiredModule; } @@ -52,7 +66,14 @@ function rewire(parentModulePath, targetPath, cache) { return rewiredModule; } -rewire.register = function (filename, setter, getter) { +/** + * Registers the setter and getter of every module according to its filename + * + * @param {!String} filename the absolute path to the module (module id) + * @param {!Function} setter + * @param {!Function} getter + */ +browserifyRewire.register = function (filename, setter, getter) { registry[filename] = { setter: setter, getter: getter @@ -62,7 +83,7 @@ rewire.register = function (filename, setter, getter) { /** * Deletes all rewired modules from the cache */ -rewire.reset = function () { +browserifyRewire.reset = function () { var modules = browserifyRequire.modules, i; @@ -73,4 +94,4 @@ rewire.reset = function () { rewiredModules = []; }; -module.exports = rewire; \ No newline at end of file +module.exports = browserifyRewire; \ No newline at end of file diff --git a/lib/detectStrictMode.js b/lib/detectStrictMode.js index d0107d0..347c455 100644 --- a/lib/detectStrictMode.js +++ b/lib/detectStrictMode.js @@ -1,3 +1,10 @@ +/** + * Returns true if the source code is intended to run in strict mode. Does not detect + * "use strict" if it occurs in a nested function. + * + * @param {!String} src + * @return {Boolean} + */ function detectStrictMode(src) { return (/^\s*"use strict";/g).test(src); } diff --git a/lib/index.js b/lib/index.js index 6763399..491599a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,30 +1,30 @@ var rewireModule; /** - * This function is needed to determine the calling parent module. - * Thus rewire acts exactly the same like require() in the test module. + * Adds a special setter and getter to the module located at filename. After the module has been rewired, you can + * call myModule.__set__(name, value) and myModule.__get__(name) to manipulate private variables. * - * @param {!String} request Path to the module that shall be rewired. Use it exactly like require(). + * @param {!String} filename Path to the module that shall be rewired. Use it exactly like require(). * @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, cache) { - delete require.cache[__filename]; // deleting self from module cache so the parent module is always up to date - +function rewire(filename, cache) { if (cache === undefined) { cache = true; } - return rewireModule(module.parent, request, cache); + return rewireModule(module.parent, filename, cache); } // Conditional require for different environments if (process.title === "browser") { module.exports = require("./browserify/browserifyRewire.js"); } else { + delete require.cache[__filename]; // deleting self from module cache so the parent module is always up to date + // Putting (require) within brackets is a hack to disable browserify's require sniffing // @see https://github.com/substack/node-browserify/issues/132#issuecomment-5281470 - rewireModule = (require)("./rewire.js"); + rewireModule = (require)("./internalRewire.js"); rewire.reset = rewireModule.reset; rewire.browserify = (require)("./browserify/browserifyMiddleware.js"); diff --git a/lib/internalRewire.js b/lib/internalRewire.js new file mode 100644 index 0000000..b4c8e62 --- /dev/null +++ b/lib/internalRewire.js @@ -0,0 +1,108 @@ +var Module = require("module"), + fs = require("fs"), + __get__ = require("./__get__.js"), + __set__ = require("./__set__.js"), + getImportGlobalsSrc = require("./getImportGlobalsSrc.js"), + detectStrictMode = require("./detectStrictMode.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[0] = moduleWrapper0; + Module.wrapper[1] = moduleWrapper1; +} + +/** + * Does actual rewiring the module. For further documentation @see index.js + */ +function internalRewire(parentModulePath, targetPath, cache) { + var testModule, + nodeRequire, + prepend, + append, + src; + + /** + * 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 + testModule.require = nodeRequire; // restoring original nodeRequire + return nodeRequire.call(testModule, path); // node's require only works when "this" points to the module + } + + // Checking params + if (typeof targetPath !== "string") { + throw new TypeError("Filename must be a string"); + } + + // Resolve full filename relative to the parent module + targetPath = Module._resolveFilename(targetPath, parentModulePath); + + // 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 + if (Array.isArray(targetPath)) { + targetPath = targetPath[1]; + } + + // Create testModule as it would be created by require() + testModule = new Module(targetPath, parentModulePath); + + // Patching requireProxy + nodeRequire = testModule.require; + testModule.require = requireProxy; + + // 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() + "; "; + + // Check if the module uses the strict mode. + // If so we must ensure that "use strict"; stays at the beginning of the module. + src = fs.readFileSync(targetPath, "utf8"); + if (detectStrictMode(src) === true) { + prepend = ' "use strict"; ' + prepend; + } + + // Apply prepend and appends + 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) { + rewiredModules.push(targetPath); // save in private cache for .reset() + require.cache[targetPath] = testModule; + } + + // This is only necessary if nothing has been required within the module + restoreOriginalWrappers(); + + return testModule.exports; +} + +/** + * Deletes all rewired modules from the cache + */ +internalRewire.reset = function () { + var i; + + for (i = 0; i < rewiredModules.length; i++) { + delete require.cache[rewiredModules[i]]; + } + + rewiredModules = []; +}; + +module.exports = internalRewire; \ No newline at end of file diff --git a/lib/rewire.js b/lib/rewire.js deleted file mode 100644 index 080847f..0000000 --- a/lib/rewire.js +++ /dev/null @@ -1,108 +0,0 @@ -var Module = require("module"), - fs = require("fs"), - __get__ = require("./__get__.js"), - __set__ = require("./__set__.js"), - getImportGlobalsSrc = require("./getImportGlobalsSrc.js"), - detectStrictMode = require("./detectStrictMode.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[0] = moduleWrapper0; - Module.wrapper[1] = moduleWrapper1; -} - -/** - * Does actual rewiring the module. For further documentation @see index.js - */ -function rewire(parentModulePath, targetPath, cache) { - var testModule, - nodeRequire, - prepend, - append, - src; - - /** - * 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 - testModule.require = nodeRequire; // restoring original nodeRequire - return nodeRequire.call(testModule, path); // node's require only works when "this" points to the module - } - - // Checking params - if (typeof targetPath !== "string") { - throw new TypeError("Filename must be a string"); - } - - // Resolve full filename relative to the parent module - targetPath = Module._resolveFilename(targetPath, parentModulePath); - - // 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 - if (Array.isArray(targetPath)) { - targetPath = targetPath[1]; - } - - // Create testModule as it would be created by require() - testModule = new Module(targetPath, parentModulePath); - - // Patching requireProxy - nodeRequire = testModule.require; - testModule.require = requireProxy; - - // 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() + "; "; - - // Check if the module uses the strict mode. - // If so we must ensure that "use strict"; stays at the beginning of the module. - src = fs.readFileSync(targetPath, "utf8"); - if (detectStrictMode(src) === true) { - prepend = ' "use strict"; ' + prepend; - } - - // Apply prepend and appends - 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) { - rewiredModules.push(targetPath); // save in private cache for .reset() - require.cache[targetPath] = testModule; - } - - // 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; - - for (i = 0; i < rewiredModules.length; i++) { - delete require.cache[rewiredModules[i]]; - } - - rewiredModules = []; -}; - -module.exports = rewire; \ No newline at end of file -- cgit v1.2.3