1 | 'use strict';
|
2 |
|
3 | var path = require('path')
|
4 | , fs = require('fs')
|
5 | , util = require('util')
|
6 | , parseInlineShims = require('./parse-inline-shims')
|
7 | , mothership = require('mothership')
|
8 | , format = require('util').format
|
9 |
|
10 | var shimsCache = {}
|
11 | , shimsByPath = {};
|
12 |
|
13 | function inspect(obj, depth) {
|
14 | return util.inspect(obj, false, depth || 5, true);
|
15 | }
|
16 |
|
17 | function isPath(s) {
|
18 | return (/^[.]{0,2}[/\\]/).test(s);
|
19 | }
|
20 |
|
21 | function validate(key, config, dir) {
|
22 | var msg
|
23 | , details = 'When evaluating shim "' + key + '": ' + inspect(config) + '\ninside ' + dir + '\n';
|
24 |
|
25 | if (!config.hasOwnProperty('exports')) {
|
26 | msg = 'browserify-shim needs at least a path and exports to do its job, you are missing the exports. ' +
|
27 | '\nIf this module has no exports, specify exports as null.'
|
28 | throw new Error(details + msg);
|
29 | }
|
30 | }
|
31 |
|
32 | function updateCache(packageDir, pack, resolvedShims, exposeGlobals) {
|
33 | shimsCache[packageDir] = { pack: pack, shims: resolvedShims, exposeGlobals: exposeGlobals };
|
34 | Object.keys(resolvedShims).forEach(function(fullPath) {
|
35 | var shim = resolvedShims[fullPath];
|
36 | validate(fullPath, shim, packageDir);
|
37 | shimsByPath[fullPath] = shim;
|
38 | });
|
39 | }
|
40 |
|
41 | function resolveDependsRelativeTo(dir, browser, depends, packDeps, messages) {
|
42 | var resolved;
|
43 |
|
44 | if (!depends) return undefined;
|
45 |
|
46 | return Object.keys(depends).reduce(function (acc, k) {
|
47 | if (browser[k]){
|
48 | acc[k] = depends[k];
|
49 | messages.push(format('Found depends "%s" exposed in browser field', k));
|
50 | } else if (!isPath(k)) {
|
51 | acc[k] = depends[k];
|
52 | if (packDeps[k]) {
|
53 | messages.push(format('Found depends "%s" as an installed dependency of the package', k));
|
54 | } else {
|
55 | messages.push(format('WARNING, depends "%s" is not a path, nor is it exposed in the browser field, nor was it found in package dependencies.', k));
|
56 | }
|
57 | } else {
|
58 |
|
59 | resolved = path.resolve(dir, k);
|
60 | acc[resolved] = depends[k];
|
61 | messages.push(format('Depends "%s" was resolved to be at [%s]', k, resolved));
|
62 | }
|
63 |
|
64 | return acc;
|
65 | }, {})
|
66 | }
|
67 |
|
68 | function resolvePaths (packageDir, shimFileDir, browser, shims, packDeps, messages) {
|
69 | return Object.keys(shims)
|
70 | .reduce(function (acc, relPath) {
|
71 | var shim = shims[relPath];
|
72 | var exposed = browser[relPath];
|
73 | var shimPath;
|
74 |
|
75 | if (exposed) {
|
76 |
|
77 |
|
78 |
|
79 | shimPath = path.resolve(packageDir, exposed);
|
80 | messages.push(format('Found "%s" in browser field referencing "%s" and resolved it to "%s"', relPath, exposed, shimPath));
|
81 | } else if (shimFileDir) {
|
82 |
|
83 |
|
84 | shimPath = path.resolve(shimFileDir, relPath);
|
85 | messages.push(format('Resolved "%s" found in shim file to "%s"', relPath, shimPath));
|
86 | } else {
|
87 |
|
88 |
|
89 | shimPath = path.resolve(packageDir, relPath);
|
90 | messages.push(format('Resolved "%s" found in package.json to "%s"', relPath, shimPath));
|
91 | }
|
92 | var depends = resolveDependsRelativeTo(shimFileDir || packageDir, browser, shim.depends, packDeps, messages);
|
93 |
|
94 | acc[shimPath] = { exports: shim.exports, depends: depends };
|
95 | return acc;
|
96 | }, {});
|
97 | }
|
98 |
|
99 | function mapifyExposeGlobals(exposeGlobals) {
|
100 | return Object.keys(exposeGlobals)
|
101 | .reduce(function (acc, k) {
|
102 |
|
103 | var val = exposeGlobals[k];
|
104 | var parts = val.split(':');
|
105 |
|
106 | if (parts.length < 2 || !parts[1].length) {
|
107 | throw new Error(
|
108 | 'Expose Globals need to have the format "global:expression.\n"'
|
109 | + inspect({ key: k, value: val }) + 'does not.'
|
110 | );
|
111 | }
|
112 |
|
113 |
|
114 | parts.shift();
|
115 | acc[k] = parts.join(':');
|
116 |
|
117 | return acc;
|
118 | }, {});
|
119 | }
|
120 |
|
121 | function separateExposeGlobals(shims) {
|
122 | var onlyShims = {}
|
123 | , exposeGlobals = {};
|
124 |
|
125 | Object.keys(shims).forEach(function (k) {
|
126 |
|
127 | if (k === '__proto__' || k === 'constructor') {
|
128 | return;
|
129 | }
|
130 |
|
131 | var val = shims[k]
|
132 | , exp = val && val.exports;
|
133 |
|
134 | if (exp && /^global\:/.test(exp)) {
|
135 | exposeGlobals[k] = exp;
|
136 | } else {
|
137 | onlyShims[k] = val;
|
138 | }
|
139 | });
|
140 |
|
141 | return { shims: onlyShims, exposeGlobals: mapifyExposeGlobals(exposeGlobals) };
|
142 | }
|
143 |
|
144 | function resolveFromShimFile(packageDir, pack, shimField, messages) {
|
145 | var shimFile = path.join(packageDir, shimField)
|
146 | , shimFileDir = path.dirname(shimFile);
|
147 |
|
148 | var allShims = require(shimFile);
|
149 | var separated = separateExposeGlobals(allShims);
|
150 |
|
151 | var resolvedShims = resolvePaths(packageDir, shimFileDir, pack.browser || {}, separated.shims, pack.dependencies || {}, messages);
|
152 | return { shims: resolvedShims, exposeGlobals: separated.exposeGlobals };
|
153 | }
|
154 |
|
155 | function resolveInlineShims(packageDir, pack, shimField, messages) {
|
156 | var allShims = parseInlineShims(shimField);
|
157 | var separated = separateExposeGlobals(allShims);
|
158 |
|
159 | var resolvedShims = resolvePaths(packageDir, null, pack.browser || {}, separated.shims, pack.dependencies || {}, messages);
|
160 | return { shims: resolvedShims, exposeGlobals: separated.exposeGlobals };
|
161 | }
|
162 |
|
163 | var resolve = module.exports = function resolveShims (file, messages, cb) {
|
164 |
|
165 | mothership(file, function (pack) { return !! pack['browserify-shim'] }, function (err, res) {
|
166 | if (err) return cb(err);
|
167 |
|
168 | if (!res || !res.pack) return cb(new Error('Unable to find a browserify-shim config section in the package.json for ' + file));
|
169 |
|
170 | var pack = res.pack;
|
171 | var packFile = res.path;
|
172 | var packageDir = path.dirname(packFile);
|
173 |
|
174 |
|
175 | var cached = shimsCache[packageDir];
|
176 |
|
177 | if (cached) {
|
178 | return cb(null, {
|
179 | package_json : packFile
|
180 | , packageDir : packageDir
|
181 | , resolvedPreviously : true
|
182 | , shim : shimsByPath[file]
|
183 | , exposeGlobals : cached.exposeGlobals
|
184 | , browser : pack.browser
|
185 | , 'browserify-shim' : pack['browserify-shim']
|
186 | , dependencies : pack.dependencies
|
187 | });
|
188 | }
|
189 |
|
190 | try {
|
191 | pack = require(packFile);
|
192 |
|
193 | var shimField = pack['browserify-shim'];
|
194 | if (!shimField) return cb(null, { package_json: packFile, shim: undefined });
|
195 |
|
196 | var resolved = typeof shimField === 'string'
|
197 | ? resolveFromShimFile(packageDir, pack, shimField, messages)
|
198 | : resolveInlineShims(packageDir, pack, shimField, messages);
|
199 |
|
200 | messages.push({ resolved: resolved.shims });
|
201 | updateCache(packageDir, pack, resolved.shims, resolved.exposeGlobals);
|
202 |
|
203 | cb(null, {
|
204 | package_json : packFile
|
205 | , packageDir : packageDir
|
206 | , shim : shimsByPath[file]
|
207 | , exposeGlobals : resolved.exposeGlobals
|
208 | , browser : pack.browser
|
209 | , 'browserify-shim' : pack['browserify-shim']
|
210 | , dependencies : pack.dependencies
|
211 | });
|
212 |
|
213 | } catch (err) {
|
214 | console.trace();
|
215 | return cb(err);
|
216 | }
|
217 | });
|
218 | }
|