-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathdebug.js
360 lines (324 loc) · 10.2 KB
/
debug.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/** @license MIT License (c) copyright 2014 original authors */
/** @author Brian Cavalier */
/** @author John Hann */
var uid = require('./lib/uid');
var es5Transform = require('./lib/es5Transform');
var metadata = require('./lib/metadata');
module.exports = {
start: startDebug,
assertNoConflicts: detectExtensionConflict,
assertRavePackage: assertRavePackage,
installDebugHooks: installDebugHooks,
logOverrides: logOverrides
};
var debugging = "\
┏( ˆ◡ˆ)┛ ┗(ˆ◡ˆ )┓ Welcome to the RaveJS debug party! ┏( ˆ◡ˆ)┛ ┗(ˆ◡ˆ )┓\n\
\n\
If you see some 404s for JSON files, that's ok! \
They'll go away when you build your app.\n\
If the 404s are spoiling your debug party, the README.md shows how to \
evict them.\n";
var replCommands = "Available commands:\n\
-> rave.dump() - returns rave's context to be viewed or manipulated.\n\
-> rave.version() - shows rave's version.\n\
-> rave.checkVersions() - checks if extensions are compatible.\n\
-> rave.restore() - restores any previous global rave variable and returns rave\
-> rave.help() - shows these commands.\n\
-> what else should we provide? File a github issue!";
var replEnabled = "Rave REPL enabled! (experimental)\n"
+ replCommands;
var multipleRaves = "Warning: multiple versions of rave are installed. \
Update the app's dependencies or try the rave.checkVersions() REPL function.";
var raveResolution = "Warning: rave conflict indicated in bower.json. \
Update the app's dependencies or try the rave.checkVersions() REPL function.";
var semverNotInstalled = "Note: rave.checkVersions() requires the npm semver \
package to verify rave extension semver conflicts. However, the semver \n\
package isn't needed if you understand semver.\nTry updating your npm or \
bower dependencies. If updating doesn't resolve the problem, reload \
and try rave.checkVersions() again after installing the npm semver package:\n\
$ npm install --save semver\n";
var updateDepsInstructions = "To update npm dependencies:\n\
$ npm cache clean && npm update && npm dedupe\n\
To update bower dependencies:\n\
$ bower cache clean && bower update";
var semverMissing = " ? {extName} does not specify a rave version. \
Please ask the author to add rave to peerDependencies (npm) or \
dependencies (bower). {bugsLink}";
var semverValid = " ✓ {extName} depends on rave {raveSemver}.";
var semverInfo = " - {extName} depends on rave {raveSemver}.";
var semverInvalid = " !!! {extName} depends on rave {raveSemver}. \
If this extension is old, please ask the author to update it. {bugsLink}";
var currRaveVersion = "Rave version is {raveVersion}.";
var unknownPackage = "Unknown package when importing {0} from {1}\n\
Did you forget to specify `--save` when installing?";
var wrongModuleType = "Possible moduleType mismatch? Module {name} appears \
to be of type {sourceType}? \nPlease ask the package author to add or update \
moduleType.";
var overriddenPackage = "Package `{overrider}` overrode metadata properties \
of package `{overridee}`.";
var defaultedPackage = "Package `{overrider}` provided default metadata for \
missing properties of package `{overridee}`.";
var uniqueThing = {};
function startDebug (context) {
var prev, rave, message;
console.log(debugging);
prev = 'rave' in global ? global.rave : uniqueThing;
rave = global.rave = {};
message = render({}, replEnabled);
// TODO: load a debug REPL module?
rave.dump = function () {
return context;
};
rave.version = function () { return findVersion(context); };
rave.checkVersions = function () {
runSemverOnExtensions(context);
};
rave.help = function () {
console.log(replCommands);
};
rave.restore = function () {
if (prev === uniqueThing) {
delete global.rave;
}
else {
global.rave = prev;
}
return rave;
};
console.log(message);
}
function assertRavePackage (context) {
if (!('rave' in context.packages)) {
throw new Error('rave package not found. Did you forget to use --save when installing?');
}
return context;
}
function installDebugHooks (context) {
var normalize = context.loader.normalize;
// log an error if rave encounters an unknown package
context.loader.normalize = function (name, refName, refUrl) {
try {
var normalized = normalize(name, refName, refUrl);
}
catch (ex) {
console.error(render(arguments, unknownPackage));
throw ex;
}
return normalized;
};
// log an error if it looks like an incorrect module type was applied
// override instantiate to catch throws of ReferenceError
// errors can happen when instantiate hook runs (AMD) or when returned factory runs (node)
// if /\bdefine\b/ in message, module is AMD, but was not declared as AMD
// if /\brequire\b|\exports\b|\bmodule\b/ in message, module is node, but was not declared as node
var instantiate = context.loader.instantiate;
context.loader.instantiate = function (load) {
try {
return Promise.resolve(instantiate(load)).then(createCheckedFactory, checkError);
}
catch (ex) {
checkError(ex);
throw ex;
}
function createCheckedFactory (result) {
var execute = result.execute;
if (execute) {
result.execute = function () {
try {
return execute.apply(this, arguments);
}
catch (ex) {
checkError(ex);
throw ex;
}
}
}
return result;
}
function checkError (ex) {
var info = {
name: load.name,
declaredType: metadata.findPackage(context.packages, load.name).moduleType
};
if (ex instanceof ReferenceError) {
if (!/\bdefine\b/.test(ex.message)) {
if (/\brequire\b|\exports\b|\bmodule\b/.test(ex.message)) {
info.sourceType = 'node';
}
}
else {
info.sourceType = 'AMD';
}
if (info.sourceType) {
console.error(render(info, wrongModuleType));
}
}
return ex;
}
};
return context;
}
function findVersion (context) {
try {
return context.packages.rave.metadata.version;
}
catch (ex) {
console.error('Rave metadata not found! Did you forget to install rave with the --save option?');
return "(unknown version)";
}
}
function render (values, template) {
return template.replace(/\{([^\}]+)\}/g, function (m, key) {
return values[key];
});
}
function detectExtensionConflict (context) {
// 1. check for more than one rave package. this indicates an npm conflict
// caused by using "dependencies" instead of "peerDependencies" and
// "devDependencies". it could also indicate that the user has installed
// rave via one package manager and extensions via the other.
if (hasMultipleRaves(context)) {
console.warn(multipleRaves);
console.log(updateDepsInstructions);
}
// 2. check for resolutions.rave in bower.json which indicates a bower conflict.
// TODO: how do we detect this if the user hasn't chosen to save the resolution?
if (hasRaveResolution(context)) {
console.warn(raveResolution);
console.log(updateDepsInstructions);
}
return context;
}
function hasMultipleRaves (context) {
var packages, version;
packages = context.packages;
for (var name in packages) {
if (packages[name].name === 'rave') {
if (typeof version === 'undefined') {
version = packages[name].version;
}
else if (version !== packages[name].version) {
return true;
}
}
}
return false;
}
function hasRaveResolution (context) {
var metadata = context.metadata;
if (metadata) {
for (var i = 0; i < metadata.length; i++) {
if (metadata.resolutions && metadata.resolutions.rave) {
return true;
}
}
}
return false;
}
function runSemverOnExtensions (context) {
return require.async('semver').then(runSemver, noSemver);
function runSemver (semver) {
var packages = context.packages;
var seen = {};
var name, pkg, raveSemver, currVer, meta, extName, satisfies, info;
currVer = findVersion(context);
console.log(render({ raveVersion: currVer }, currRaveVersion));
for (name in packages) {
pkg = packages[name];
if (!(pkg.name in seen)) {
seen[pkg.name] = true;
meta = pkg.metadata;
extName = meta.rave && (typeof meta.rave === 'string'
? meta.rave
: meta.rave.extension);
if (extName) {
raveSemver = meta.dependencies && meta.dependencies.rave
|| meta.peerDependencies && meta.peerDependencies.rave;
satisfies = semver && semver.satisfies(currVer, raveSemver);
info = {
extName: meta.name,
raveSemver: raveSemver,
bugsLink: findBugsLink(meta) || ''
};
if (!raveSemver) {
console.log(render(info, semverMissing));
}
else if (!semver) {
console.log(render(info, semverInfo));
}
else if (satisfies) {
console.log(render(info, semverValid));
}
else {
console.log(render(info, semverInvalid));
}
}
}
}
console.log('\n' + updateDepsInstructions);
}
function noSemver () {
console.log(semverNotInstalled);
runSemver();
}
}
function findBugsLink (meta) {
var link = '';
if (meta.bugs) {
link = typeof meta.bugs === 'string'
? meta.bugs
: meta.bugs.url || meta.bugs.email;
}
if (!link && meta.homepage) {
link = meta.homepage;
}
if (!link && meta.maintainers) {
link = findPersonLink(meta.maintainers[0]);
}
if (!link && meta.contributors) {
link = findPersonLink(meta.contributors[0]);
}
if (!link && meta.authors) {
link = findPersonLink(meta.authors[0]);
}
if (!link && meta.author) {
link = findPersonLink(meta.author);
}
return link;
}
function findPersonLink (person) {
if (typeof person === 'string') {
return person;
}
else {
return person.url || person.web || person.homepage || person.email;
}
}
function logOverrides (context) {
var seen, name, pkg, extMeta, oname;
seen = {};
for (name in context.packages) {
pkg = context.packages[name];
// packages are keyed by versioned and unversioned names
if (!(pkg.name in seen) && pkg.metadata && pkg.metadata.rave) {
seen[pkg.name] = true;
extMeta = pkg.metadata.rave;
// TODO: ensure that overridee is found
if (extMeta.missing) {
for (oname in extMeta.missing) {
if (oname in context.packages) {
console.log(render({ overrider: pkg.name, overridee: oname }, defaultedPackage));
}
}
}
if (extMeta.overrides) {
for (oname in extMeta.overrides) {
if (oname in context.packages) {
console.log(render({ overrider: pkg.name, overridee: oname }, overriddenPackage));
}
}
}
}
}
return context;
}