UNPKG

4.95 kBJavaScriptView Raw
1/*
2 Copyright (c) 2014 Google Inc. All rights reserved.
3 Copyright (c) 2012-2013 Johannes Ewald.
4
5 Use of this source code is governed by the MIT License, available in this package's LICENSE file
6 or at http://opensource.org/licenses/MIT.
7 */
8const _ = require('lodash');
9const fs = require('fs');
10const Module = require('module');
11
12const originalWrapper = Module.wrapper.slice(0);
13const requizzleWrappers = {
14 extras: require('./wrappers/extras'),
15 requirePaths: require('./wrappers/requirepaths'),
16 strict: require('./wrappers/strict')
17};
18
19function wrap(wrappers, script) {
20 return wrappers[0] + script + wrappers[1];
21}
22
23function replaceWrapper(wrapperObj) {
24 const joiner = '\n';
25 const before = wrapperObj.before.join(joiner);
26 const after = wrapperObj.after.join(joiner);
27 const wrappers = [
28 originalWrapper[0] + before,
29 after + originalWrapper[1]
30 ];
31
32 Module.wrap = wrap.bind(null, wrappers);
33}
34
35function restoreWrapper() {
36 Module.wrap = wrap.bind(null, originalWrapper);
37}
38
39function createModule(targetPath, parentModule, moduleCache) {
40 moduleCache[targetPath] = moduleCache[targetPath] || new Module(targetPath, parentModule);
41
42 return moduleCache[targetPath];
43}
44
45/**
46 * Wrapper for `require()` to prevent the target module's dependencies from being swizzled.
47 *
48 * @param {!Module} targetModule - The module that is being swizzled.
49 * @param {!function} nodeRequire - The original `require()` method for the target module.
50 * @param {!string} filepath - The value passed to `require()`.
51 * @return {!Module} The requested module dependency.
52 */
53function requireProxy(targetModule, nodeRequire, filepath) {
54 restoreWrapper();
55 targetModule.require = nodeRequire;
56
57 return nodeRequire.call(targetModule, filepath);
58}
59
60/**
61 * Wrapper for `require()` to swizzle the target module's dependencies, using the same settings as
62 * the target module.
63 *
64 * @param {!Module} targetModule - The module that is being swizzled.
65 * @param {!Object} opts - The Requizzle options object.
66 * @param {!string} filepath - The value passed to `require()`.
67 * @return {!Module} The requested module dependency.
68 */
69function infectProxy(targetModule, cache, opts, filepath) {
70 let moduleExports;
71 // loaded here to avoid circular dependencies
72 const Requizzle = require('./requizzle');
73 let requizzle;
74
75 opts = _.clone(opts);
76 opts.parent = targetModule;
77 requizzle = new Requizzle(opts, cache);
78
79 moduleExports = requizzle.requizzle(filepath);
80
81 return moduleExports;
82}
83
84exports.load = function load(targetPath, parentModule, wrapper, cache, options) {
85 let nodeRequire;
86 let targetModule;
87
88 // Handle circular requires, and avoid reloading modules unnecessarily
89 if (cache.module[targetPath]) {
90 return cache.module[targetPath];
91 }
92
93 targetModule = createModule(targetPath, parentModule, cache.module);
94 nodeRequire = targetModule.require;
95
96 if (options.infect) {
97 targetModule.require = filepath => infectProxy(targetModule, cache, options, filepath);
98 } else {
99 targetModule.require = filepath => requireProxy(targetModule, nodeRequire, filepath);
100 }
101
102 // update the wrapper before we load the target module
103 replaceWrapper(wrapper);
104
105 targetModule.load(targetModule.id);
106
107 // make sure the wrapper is restored even if the target module doesn't load any dependencies
108 restoreWrapper();
109
110 return targetModule;
111};
112
113/**
114 * Check whether the entire module includes a `'use strict'` declaration.
115 *
116 * @param {string} src - The source file to check.
117 * @return {boolean} Set to `true` if the module includes a `use strict` declaration.
118 */
119function detectStrictMode(src) {
120 return (/^\s*(?:["']use strict["'])[ \t]*(?:[\r\n]|;)/g).test(src);
121}
122
123function loadSource(targetPath, sourceCache) {
124 if (sourceCache[targetPath] === undefined) {
125 sourceCache[targetPath] = fs.readFileSync(targetPath, 'utf8');
126 }
127
128 return sourceCache[targetPath];
129}
130
131exports.createWrapper = function createWrapper(targetPath, parentModule, cache, options) {
132 let src;
133 const wrapperObject = {
134 before: [],
135 after: []
136 };
137
138 function add(wrapperFunctions, opts) {
139 const params = [targetPath, parentModule, opts];
140
141 ['before', 'after'].forEach(item => {
142 const result = wrapperFunctions[item].apply(null, params);
143
144 if (result) {
145 wrapperObject[item].push(result);
146 }
147 });
148 }
149
150 // Preserve the module's `use strict` declaration if present
151 src = loadSource(targetPath, cache.source);
152 if (detectStrictMode(src) === true) {
153 add(requizzleWrappers.strict);
154 }
155
156 if (options.requirePaths) {
157 add(requizzleWrappers.requirePaths, options.requirePaths);
158 }
159
160 if (options.extras) {
161 add(requizzleWrappers.extras, options.extras);
162 }
163
164 return wrapperObject;
165};