UNPKG

7.81 kBJavaScriptView Raw
1'use strict';
2
3var 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
10var shimsCache = {}
11 , shimsByPath = {};
12
13function inspect(obj, depth) {
14 return util.inspect(obj, false, depth || 5, true);
15}
16
17function isPath(s) {
18 return (/^[.]{0,2}[/\\]/).test(s);
19}
20
21function 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
32function 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
41function 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 // otherwise resolve the path
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
68function 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 // lib exposed under different name/path in package.json's browser field
77 // and it is referred to by this alias in the shims (either external or in package.json)
78 // i.e.: 'non-cjs': { ... } -> browser: { 'non-cjs': './vendor/non-cjs.js }
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 // specified via relative path to shim file inside shim file
83 // i.e. './vendor/non-cjs': { exports: .. }
84 shimPath = path.resolve(shimFileDir, relPath);
85 messages.push(format('Resolved "%s" found in shim file to "%s"', relPath, shimPath));
86 } else {
87 // specified via relative path in package.json browserify-shim config
88 // i.e. 'browserify-shim': { './vendor/non-cjs': 'noncjs' }
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
99function 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 // this also handle unlikely cases of 'global:_.someFunc(':')' with a `:` in the actual global expression
114 parts.shift();
115 acc[k] = parts.join(':');
116
117 return acc;
118 }, {});
119}
120
121function separateExposeGlobals(shims) {
122 var onlyShims = {}
123 , exposeGlobals = {};
124
125 Object.keys(shims).forEach(function (k) {
126 // https://github.com/thlorenz/browserify-shim/issues/245
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
144function 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
155function 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
163var resolve = module.exports = function resolveShims (file, messages, cb) {
164 // find the package.json that defines browserify-shim config for this file
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 // we cached this before which means it was also grouped by file
175 var cached = shimsCache[packageDir];
176 // if it was cached, that means any package fixes were applied as well
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}