Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81159 views
1
var assert = require("assert");
2
var Watcher = require("../lib/watcher").Watcher;
3
var BuildContext = require("../lib/context").BuildContext;
4
var ModuleReader = require("../lib/reader").ModuleReader;
5
var ReadFileCache = require("../lib/cache").ReadFileCache;
6
var grepP = require("../lib/grep");
7
var util = require("../lib/util");
8
var fs = require("fs");
9
var Q = require("q");
10
11
var path = require("path");
12
var sourceDir = path.resolve(__dirname, "source");
13
var outputDir = path.resolve(__dirname, "output");
14
15
try {
16
fs.mkdirSync(outputDir);
17
} catch (exists) {
18
// pass
19
}
20
21
var watcher = new Watcher(new ReadFileCache(sourceDir), false);
22
23
// Get a new context. Defaults to setRelativize(true) and setUseProvidesModule(true)
24
function getNewContext(options) {
25
var context = new BuildContext({
26
config: { debug: options.debug },
27
sourceDir: sourceDir
28
}, new ReadFileCache(sourceDir));
29
context.setCacheDirectory(path.join(outputDir, options.cacheDirectory));
30
context.setRelativize(options.relative === undefined || options.relative);
31
context.setUseProvidesModule(options.useProvidesModule === undefined || options.useProvidesModule );
32
return context;
33
}
34
35
function getNewDebugContext(options) {
36
options = options || {};
37
options.debug = true;
38
options.cacheDirectory = ".debug-module-cache";
39
return getNewContext(options);
40
}
41
42
function getNewReleaseContext(options) {
43
options = options || {};
44
options.debug = false;
45
options.cacheDirectory = ".release-module-cache";
46
return getNewContext(options);
47
}
48
49
var debugContext = getNewDebugContext();
50
51
var releaseContext = getNewReleaseContext();
52
53
function getProvidedP(id) {
54
var context = this;
55
return context.getProvidedP().then(function(idToPath) {
56
if (idToPath.hasOwnProperty(id))
57
return context.readFileP(idToPath[id]);
58
});
59
}
60
61
function getSourceP(id) {
62
return this.readModuleP(id);
63
}
64
65
function waitForHelpers(done, helperP, contextOptions) {
66
Q.all([
67
helperP(getNewDebugContext(contextOptions)),
68
helperP(getNewReleaseContext(contextOptions))
69
]).done(function() {
70
done();
71
});
72
}
73
74
function checkHome(assert, home) {
75
return Q(home).then(function(home) {
76
assert.strictEqual(home.id, "home");
77
assert.strictEqual(typeof home.source, "string");
78
assert.notEqual(home.source.indexOf("exports"), -1);
79
assert.strictEqual(home.source.indexOf('require("./assert");'), 0);
80
return home;
81
}).invoke("getRequiredP").then(function(reqs) {
82
assert.strictEqual(reqs.length, 1);
83
assert.strictEqual(reqs[0].id, "assert");
84
});
85
}
86
87
describe("ModuleReader", function() {
88
it('should read "home" module correctly', function(done) {
89
var reader = new ModuleReader(debugContext, [getSourceP], []);
90
var homeP = reader.readModuleP("home");
91
checkHome(assert, homeP).done(done);
92
});
93
94
it("should generate stubs for missing modules", function(done) {
95
function helperP(context) {
96
var reader = new ModuleReader(context, [getSourceP], []);
97
var id = "this/module/should/not/exist";
98
return reader.readModuleP(id).then(function(module) {
99
assert.strictEqual(module.id, id);
100
assert.notEqual(module.source.indexOf("throw new Error"), -1);
101
});
102
}
103
104
waitForHelpers(done, helperP);
105
});
106
107
it("should skip failing resolvers", function(done) {
108
var reader = new ModuleReader(debugContext, [function(id) {
109
return this.makePromise(function(callback) {
110
process.nextTick(function() {
111
callback(new Error("bad thing happen"));
112
});
113
});
114
}, function(id) {
115
throw new Error("superbad situation");
116
}, getSourceP], []);
117
118
var homeP = reader.readModuleP("home");
119
checkHome(assert, homeP).done(done);
120
});
121
122
it("should cache previously-read modules", function(done) {
123
var reader = new ModuleReader(debugContext, [getSourceP], []);
124
125
var homes = [
126
reader.readModuleP("home"),
127
reader.readModuleP("home"),
128
reader.readModuleP.originalFn.call(reader, "home")
129
];
130
131
assert.strictEqual(homes[0], homes[1]);
132
assert.notStrictEqual(homes[0], homes[2]);
133
assert.notStrictEqual(homes[1], homes[2]);
134
135
Q.spread(homes, function(h0, h1, h2) {
136
assert.strictEqual(h0, h1);
137
assert.strictEqual(h0, h2);
138
assert.strictEqual(h1, h2);
139
140
assert.strictEqual(h0.hash, h1.hash);
141
assert.strictEqual(h1.hash, h2.hash);
142
}).done(done);
143
});
144
145
it("should allow multiple file output", function(done) {
146
var mfSource = [
147
"function multiFile(foo) {",
148
" console.log(foo);",
149
"}"
150
].join("\n");
151
152
var pkg = require("../package.json");
153
154
function getSourceP(id) {
155
assert.strictEqual(id, "multi-file");
156
return Q(mfSource);
157
}
158
159
function processP1(id, source) {
160
assert.strictEqual(source, mfSource);
161
return {
162
".js": source,
163
".json": util.readJsonFileP(
164
path.join(__dirname, "..", "package.json")
165
)
166
};
167
}
168
169
function processP2(id, input) {
170
assert.strictEqual(input[".js"], mfSource);
171
172
var json = input[".json"];
173
assert.deepEqual(json, pkg);
174
175
input[".json"] = util.makePromise(function(callback) {
176
process.nextTick(function() {
177
callback(null, JSON.stringify(json));
178
});
179
});
180
181
return input;
182
}
183
184
function processP3(id, input) {
185
assert.strictEqual(input[".js"], mfSource);
186
assert.strictEqual(typeof input[".json"], "string");
187
assert.deepEqual(JSON.parse(input[".json"]), pkg);
188
189
return input;
190
}
191
192
var reader = new ModuleReader(
193
debugContext,
194
[getSourceP],
195
[processP1, processP2, processP3]
196
);
197
198
reader.readModuleP("multi-file").then(function(mf) {
199
assert.strictEqual(mf.id, "multi-file");
200
assert.strictEqual(mf.source, mfSource);
201
assert.deepEqual(JSON.parse(mf.output[".json"]), pkg);
202
}).done(done);
203
});
204
});
205
206
describe("grepP", function() {
207
it("should find @providesModule identifiers", function(done) {
208
Q.spread([
209
grepP("@providesModule\\s+\\S+", sourceDir),
210
debugContext.getProvidedP()
211
], function(pathToMatch, valueToPath) {
212
assert.deepEqual(pathToMatch, {
213
"widget/share.js": "@providesModule WidgetShare",
214
"widget/.bogus.js": "@providesModule WidgetShare",
215
"widget/bogus.js~": "@providesModule WidgetShare"
216
});
217
assert.deepEqual(valueToPath, {
218
"WidgetShare": "widget/share.js"
219
});
220
}).done(done);
221
});
222
});
223
224
describe("@providesModule", function() {
225
it("should parse docblocks correctly", function(done) {
226
var code = arguments.callee.toString();
227
228
/**
229
* Look, Ma! A test function that uses itself as input!
230
* @providesModule
231
* @providesModule foo/bar
232
*/
233
234
assert.strictEqual(
235
code.split("@provides" + "Module").length,
236
4);
237
238
assert.strictEqual(
239
debugContext.getProvidedId(code),
240
"foo/bar");
241
242
assert.strictEqual(
243
debugContext.getProvidedId(
244
"no at-providesModule, here"),
245
null);
246
247
/**
248
* Just to make sure we only pay attention to the first one.
249
* @providesModule ignored
250
*/
251
252
function helper(context) {
253
var reader = new ModuleReader(context, [
254
getProvidedP,
255
getSourceP
256
], []);
257
258
return Q.all([
259
Q.spread([
260
reader.readModuleP("widget/share"),
261
reader.readModuleP("WidgetShare")
262
], function(ws1, ws2) {
263
assert.strictEqual(ws1.id, ws2.id);
264
assert.strictEqual(ws1.id, "WidgetShare");
265
assert.strictEqual(ws1, ws2);
266
}),
267
268
reader.readMultiP([
269
"widget/share",
270
"WidgetShare"
271
]).then(function(modules) {
272
assert.strictEqual(modules.length, 1);
273
assert.strictEqual(modules[0].id, "WidgetShare");
274
}),
275
276
reader.readModuleP(
277
"widget/gallery"
278
).then(function(gallery) {
279
return gallery.getRequiredP();
280
}).then(function(deps) {
281
assert.strictEqual(deps.length, 1);
282
assert.strictEqual(deps[0].id, "WidgetShare");
283
}),
284
285
Q.spread([
286
reader.getSourceP("widget/share"),
287
reader.getSourceP("WidgetShare")
288
], function(source1, source2) {
289
assert.strictEqual(source1, source2);
290
})
291
]);
292
}
293
294
waitForHelpers(done, helper);
295
});
296
});
297
298
describe("context.makePromise", function() {
299
it("should make goodly promises", function(done) {
300
var error = new Error("test");
301
302
function helperP(context) {
303
return context.makePromise(function(callback) {
304
process.nextTick(function() {
305
callback(error, "asdf");
306
});
307
}).then(function() {
308
assert.ok(false, "should have thrown an error");
309
}, function(err) {
310
assert.strictEqual(err, error);
311
312
return context.makePromise(function(callback) {
313
process.nextTick(function() {
314
callback(null, "success");
315
});
316
}).then(function(result) {
317
assert.strictEqual(result, "success");
318
});
319
})
320
}
321
322
waitForHelpers(done, helperP);
323
});
324
});
325
326
describe("util.relativize", function() {
327
it("should resolve correct relative module identifiers", function(done) {
328
var moduleId = "some/deeply/nested/module";
329
var processor = require("../lib/relative").getProcessor(null);
330
331
function makeSource(id) {
332
var str = JSON.stringify(id);
333
return "require(" + str + ");\n" +
334
"require(function(){require(" + str + ")}())";
335
}
336
337
function helperP(requiredId, expected) {
338
assert.strictEqual(
339
util.relativize(moduleId, requiredId),
340
expected);
341
342
return processor(
343
moduleId,
344
makeSource(requiredId)
345
).then(function(output) {
346
assert.deepEqual(output, {
347
".js": makeSource(expected)
348
});
349
});
350
}
351
352
Q.all([
353
helperP("another/nested/module",
354
"../../../another/nested/module"),
355
helperP("../../buried/module/./file",
356
"../../buried/module/file"),
357
helperP("../../buried/module/../file",
358
"../../buried/file"),
359
helperP("./same/level",
360
"./same/level"),
361
helperP("./same/./level",
362
"./same/level"),
363
helperP("./same/../level",
364
"./level"),
365
helperP("some/deeply/buried/treasure",
366
"../buried/treasure"),
367
helperP("./file", "./file"),
368
helperP("./file/../../../module",
369
"../../module"),
370
helperP("./file/../../module",
371
"../module")
372
]).done(function() {
373
done();
374
});
375
});
376
});
377
378
describe("canonical module identifiers", function() {
379
it("should be returned by reader.getCanonicalIdP", function(done) {
380
function helperP(context) {
381
var reader = new ModuleReader(context, [
382
getProvidedP,
383
getSourceP
384
], []);
385
386
return Q.spread([
387
reader.getCanonicalIdP("widget/share"),
388
reader.getCanonicalIdP("WidgetShare"),
389
reader.readModuleP("widget/share").get("id"),
390
reader.readModuleP("WidgetShare").get("id")
391
], function(ws1, ws2, ws3, ws4) {
392
assert.strictEqual(ws1, "WidgetShare");
393
assert.strictEqual(ws2, "WidgetShare");
394
assert.strictEqual(ws3, "WidgetShare");
395
assert.strictEqual(ws4, "WidgetShare");
396
});
397
}
398
399
waitForHelpers(done, helperP);
400
});
401
402
it("should replace non-canonical required identifiers", function(done) {
403
function helperP(context) {
404
assert.strictEqual(context.ignoreDependencies, false);
405
406
var reader = new ModuleReader(context, [
407
getProvidedP,
408
getSourceP
409
], []);
410
411
return reader.readModuleP("widget/follow").then(function(follow) {
412
assert.strictEqual(follow.source.indexOf("widget/share"), -1);
413
414
assert.strictEqual(strCount(
415
'require("../WidgetShare")',
416
follow.source
417
), 4);
418
419
assert.strictEqual(strCount(
420
'require("./gallery")',
421
follow.source
422
), 2);
423
424
assert.strictEqual(strCount(
425
'require("../assert")',
426
follow.source
427
), 2);
428
});
429
}
430
431
waitForHelpers(done, helperP);
432
});
433
434
function strCount(substring, string) {
435
return string.split(substring).length - 1;
436
}
437
});
438
439
describe("canonical module identifiers without --use-provides-module", function() {
440
it("should not be returned by reader.getCanonicalIdP nor reader.readModuleP", function(done) {
441
function helperP(context) {
442
console.log(context.useProvidesModule);
443
var reader = new ModuleReader(context, [
444
getProvidedP,
445
getSourceP
446
], []);
447
448
return Q.spread([
449
reader.getCanonicalIdP("widget/share"),
450
reader.getCanonicalIdP("WidgetShare"),
451
reader.readModuleP("widget/share").get("id"),
452
reader.readModuleP("WidgetShare").get("id")
453
], function(ws1, ws2, ws3, ws4) {
454
assert.strictEqual(ws1, "widget/share");
455
assert.strictEqual(ws2, "WidgetShare");
456
assert.strictEqual(ws3, "widget/share");
457
assert.strictEqual(ws4, "WidgetShare");
458
});
459
}
460
461
waitForHelpers(done, helperP, {useProvidesModule: false});
462
});
463
})
464
465
describe("util.flatten", function() {
466
it("should flatten deeply nested arrays", function() {
467
function check(input, expected) {
468
var flat = util.flatten(input);
469
assert.deepEqual(flat, expected);
470
}
471
472
check(1, 1);
473
check([[],,[],1, 2], [1, 2]);
474
check([[[[[[]]]]]], []);
475
check([[1,[[[[2]]],3]]], [1, 2, 3]);
476
check([[1],[[2]],[[[3]]]], [1, 2, 3]);
477
check([,,,], []);
478
});
479
});
480
481
describe("util.testWriteFd", function() {
482
it("should write non-ASCII file data", function(done) {
483
var file = path.join(outputDir, "writeFdP.test.txt");
484
485
function check(content) {
486
function afterUnlink(err) {
487
return util.openExclusiveP(file).then(function(fd) {
488
return util.writeFdP(fd, content).then(function(written) {
489
assert.strictEqual(content, written);
490
});
491
});
492
}
493
494
return function() {
495
return util.unlinkP(file).then(afterUnlink, afterUnlink);
496
};
497
}
498
499
Q("ignored")
500
.then(check("x"))
501
.then(check("x\ny"))
502
.then(check("x\r\ny"))
503
.then(check("\t\nx\t"))
504
.then(check("\ufeff")) // zero-width non-breaking space
505
.then(check("\u2603")) // snowman
506
.then(check("\ud83d\udc19")) // octopus
507
.finally(function() {
508
return util.unlinkP(file);
509
}).done(done);
510
});
511
});
512
513
describe("Watcher", function() {
514
it("should basically work", function(done) {
515
var watcher = new Watcher(new ReadFileCache(sourceDir), false);
516
var dummy = "dummy.js";
517
var dummyFile = path.join(sourceDir, dummy);
518
519
function waitForChangeP() {
520
return util.makePromise(function(callback) {
521
watcher.on("changed", function(path) {
522
callback(null, path);
523
});
524
});
525
}
526
527
util.unlinkP(dummyFile).then(function() {
528
return util.openExclusiveP(dummyFile).then(function(fd) {
529
return util.writeFdP(fd, "dummy");
530
});
531
}).then(function() {
532
return Q.all([
533
watcher.readFileP("home.js"),
534
watcher.readFileP(dummy)
535
]);
536
}).then(function() {
537
return Q.all([
538
util.unlinkP(dummyFile),
539
waitForChangeP()
540
]);
541
}).done(function() {
542
watcher.close();
543
done();
544
});
545
});
546
547
it("should be able to watch directories", function(done) {
548
var watcher = new Watcher(new ReadFileCache(sourceDir), false);
549
var watchMe = "watchMe.js";
550
var fullPath = path.join(sourceDir, watchMe);
551
552
function waitForChangeP() {
553
return util.makePromise(function(callback) {
554
watcher.once("changed", function(path) {
555
callback(null, path);
556
});
557
});
558
}
559
560
function write(content) {
561
return util.openFileP(fullPath).then(function(fd) {
562
var promise = waitForChangeP();
563
util.writeFdP(fd, content);
564
return promise;
565
});
566
}
567
568
util.unlinkP(fullPath).then(function() {
569
return watcher.readFileP(watchMe).then(function(source) {
570
assert.ok(false, "readFileP should have failed");
571
}, function(err) {
572
assert.strictEqual(err.code, "ENOENT");
573
});
574
}).then(function() {
575
return write("first");
576
}).then(function() {
577
return write("second");
578
}).then(function() {
579
var promise = waitForChangeP();
580
util.unlinkP(fullPath);
581
return promise;
582
}).then(function() {
583
return write("third");
584
}).finally(function() {
585
return util.unlinkP(fullPath);
586
}).done(function() {
587
watcher.close();
588
done();
589
});
590
});
591
});
592
593