UNPKG

7.51 kBJavaScriptView Raw
1'use strict';
2
3var util = require('util')
4 , resolve = require('resolve')
5 , exposify = require('exposify')
6 , format = require('util').format
7 , path = require('path')
8 , through = require('through')
9 , resolveShims = require('./lib/resolve-shims')
10 , rename = require('rename-function-calls')
11 , debug = require('./lib/debug')
12
13var shimRequire = '__browserify_shim_require__';
14
15function requireDependencies(depends, packageRoot, browserAliases, dependencies) {
16 if (!depends) return '';
17
18 function customResolve (k) {
19 // resolve aliases to full paths to avoid conflicts when require is injected into a file
20 // inside another package, i.e. the it's shim was defined in a package.json one level higher
21 // aliases don't get resolved by browserify in that case, since it only looks in the package.json next to it
22 var browserAlias = browserAliases && browserAliases[k]
23 , dependency = dependencies && dependencies[k]
24 , alias;
25
26 try {
27 // prefer browser aliases defined explicitly
28 alias = browserAlias
29 ? path.resolve(packageRoot, browserAlias)
30
31 // but also consider dependencies installed in the package in which shims were defined
32 : dependency
33 ? resolve.sync(k, { basedir: packageRoot })
34
35 // lets hope for the best that browserify will be able to resolve this, cause we can't
36 : k;
37 } catch (err) {
38 // resolve.sync may fail, in which case we give up and hope browserify can figure it out
39 alias = k;
40 }
41
42 return { alias: alias, exports: depends[k] || null };
43 }
44
45 function noResolve(k) {
46 return { alias: k, exports: depends[k] || null };
47 }
48
49 return Object.keys(depends)
50
51 // if the package was looked up from the parent of its enclosing package we need to pre-resolve the depends
52 .map(customResolve)
53 .reduce(
54 function (acc, dep) {
55 var alias = dep.alias.replace(/\\/g, "\\\\");
56 return dep.exports
57 // Example: jQuery = global.jQuery = require("jquery");
58 // the global dangling variable is needed cause some libs reference it as such and it breaks outside of the browser,
59 // i.e.: (function ($) { ... })( jQuery )
60 // This little extra makes it work everywhere and since it's on top, it will be shadowed by any other definitions
61 // so it doesn't conflict with anything.
62 ? acc + dep.exports + ' = global.' + dep.exports + ' = require("' + alias + '");\n'
63 : acc + 'require("' + alias + '");\n';
64 }
65 , '\n; '
66 );
67}
68
69function bindWindowWithExports(s, dependencies) {
70 // purposely make module, exports, require and define be 'undefined',
71 // but pass a function that allows exporting our dependency from the window or the context
72
73 // This results in code similarly to this example which shims ember which depends on jquery:
74
75 /**
76 * -- browserify wrapper
77 * function(require,module,exports){
78 *
79 * -- our deps (which still have access to require)
80 * jquery = global.jquery = require("/full/path/to/jquery.js");
81 *
82 * -- assigning shimmed require to actual require
83 * -- this shouldn't matter, but would fix cases where libraries reach __browserify_shim_require__(x) as long
84 * -- as x was included in the bundle
85 *
86 * __browserify_shim_require__=require;
87 *
88 * -- also it won't hurt anything
89 *
90 * -- browserify-shim wrapper
91 * (function browserifyShim(module, exports, require, define, browserify_shim__define__module__export__) {
92 * -- inside this function neither module, exports, require, or define are defined
93 *
94 * -- browserify_shim__define__module__export__ allows exporting (since module and exports aren't available)
95 *
96 * [..] -- code that needs shimming
97 *
98 * -- exporting whatever ember attached to the window
99 * ; browserify_shim__define__module__export__(typeof ember != "undefined" ? ember : window.ember);
100 *
101 * }).call(global, undefined, undefined, undefined, undefined, function defineExport(ex) { module.exports = ex; });
102 * -- browserify-shim wrapper closed
103 * }
104 * -- browserify wrapper closed
105 */
106
107 // Shadowing require is necessary to fix code that tries to do common-js, but ends up requiring deps that cannot be resolved
108 // In the case below we want the below condition to be false at run time.
109 /**
110 * if (!jQuery && typeof require === 'function') {
111 * jQuery = require('jquery');
112 * }
113 */
114
115 // Additionally `require('jquery')` needs to be refactored to prevent browserify from looking for 'jquery' at bundle time.
116 // The rewriting step happens inside the main @see shim function.
117 // Thus it gets rewritten via rename-function-calls:
118 /**
119 * if (!jQuery && typeof require === 'function') {
120 * jQuery = __browserify_shim_removed_require__('jquery');
121 * }
122 */
123 // The fact that __browserify_shim_removed_require__ is not defined doesn't matter since we never enter that block.
124
125 return dependencies
126 + '; var ' + shimRequire + '=require;'
127 + '(function browserifyShim(module, exports, require, define, browserify_shim__define__module__export__) {\n'
128 + s
129 + '\n}).call(global, undefined, undefined, undefined, undefined, function defineExport(ex) { module.exports = ex; });\n';
130}
131
132function bindWindowWithoutExports(s, dependencies) {
133 // if a module doesn't need anything to be exported, it is likely, that it exports itself properly
134 // therefore it is not a good idea to override the module here, however we need to still disable require
135 // all else is similar to @see bindWindowWithExports
136 return dependencies
137 + '; var ' + shimRequire + '=require;'
138 + '(function browserifyShim(module, define, require) {\n'
139 + s
140 + '\n}).call(global, module, undefined, undefined);\n';
141}
142
143function moduleExport(exp) {
144 return format('\n; browserify_shim__define__module__export__(typeof %s != "undefined" ? %s : window.%s);\n', exp, exp, exp);
145}
146
147function wrap(content, config, packageRoot, browserAliases) {
148 var exported = config.exports
149 ? content + moduleExport(config.exports)
150 : content
151 , dependencies = requireDependencies(config.depends, packageRoot, browserAliases)
152 , boundWindow = config.exports
153 ? bindWindowWithExports(exported, dependencies)
154 : bindWindowWithoutExports(exported, dependencies);
155
156 return boundWindow;
157}
158
159module.exports = function shim(file) {
160 var content = '';
161 var stream = through(write, end);
162 return stream;
163
164 function write(buf) { content += buf; }
165 function end() {
166 var messages = [];
167 resolveShims(file, messages, function (err, info) {
168 if (err) {
169 stream.emit('error', err);
170 return stream.queue(null);
171 }
172
173 debug('');
174 debug.inspect({ file: file, info: info, messages: messages });
175
176 var eg = info.exposeGlobals;
177 if(eg && Object.keys(eg)) {
178 content = exposify.expose(eg, content);
179 }
180
181 if (info.shim) {
182
183 // at this point we consider all remaining (not exposified) require statements to be invalid (why else are we shimming this)
184 content = rename('require', shimRequire, content);
185
186 var transformed = wrap(content, info.shim, info.packageDir, info.browser)
187 stream.queue(transformed);
188 } else {
189 stream.queue(content);
190 }
191
192 stream.queue(null);
193 });
194 }
195}