166 lines
4.8 KiB
JavaScript
166 lines
4.8 KiB
JavaScript
/*
|
|
Copyright (c) 2014 Google Inc. All rights reserved.
|
|
Copyright (c) 2012-2013 Johannes Ewald.
|
|
|
|
Use of this source code is governed by the MIT License, available in this package's LICENSE file
|
|
or at http://opensource.org/licenses/MIT.
|
|
*/
|
|
const _ = require('lodash');
|
|
const fs = require('fs');
|
|
const Module = require('module');
|
|
|
|
const originalWrapper = Module.wrapper.slice(0);
|
|
const requizzleWrappers = {
|
|
extras: require('./wrappers/extras'),
|
|
requirePaths: require('./wrappers/requirepaths'),
|
|
strict: require('./wrappers/strict')
|
|
};
|
|
|
|
function wrap(wrappers, script) {
|
|
return wrappers[0] + script + wrappers[1];
|
|
}
|
|
|
|
function replaceWrapper(wrapperObj) {
|
|
const joiner = '\n';
|
|
const before = wrapperObj.before.join(joiner);
|
|
const after = wrapperObj.after.join(joiner);
|
|
const wrappers = [
|
|
originalWrapper[0] + before,
|
|
after + originalWrapper[1]
|
|
];
|
|
|
|
Module.wrap = wrap.bind(null, wrappers);
|
|
}
|
|
|
|
function restoreWrapper() {
|
|
Module.wrap = wrap.bind(null, originalWrapper);
|
|
}
|
|
|
|
function createModule(targetPath, parentModule, moduleCache) {
|
|
moduleCache[targetPath] = moduleCache[targetPath] || new Module(targetPath, parentModule);
|
|
|
|
return moduleCache[targetPath];
|
|
}
|
|
|
|
/**
|
|
* Wrapper for `require()` to prevent the target module's dependencies from being swizzled.
|
|
*
|
|
* @param {!Module} targetModule - The module that is being swizzled.
|
|
* @param {!function} nodeRequire - The original `require()` method for the target module.
|
|
* @param {!string} filepath - The value passed to `require()`.
|
|
* @return {!Module} The requested module dependency.
|
|
*/
|
|
function requireProxy(targetModule, nodeRequire, filepath) {
|
|
restoreWrapper();
|
|
targetModule.require = nodeRequire;
|
|
|
|
return nodeRequire.call(targetModule, filepath);
|
|
}
|
|
|
|
/**
|
|
* Wrapper for `require()` to swizzle the target module's dependencies, using the same settings as
|
|
* the target module.
|
|
*
|
|
* @param {!Module} targetModule - The module that is being swizzled.
|
|
* @param {!Object} opts - The Requizzle options object.
|
|
* @param {!string} filepath - The value passed to `require()`.
|
|
* @return {!Module} The requested module dependency.
|
|
*/
|
|
function infectProxy(targetModule, cache, opts, filepath) {
|
|
let moduleExports;
|
|
// loaded here to avoid circular dependencies
|
|
const Requizzle = require('./requizzle');
|
|
let requizzle;
|
|
|
|
opts = _.clone(opts);
|
|
opts.parent = targetModule;
|
|
requizzle = new Requizzle(opts, cache);
|
|
|
|
moduleExports = requizzle.requizzle(filepath);
|
|
|
|
return moduleExports;
|
|
}
|
|
|
|
exports.load = function load(targetPath, parentModule, wrapper, cache, options) {
|
|
let nodeRequire;
|
|
let targetModule;
|
|
|
|
// Handle circular requires, and avoid reloading modules unnecessarily
|
|
if (cache.module[targetPath]) {
|
|
return cache.module[targetPath];
|
|
}
|
|
|
|
targetModule = createModule(targetPath, parentModule, cache.module);
|
|
nodeRequire = targetModule.require;
|
|
|
|
if (options.infect) {
|
|
targetModule.require = filepath => infectProxy(targetModule, cache, options, filepath);
|
|
} else {
|
|
targetModule.require = filepath => requireProxy(targetModule, nodeRequire, filepath);
|
|
}
|
|
|
|
// update the wrapper before we load the target module
|
|
replaceWrapper(wrapper);
|
|
|
|
targetModule.load(targetModule.id);
|
|
|
|
// make sure the wrapper is restored even if the target module doesn't load any dependencies
|
|
restoreWrapper();
|
|
|
|
return targetModule;
|
|
};
|
|
|
|
/**
|
|
* Check whether the entire module includes a `'use strict'` declaration.
|
|
*
|
|
* @param {string} src - The source file to check.
|
|
* @return {boolean} Set to `true` if the module includes a `use strict` declaration.
|
|
*/
|
|
function detectStrictMode(src) {
|
|
return (/^\s*(?:["']use strict["'])[ \t]*(?:[\r\n]|;)/g).test(src);
|
|
}
|
|
|
|
function loadSource(targetPath, sourceCache) {
|
|
if (sourceCache[targetPath] === undefined) {
|
|
sourceCache[targetPath] = fs.readFileSync(targetPath, 'utf8');
|
|
}
|
|
|
|
return sourceCache[targetPath];
|
|
}
|
|
|
|
exports.createWrapper = function createWrapper(targetPath, parentModule, cache, options) {
|
|
let src;
|
|
const wrapperObject = {
|
|
before: [],
|
|
after: []
|
|
};
|
|
|
|
function add(wrapperFunctions, opts) {
|
|
const params = [targetPath, parentModule, opts];
|
|
|
|
['before', 'after'].forEach(item => {
|
|
const result = wrapperFunctions[item].apply(null, params);
|
|
|
|
if (result) {
|
|
wrapperObject[item].push(result);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Preserve the module's `use strict` declaration if present
|
|
src = loadSource(targetPath, cache.source);
|
|
if (detectStrictMode(src) === true) {
|
|
add(requizzleWrappers.strict);
|
|
}
|
|
|
|
if (options.requirePaths) {
|
|
add(requizzleWrappers.requirePaths, options.requirePaths);
|
|
}
|
|
|
|
if (options.extras) {
|
|
add(requizzleWrappers.extras, options.extras);
|
|
}
|
|
|
|
return wrapperObject;
|
|
};
|