1 | ;
|
2 |
|
3 | var 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 |
|
13 | var shimRequire = '__browserify_shim_require__';
|
14 |
|
15 | function 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 |
|
69 | function 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 |
|
132 | function 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 |
|
143 | function moduleExport(exp) {
|
144 | return format('\n; browserify_shim__define__module__export__(typeof %s != "undefined" ? %s : window.%s);\n', exp, exp, exp);
|
145 | }
|
146 |
|
147 | function 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 |
|
159 | module.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 | }
|