intercept-require
Advanced tools
Comparing version
140
lib/index.js
@@ -12,59 +12,46 @@ // FORKED FROM https://bitbucket.org/ralphv/require-hook/ | ||
var moduleProto = Module.prototype; | ||
var originalRequire = moduleProto.require; | ||
function createInterceptor (__require, interceptor, config) { | ||
function defaultConfig () { | ||
return { | ||
testOnlySubPath: [], | ||
alternateProjectPaths: [] | ||
}; | ||
} | ||
return function interceptedRequire (moduleId) { | ||
var interceptorResult; | ||
var __exports = null; | ||
var info = generateRequireInfo(moduleId); | ||
var SEP = path.sep; | ||
// config.shortCircuit can avoid disk I/O entirely | ||
if (config.shortCircuit && interceptor) { | ||
info.attemptedShortCircuit = true; | ||
var requireListener, config; | ||
// either no matcher function, or matcher succeeds | ||
if (!config.shortCircuitMatch || config.shortCircuitMatch(info)) { | ||
info.didShortCircuit = true; | ||
return interceptor(null, info); | ||
} | ||
} | ||
function interceptedRequire (moduleId) { | ||
var listenerResult; | ||
var result = null; | ||
var error = null; | ||
var info = generateRequireInfo(moduleId); | ||
try { | ||
/*jshint validthis:true */ | ||
__exports = __require.apply(this, arguments); | ||
/*jshint validthis:false */ | ||
} catch (err) { | ||
info.error = err; | ||
} | ||
// config.shortCircuit can avoid disk I/O entirely | ||
if (config.shortCircuit && requireListener) { | ||
info.attemptedShortCircuit = true; | ||
// either no matcher function, or matcher succeeds | ||
if (!config.shortCircuitMatch || config.shortCircuitMatch(info)) { | ||
info.didShortCircuit = true; | ||
return requireListener(null, info); | ||
// if there's an interceptor, call it | ||
if (interceptor) { | ||
interceptorResult = interceptor(__exports, info); | ||
return interceptorResult == null ? __exports : interceptorResult; | ||
} | ||
} | ||
try { | ||
/*jshint validthis:true */ | ||
result = originalRequire.apply(this, arguments); | ||
/*jshint validthis:false */ | ||
} catch (err) { | ||
result = null; | ||
error = err; | ||
// otherwise behave normally; throw an error if it occurred | ||
// else just return the result of `require()` | ||
if (info.error) throw info.error; | ||
return __exports; | ||
} | ||
info.error = error; | ||
} | ||
// if there's a listener, do listener things | ||
if (requireListener) { | ||
listenerResult = requireListener(result, info); | ||
return (listenerResult == null ? result : listenerResult); | ||
} | ||
// otherwise behave normally; throw an error if it occurred | ||
// else just return the result of `require()` | ||
if (error) throw error; | ||
return result; | ||
} | ||
var localModuleRe = /^[\/\.]/; | ||
function isLocal (moduleId) { | ||
return localModuleRe.test(moduleId) | ||
return localModuleRe.test(moduleId); | ||
} | ||
@@ -83,24 +70,5 @@ | ||
function isAbsPath (requireString) { | ||
return requireString[0] === SEP; | ||
return requireString[0] === path.sep; | ||
} | ||
function isNative (moduleId) { | ||
return process.binding("natives").hasOwnProperty(moduleId); | ||
} | ||
var localModuleRe = /^[\/\.]/; | ||
function isLocal (moduleId) { | ||
return localModuleRe.test(moduleId); | ||
} | ||
function isThirdParty (moduleId) { | ||
return !isNative(moduleId) && !isLocal(moduleId); | ||
} | ||
function isTestOnly (callingFile) { | ||
return config.testOnlySubPath.some(function (pathPiece) { | ||
return callingFile.indexOf(pathPiece) !== -1; | ||
}); | ||
} | ||
function resolveAbsolutePath (callingFile, moduleId) { | ||
@@ -110,20 +78,25 @@ if (isAbsPath(moduleId)) { | ||
} | ||
var dir = path.dirname(callingFile); | ||
if (typeof dir === "string" && typeof moduleId === "string") { | ||
return path.join(dir, moduleId); | ||
} | ||
return null; | ||
} | ||
var safeCallerList = {"module.js": null}; | ||
function getCallingFile () { | ||
var stack = callsite(); | ||
var currentFile = stack.shift().getFileName(); | ||
var safeCallerList = new Set(["internal/module.js", "module.js"]); | ||
while (stack.length) { | ||
var callingFile = stack.shift().getFileName(); | ||
if (callingFile !== currentFile && !safeCallerList.hasOwnProperty(callingFile)) { | ||
if (callingFile !== currentFile && !safeCallerList.has(callingFile)) { | ||
return callingFile; | ||
} | ||
} | ||
return "[Unknown calling file.]"; | ||
return "[Unknown calling file]"; | ||
} | ||
@@ -158,31 +131,20 @@ | ||
absPathResolvedCorrectly: absPathResolvedCorrectly, | ||
testOnly: isTestOnly(callingFile), | ||
local: local, | ||
error: null, | ||
}; | ||
} | ||
var api = module.exports = { | ||
attach: function attach (settings) { | ||
settings = settings || {}; | ||
assert.equal((typeof settings), "object", "argument `settings` must be an object or null"); | ||
config = assign(defaultConfig(), settings); | ||
moduleProto.require = interceptedRequire; | ||
}, | ||
function intercept (fn, settings) { | ||
assert(typeof fn === "function", "argument must be a function."); | ||
detach: function detach () { | ||
moduleProto.require = originalRequire; | ||
config = null; | ||
api.resetListener(); | ||
}, | ||
var config = assign({}, settings || {}); | ||
var original = Module.prototype.require; | ||
setListener: function setListener (listener) { | ||
assert.equal((typeof listener), "function", "argument `listener` must be a function.") | ||
requireListener = listener; | ||
}, | ||
Module.prototype.require = createInterceptor(original, fn, config); | ||
resetListener: function resetListener () { | ||
requireListener = null; | ||
}, | ||
return function restore () { | ||
Module.prototype.require = original; | ||
}; | ||
} | ||
originalRequire: originalRequire | ||
}; | ||
module.exports = intercept; |
{ | ||
"name": "intercept-require", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "Intercept calls to require()", | ||
@@ -25,19 +25,17 @@ "keywords": [ | ||
"callsite": "^1.0.0", | ||
"lodash.find": "^2.4.1", | ||
"lodash.intersection": "^2.4.1", | ||
"object-assign": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"butt": "^2.0.0", | ||
"chai": "^1.10.0", | ||
"grunt": "^0.4.5", | ||
"codecov.io": "0.0.8", | ||
"expect": "^1.20.2", | ||
"istanbul": "^0.3.5", | ||
"jshint": "^2.5.11", | ||
"mocha": "^2.1.0", | ||
"should": "*" | ||
"mocha": "^2.1.0" | ||
}, | ||
"scripts": { | ||
"lint": "jshint", | ||
"test": "mocha ./test/index-test.js" | ||
"test": "mocha ./test/test.js", | ||
"cover": "node --harmony ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- ./test/index-test.js -R spec", | ||
"codecovio": "npm run cover; cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js --verbose" | ||
}, | ||
"main": "index.js" | ||
} |
100
README.md
@@ -1,4 +0,1 @@ | ||
#### forked from [https://bitbucket.org/ralphv/require-hook](https://bitbucket.org/ralphv/require-hook) | ||
--------------------------------------------------------------------------------------------------- | ||
# intercept-require [](https://travis-ci.org/nickb1080/intercept-require) | ||
@@ -13,78 +10,33 @@ | ||
## API | ||
#### `.attach([Object settings])` | ||
Replace `Module.prototype.require` with an intercepting function. Calls to `require()` continue to behave normally until a listener is set. `settings` is optional, and accepts two options `Boolean shortCircuit` and `Function shortCircuitMatch<Object info>`. | ||
Short-circuiting allows a consumer to skip disk I/O entirely. In normal situatons, `intercept-require` makes a real `require()` call and intercepts it _on the way back_. Short-circuiting skips this step. This is probably useful only in obscure cases. Further, in the few cases where short-circuiting is necessary, it's unlikely that all `require()` calls need to be short-circuited. `shortCircuitMatch` is a function which is passed the `info` object and returns whether or not the call should be short-circuited. | ||
#### `.detach()` | ||
Restore `Module.prototype.require` to it's original value. This also resets the `listener`, so that if `.attach()` is later called, no listener will initially be set. | ||
#### `.setListener(Function<Object info, Object result> listener)` | ||
Set the listener that will be invoked on every `require()` call. The listener is passed two arguments: an `info` object that represents some metadata about the `require()` call and the module that was found, and an `result` object which contains the `module.exports` of whatever module would have been found had `require()` been called normally, **unless** the `require()` call throws, in which case `result` will be `undefined` and `info.error` will contain the caught error. | ||
When short-circuiting is active, `result` will be null. | ||
The return value of `setListener()` is passed to the requiring module as the return value of `require()` **unless an error is returned, in which case it will be thrown**. If you want to handle (and possibly recover from) errors, then | ||
#### `.resetListener()` | ||
Discard the current `listener`. Until another listener is set, all `require()` calls will behave as normal. | ||
#### `.originalRequire` | ||
A reference to the original function found at `Module.prototype.require`. It's technically possible that this isn't the built in function if something else has overwritten it before `intercept-require` is run. | ||
## Example | ||
```js | ||
var intercept = require("intercept-require"); | ||
intercept.attach(); | ||
// Module.prototype.require is now overwritten with the interceptor... | ||
const intercept = require("intercept-require"); | ||
// in this example, just transparently log every require | ||
const restore = intercept(function (moduleExport, info) { | ||
// moduleExport is whatever the actual module exported | ||
// However, no listener is set right now, so this works as normal. | ||
require("path"); | ||
var lastRequireInfo; | ||
intercept.setListener(function (moduleExport, info) { | ||
// moduleExport is whatever was found by the built-in require | ||
lastRequireInfo = info; | ||
// info looks like: | ||
// { | ||
// moduleId: "lodash", | ||
// callingFile: "index.js", | ||
// native: false, | ||
// extname: ".js", | ||
// thirdParty: true, | ||
// exports: [[actual lodash object]] | ||
// absPath: /from/root/to/project/node_modules/lodash/lodash.js, | ||
// absPathResolvedCorrectly: true, | ||
// testOnly: false, | ||
// local: false | ||
// } | ||
console.log("require:", info.moduleId, "from", info.callingFile); | ||
// value returned from this function will be passed back to the caller as if it was module.exports | ||
return moduleExport; | ||
}); | ||
}, config); | ||
// config has only one option `shortCircuit: boolean` | ||
// if short-circuit is active, `moduleExport` argument to listener will be null | ||
require("lodash"); | ||
// lastRequireInfo looks like: | ||
// { | ||
// moduleId: "lodash", | ||
// callingFile: "index.js", | ||
// native: false, | ||
// extname: ".js", | ||
// thirdParty: true, | ||
// exports: [[actual lodash object]] | ||
// absPath: /from/root/to/project/node_modules/lodash/lodash.js, | ||
// absPathResolvedCorrectly: true, | ||
// testOnly: false, | ||
// local: false | ||
// } | ||
// require() calls now being intercepted | ||
restore(); | ||
// require() calls no longer intercepted | ||
``` | ||
### Info | ||
`info` objects adhere to this schema | ||
``` | ||
{ | ||
exports: Any, | ||
moduleId: String, | ||
callingFile: String, | ||
extname: String, | ||
absPath: String, | ||
core: Boolean, | ||
thirdParty: Boolean, | ||
local: Boolean, | ||
absPathResolvedCorrectly: Boolean, | ||
testOnly: Boolean | ||
} | ||
``` | ||
### License | ||
MIT |
@@ -1,4 +0,3 @@ | ||
module.exports = { | ||
a: 0 | ||
b: 1 | ||
}; | ||
// no comma between properties | ||
// requiring this file will throw an error | ||
module.exports = {a: 0 b: 1}; |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
2
-50%5
-16.67%14544
-39.18%10
-9.09%310
-12.18%42
-53.33%1
Infinity%- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed