Last active
August 29, 2015 14:06
-
-
Save devijvers/d01c177944b2223de8c1 to your computer and use it in GitHub Desktop.
required(): hack the node.js require cache for fun and profit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require("coffee-script/register"); | |
| required = require("./required.coffee"); | |
| _ = require("lodash"); | |
| // require("underscore") will now work in every module | |
| required("underscore", _); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # License: MIT | |
| # Copyright Steven Devijver, 2014 | |
| # | |
| # required(): hack the node.js require cache | |
| # | |
| # By design not suitable for production environments. | |
| # Use at your own risk. | |
| # This script has only been tested with global modules. | |
| # | |
| # Example usage: | |
| # | |
| # // main.js | |
| # require("coffee-script/register"); | |
| # required = require("./required.coffee"); | |
| # _ = require("lodash"); | |
| # | |
| # // require("underscore") will now work in every module | |
| # required("underscore", _); | |
| _module = require("module") | |
| _ = require("lodash") | |
| path = require("path") | |
| fs = require("fs") | |
| # ##### HELPER FUNCTIONS ##### | |
| exists = (location) -> | |
| try | |
| fs.statSync location | |
| true | |
| catch error | |
| false | |
| mkdirs = (dirs..., last) -> | |
| flattened = _.flatten([dirs, [last]]) | |
| if last | |
| location = path.join.apply this, flattened | |
| else | |
| location = path.join.apply this, dirs | |
| if !exists location | |
| mkdirs dirs | |
| fs.mkdirSync location | |
| location | |
| # Keep require.cache handy | |
| # This cache is used by node.js to store loaded modules. | |
| # We're going to add 'fake' yet real-looking modules. | |
| cache = require.cache | |
| # Get hold of the root module, that is the main script. | |
| # It always has id '.' | |
| getRootModule = -> | |
| key = _.findKey(cache, (module) -> "." == module.id) | |
| cache[key] | |
| rootModule = getRootModule() | |
| # We need the directory where the main script resides | |
| basedir = path.dirname rootModule.filename | |
| # When you require() a module node.js will check if the | |
| # corresponding file exists, even after the module has | |
| # already been loaded in the cache. | |
| # Since we'll add our entries to the cache we still | |
| # need to provide a directory with the module name and | |
| # a index.js file within that directory if we want to | |
| # mislead node.js in returning our module from the cache. | |
| # First let's create a cache directory that will hold | |
| # module directories for us. We don't want to pollute | |
| # node_modules/. We'll create node_modules/required_cache | |
| # in the directory where the main script resides. | |
| cacheDir = mkdirs basedir, "node_modules", 'required_cache' | |
| # Next we have to tell node that it needs to look in the | |
| # required_cache directory when looking for global modules. | |
| # There is no API to do this, the only vector left is hacking | |
| # which unavoidably means trouble. | |
| # When node starts it reads the NODE_PATH environment variable | |
| # and adds directories specified there to the global path array. | |
| # However, node ONLY does that at startup. By the time | |
| # required.coffee is being loaded it's too late to add global paths. | |
| # That's to say, too late unless you don't mind screwing around | |
| # with the module system of the VM under your feet. | |
| # First we add the node_modules/required_cache directory | |
| # to the NODE_PATH entry in the process.env object. | |
| process.env['NODE_PATH'] += path.delimiter + cacheDir | |
| # Now we re-launch the _initPaths() function in lib/module.js. | |
| # The node developers never intended this function to be executed | |
| # after startup. Problem? | |
| _module._initPaths() | |
| # It turns out, no. All it does is update the paths array that | |
| # node uses to hunt for global modules. Still, don't use this | |
| # in a production environment. | |
| # The actual required() function. Pass in a module name and | |
| # the exports you want to expose and you're done. | |
| required = (module, exports) -> | |
| # Create the node_modules/required_cache/<module> directory | |
| moduleCacheDir = mkdirs cacheDir, module | |
| # Touch the node_modules/required_cache/<module>/index.js file | |
| cacheFile = path.join moduleCacheDir, "index.js" | |
| fd = fs.openSync cacheFile, 'a+', 666 | |
| fs.writeSync fd, "" | |
| fs.closeSync fd | |
| # Add the exports to require.cache in the format node expects. | |
| cache[cacheFile] = | |
| id: module | |
| filename: cacheFile | |
| exports: exports | |
| parent: rootModule | |
| exports | |
| module.exports = required |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment