Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ proxyquire.browserify()
.pipe(fs.createWriteStream(__dirname + '/bundle.js'));
```

### proxyquire.plugin()

Instead of being used instead of `browserify()`, proxyquireify can also be used as a browserify plugin.

```js
var browserify = require('browserify');
var proxyquire = require('proxyquireify');

browserify()
.plugin(proxyquire.plugin)
.require(require.resolve('./test'), { entry: true })
.bundle()
.pipe(fs.createWriteStream(__dirname + '/bundle.js'));
```

The plugin is also exported from the file plugin.js so that you can use proxyquireify when running browserify
from the command line.

```sh
browserify -p proxyquireify/plugin test.js > bundle.js
```

### proxyquire(request: String, stubs: Object)

- **request**: path to the module to be tested e.g., `../lib/foo`
Expand Down
53 changes: 33 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,35 @@ function validateArguments(request, stubs) {

var stubs;

function stub(stubs_) {
stubs = stubs_;
function stub(stubs_) {
stubs = stubs_;
// This cache is used by the prelude as an alternative to the regular cache.
// It is not read or written here, except to set it to an empty object when
// adding stubs and to reset it to null when clearing stubs.
module.exports._cache = {};
}

function reset() {
stubs = undefined;
function reset() {
stubs = undefined;
module.exports._cache = null;
}

function fillMissingKeys(mdl, original) {
Object.keys(original).forEach(function (key) {
if (!mdl[key]) mdl[key] = original[key];
if (!mdl[key]) mdl[key] = original[key];
});
if (typeof mdl === 'function' && typeof original === 'function') {
Object.keys(original.prototype).forEach(function (key) {
if (!mdl.prototype[key]) mdl.prototype[key] = original.prototype[key];
});
}

return mdl;
}

var proxyquire = module.exports = function (require_) {
if (typeof require_ != 'function')
throw new ProxyquireifyError(
throw new ProxyquireifyError(
'It seems like you didn\'t initialize proxyquireify with the require in your test.\n'
+ 'Make sure to correct this, i.e.: "var proxyquire = require(\'proxyquireify\')(require);"'
);
Expand All @@ -65,26 +75,29 @@ var proxyquire = module.exports = function (require_) {
};
};

proxyquire.proxy = function (require_) {
return function (request) {
function original() {
return require_(request);
}
// Start with the default cache
proxyquire._cache = null;

if (!stubs) return original();
proxyquire._proxy = function (require_, request) {
function original() {
return require_(request);
}

var stub = stubs[request];
if (!stubs) return original();

if (!stub) return original();
var stub = stubs[request];

var stubWideNoCallThru = !!stubs['@noCallThru'] && stub['@noCallThru'] !== false;
var noCallThru = stubWideNoCallThru || !!stub['@noCallThru'];
return noCallThru ? stub : fillMissingKeys(stub, original());
};
if (!stub) return original();

var stubWideNoCallThru = !!stubs['@noCallThru'] && stub['@noCallThru'] !== false;
var noCallThru = stubWideNoCallThru || !!stub['@noCallThru'];
return noCallThru ? stub : fillMissingKeys(stub, original());
};

if (require.cache) {
// only used during build, so prevent browserify from including it
var hackPrelude = './lib/hack-prelude';
proxyquire.browserify = require(hackPrelude).browserify;
var hackPreludePath = './lib/hack-prelude';
var hackPrelude = require(hackPreludePath);
proxyquire.browserify = hackPrelude.browserify;
proxyquire.plugin = hackPrelude.plugin;
}
32 changes: 17 additions & 15 deletions lib/hack-prelude.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
'use strict';
var fs = require('fs')
, preludePath = require.resolve('browserify/node_modules/browser-pack/_prelude')
, preludeHackPath = require.resolve('./prelude')
, hack = fs.readFileSync(preludeHackPath, 'utf-8');

var fs = require('fs');
var path = require('path');

// browser-pack reads the prelude via readfile sync so we'll override it here
// this way we can swap out what prelude it gets with this little hack
var fs_readFileSync = fs.readFileSync;
fs.readFileSync = function (path) {
if (path === preludePath) return hack;
var preludePath = path.join(__dirname, 'prelude.js');
var prelude = fs.readFileSync(preludePath, 'utf8');

var args = [].slice.call(arguments);
return fs_readFileSync.apply(null, args);
// This plugin replaces the prelude and adds a transform
var plugin = exports.plugin = function (bfy, opts) {
var oldBrowserPack = bfy._browserPack;

bfy._browserPack = function (params) {
params.preludePath = preludePath;
params.prelude = prelude;
return oldBrowserPack(params);
}

bfy.transform(require('./transform'));
};

// Maintain support for the old interface
exports.browserify = function (files) {
delete require.cache[require.resolve('browserify')];
delete require.cache[require.resolve('browserify/node_modules/browser-pack')];

return require('browserify')(files).transform(require('./transform'));
return require('browserify')(files).plugin(plugin);
};
53 changes: 39 additions & 14 deletions lib/prelude.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,37 @@
// anything defined in a previous bundle is accessed via the
// orig method which is the requireuire for previous bundles

(function(modules, cache, entry) {
(function outer (modules, cache, entry) {
// Save the require from previous bundle to this closure if any
var previousRequire = typeof require == "function" && require;

function findProxyquireifyName() {
var deps = Object.keys(modules)
.map(function (k) { return modules[k][1]; });
var deps = Object.keys(modules)
.map(function (k) { return modules[k][1]; });

for (var i = 0; i < deps.length; i++) {
var pq = deps[i]['proxyquireify'];
if (pq) return pq;
}
for (var i = 0; i < deps.length; i++) {
var pq = deps[i]['proxyquireify'];
if (pq) return pq;
}
}

var proxyquireifyName = findProxyquireifyName();

function newRequire(name, jumped){
var m = cache[name];
var dontcache = name !== proxyquireifyName;
// Find the proxyquireify module, if present
var pqify = (proxyquireifyName != null) && cache[proxyquireifyName];

// Proxyquireify provides a separate cache that is used when inside
// a proxyquire call, and is set to null outside a proxyquire call.
// This allows the regular caching semantics to work correctly both
// inside and outside proxyquire calls while keeping the cached
// modules isolated.
// When switching from one proxyquire call to another, it clears
// the cache to prevent contamination between different sets
// of stubs.
var currentCache = (pqify && pqify.exports._cache) || cache;

if(!m || dontcache) {
if(!currentCache[name]) {
if(!modules[name]) {
// if we cannot find the the module within our internal map or
// cache jump to the current global require ie. the last bundle
Expand All @@ -41,13 +51,28 @@
if (previousRequire) return previousRequire(name, true);
throw new Error('Cannot find module \'' + name + '\'');
}
m = cache[name] = {exports:{}};
modules[name][0](function(x){
var m = currentCache[name] = {exports:{}};

// The normal browserify require function
var req = function(x){
var id = modules[name][1][x];
return newRequire(id ? id : x);
},m,m.exports);
};

// The require function substituted for proxyquireify
var moduleRequire = function(x){
var pqify = (proxyquireifyName != null) && cache[proxyquireifyName];
// Only try to use the proxyquireify version if it has been `require`d
if (pqify && pqify.exports._proxy) {
return pqify.exports._proxy(req, x);
} else {
return req(x);
}
};

modules[name][0].call(m.exports,moduleRequire,m,m.exports,outer,modules,currentCache,entry);
}
return cache[name].exports;
return currentCache[name].exports;
}
for(var i=0;i<entry.length;i++) newRequire(entry[i]);

Expand Down
6 changes: 1 addition & 5 deletions lib/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

var through = require('through')
, findDependencies = require('./find-dependencies')
, excludes = {}
, prelude =
'/*proxyquireify override */;var require_ = require;'
+ 'var require = require(\'proxyquireify\').proxy(require_);\n'
;

function requireDependencies(src) {
Expand All @@ -17,6 +13,7 @@ function requireDependencies(src) {

module.exports = function (file) {
if (file === require.resolve('../index')) return through();
if (!/\.js$/.test(file)) return through();
var data = '';

return through(write, end);
Expand All @@ -25,7 +22,6 @@ module.exports = function (file) {
function end() {
var deps = requireDependencies(data);
this.queue(deps);
this.queue(prelude);
this.queue(data);
this.queue(null);
}
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"name": "proxyquireify",
"version": "0.4.0",
"description": "Proxies browserify's require in order to allow overriding dependencies during testing.",
"main": "proxyquireify.js",
"main": "index.js",
"scripts": {
"test": "tap test/*.js && echo 'TODO: node test/clientside/run.js'"
"test": "tap test/*.js && node test/clientside/run.js"
},
"repository": {
"type": "git",
Expand All @@ -19,7 +19,7 @@
"browserify": "~3.44.2",
"mold-source-map": "~0.2.0",
"tap": "~0.4.8",
"tape": "~0.3.2"
"tape": "~2.12.3"
},
"keywords": [
"require",
Expand Down
3 changes: 3 additions & 0 deletions plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file allows using proxyquireify as a browserify plugin named
// 'proxyquireify/plugin'
module.exports = require('./lib/hack-prelude').plugin;