$
This commit is contained in:
237
node_modules/istanbul-lib-hook/lib/hook.js
generated
vendored
Normal file
237
node_modules/istanbul-lib-hook/lib/hook.js
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
'use strict';
|
||||
/*
|
||||
Copyright 2012-2015, Yahoo Inc.
|
||||
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
|
||||
*/
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
const appendTransform = require('append-transform');
|
||||
const originalCreateScript = vm.createScript;
|
||||
const originalRunInThisContext = vm.runInThisContext;
|
||||
const originalRunInContext = vm.runInContext;
|
||||
|
||||
function transformFn(matcher, transformer, verbose) {
|
||||
return function(code, options) {
|
||||
options = options || {};
|
||||
|
||||
// prior to 2.x, hookRequire returned filename
|
||||
// rather than object.
|
||||
if (typeof options === 'string') {
|
||||
options = { filename: options };
|
||||
}
|
||||
|
||||
const shouldHook =
|
||||
typeof options.filename === 'string' &&
|
||||
matcher(path.resolve(options.filename));
|
||||
let transformed;
|
||||
let changed = false;
|
||||
|
||||
if (shouldHook) {
|
||||
if (verbose) {
|
||||
console.error(
|
||||
'Module load hook: transform [' + options.filename + ']'
|
||||
);
|
||||
}
|
||||
try {
|
||||
transformed = transformer(code, options);
|
||||
changed = true;
|
||||
} catch (ex) {
|
||||
console.error(
|
||||
'Transformation error for',
|
||||
options.filename,
|
||||
'; return original code'
|
||||
);
|
||||
console.error(ex.message || String(ex));
|
||||
if (verbose) {
|
||||
console.error(ex.stack);
|
||||
}
|
||||
transformed = code;
|
||||
}
|
||||
} else {
|
||||
transformed = code;
|
||||
}
|
||||
return { code: transformed, changed };
|
||||
};
|
||||
}
|
||||
/**
|
||||
* unloads the required caches, removing all files that would have matched
|
||||
* the supplied matcher.
|
||||
* @param {Function} matcher - the match function that accepts a file name and
|
||||
* returns if that file should be unloaded from the cache.
|
||||
*/
|
||||
function unloadRequireCache(matcher) {
|
||||
/* istanbul ignore else: impossible to test */
|
||||
if (matcher && typeof require !== 'undefined' && require && require.cache) {
|
||||
Object.keys(require.cache).forEach(filename => {
|
||||
if (matcher(filename)) {
|
||||
delete require.cache[filename];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* hooks `require` to return transformed code to the node module loader.
|
||||
* Exceptions in the transform result in the original code being used instead.
|
||||
* @method hookRequire
|
||||
* @static
|
||||
* @param matcher {Function(filePath)} a function that is called with the absolute path to the file being
|
||||
* `require`-d. Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
|
||||
* @param transformer {Function(code, filePath)} a function called with the original code and the associated path of the file
|
||||
* from where the code was loaded. Should return the transformed code.
|
||||
* @param options {Object} options Optional.
|
||||
* @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called
|
||||
* @param {Function} [options.postLoadHook] a function that is called with the name of the file being
|
||||
* required. This is called after the require is processed irrespective of whether it was transformed.
|
||||
* @returns {Function} a reset function that can be called to remove the hook
|
||||
*/
|
||||
function hookRequire(matcher, transformer, options) {
|
||||
options = options || {};
|
||||
let disable = false;
|
||||
const fn = transformFn(matcher, transformer, options.verbose);
|
||||
const postLoadHook =
|
||||
options.postLoadHook && typeof options.postLoadHook === 'function'
|
||||
? options.postLoadHook
|
||||
: null;
|
||||
|
||||
const extensions = options.extensions || ['.js'];
|
||||
|
||||
extensions.forEach(ext => {
|
||||
appendTransform((code, filename) => {
|
||||
if (disable) {
|
||||
return code;
|
||||
}
|
||||
const ret = fn(code, filename);
|
||||
if (postLoadHook) {
|
||||
postLoadHook(filename);
|
||||
}
|
||||
return ret.code;
|
||||
}, ext);
|
||||
});
|
||||
|
||||
return function() {
|
||||
disable = true;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* hooks `vm.createScript` to return transformed code out of which a `Script` object will be created.
|
||||
* Exceptions in the transform result in the original code being used instead.
|
||||
* @method hookCreateScript
|
||||
* @static
|
||||
* @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.createScript`
|
||||
* Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
|
||||
* @param transformer {Function(code, filePath)} a function called with the original code and the filename passed to
|
||||
* `vm.createScript`. Should return the transformed code.
|
||||
* @param options {Object} options Optional.
|
||||
* @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called
|
||||
*/
|
||||
function hookCreateScript(matcher, transformer, opts) {
|
||||
opts = opts || {};
|
||||
const fn = transformFn(matcher, transformer, opts.verbose);
|
||||
vm.createScript = function(code, file) {
|
||||
const ret = fn(code, file);
|
||||
return originalCreateScript(ret.code, file);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* unhooks vm.createScript, restoring it to its original state.
|
||||
* @method unhookCreateScript
|
||||
* @static
|
||||
*/
|
||||
function unhookCreateScript() {
|
||||
vm.createScript = originalCreateScript;
|
||||
}
|
||||
/**
|
||||
* hooks `vm.runInThisContext` to return transformed code.
|
||||
* @method hookRunInThisContext
|
||||
* @static
|
||||
* @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.runInThisContext`
|
||||
* Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
|
||||
* @param transformer {Function(code, options)} a function called with the original code and the filename passed to
|
||||
* `vm.runInThisContext`. Should return the transformed code.
|
||||
* @param opts {Object} [opts={}] options
|
||||
* @param {Boolean} [opts.verbose] write a line to standard error every time the transformer is called
|
||||
*/
|
||||
function hookRunInThisContext(matcher, transformer, opts) {
|
||||
opts = opts || {};
|
||||
const fn = transformFn(matcher, transformer, opts.verbose);
|
||||
vm.runInThisContext = function(code, options) {
|
||||
const ret = fn(code, options);
|
||||
return originalRunInThisContext(ret.code, options);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* unhooks vm.runInThisContext, restoring it to its original state.
|
||||
* @method unhookRunInThisContext
|
||||
* @static
|
||||
*/
|
||||
function unhookRunInThisContext() {
|
||||
vm.runInThisContext = originalRunInThisContext;
|
||||
}
|
||||
/**
|
||||
* hooks `vm.runInContext` to return transformed code.
|
||||
* @method hookRunInContext
|
||||
* @static
|
||||
* @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.createScript`
|
||||
* Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
|
||||
* @param transformer {Function(code, filePath)} a function called with the original code and the filename passed to
|
||||
* `vm.createScript`. Should return the transformed code.
|
||||
* @param opts {Object} [opts={}] options
|
||||
* @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called
|
||||
*/
|
||||
function hookRunInContext(matcher, transformer, opts) {
|
||||
opts = opts || {};
|
||||
const fn = transformFn(matcher, transformer, opts.verbose);
|
||||
vm.runInContext = function(code, context, file) {
|
||||
const ret = fn(code, file);
|
||||
const coverageVariable = opts.coverageVariable || '__coverage__';
|
||||
// Refer coverage variable in context to global coverage variable.
|
||||
// So that coverage data will be written in global coverage variable for unit tests run in vm.runInContext.
|
||||
// If all unit tests are run in vm.runInContext, no global coverage variable will be generated.
|
||||
// Thus initialize a global coverage variable here.
|
||||
if (!global[coverageVariable]) {
|
||||
global[coverageVariable] = {};
|
||||
}
|
||||
context[coverageVariable] = global[coverageVariable];
|
||||
return originalRunInContext(ret.code, context, file);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* unhooks vm.runInContext, restoring it to its original state.
|
||||
* @method unhookRunInContext
|
||||
* @static
|
||||
*/
|
||||
function unhookRunInContext() {
|
||||
vm.runInContext = originalRunInContext;
|
||||
}
|
||||
/**
|
||||
* istanbul-lib-hook provides mechanisms to transform code in the scope of `require`,
|
||||
* `vm.createScript`, `vm.runInThisContext` etc.
|
||||
*
|
||||
* This mechanism is general and relies on a user-supplied `matcher` function that
|
||||
* determines when transformations should be performed and a user-supplied `transformer`
|
||||
* function that performs the actual transform. Instrumenting code for coverage is
|
||||
* one specific example of useful hooking.
|
||||
*
|
||||
* Note that both the `matcher` and `transformer` must execute synchronously.
|
||||
*
|
||||
* @module Exports
|
||||
* @example
|
||||
* var hook = require('istanbul-lib-hook'),
|
||||
* myMatcher = function (file) { return file.match(/foo/); },
|
||||
* myTransformer = function (code, file) {
|
||||
* return 'console.log("' + file + '");' + code;
|
||||
* };
|
||||
*
|
||||
* hook.hookRequire(myMatcher, myTransformer);
|
||||
* var foo = require('foo'); //will now print foo's module path to console
|
||||
*/
|
||||
module.exports = {
|
||||
hookRequire,
|
||||
hookCreateScript,
|
||||
unhookCreateScript,
|
||||
hookRunInThisContext,
|
||||
unhookRunInThisContext,
|
||||
hookRunInContext,
|
||||
unhookRunInContext,
|
||||
unloadRequireCache
|
||||
};
|
Reference in New Issue
Block a user