-
Notifications
You must be signed in to change notification settings - Fork 19
/
html-snapshots.js
149 lines (127 loc) · 4.19 KB
/
html-snapshots.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
/*
* html-snapshots.js
*
* Produce html snapshots using for a website for SEO purposes.
* This is required for javascript SPAs or ajax page output.
* By default, uses a selector to search content to determine if
* a page is "ready" for its html snapshot.
*
* Copyright (c) 2013, 2014, Alex Grant, LocalNerve, contributors
* Licensed under the MIT license.
*/
var spawn = require("child_process").spawn;
var path = require("path");
var rimraf = require("rimraf").sync;
var asyncLib = require("async");
var _ = require("lodash");
var common = require("./common");
var inputFactory = require("./input-generators");
var async = require("./async");
var phantomDir = "./phantom";
var snapshotScript = path.join(phantomDir, "default.js");
/**
* Determine the default phantomJS module path. This is overridden by the
* phatomjs option.
*
* Technique (and concerns) from karma-phantomjs-launcher (MIT).
*/
var defaultPhantomJSExePath = function () {
// If the path we're given by phantomjs is to a .cmd, it is pointing to a global copy.
// Using the cmd as the process to execute causes problems cleaning up the processes
// so we walk from the cmd to the phantomjs.exe and use that instead.
var phantomSource = require('phantomjs').path;
if (path.extname(phantomSource).toLowerCase() === '.cmd') {
return path.join(path.dirname( phantomSource ), '//node_modules//phantomjs//lib//phantom//phantomjs.exe');
}
return phantomSource;
};
/**
* This module's defaults
*/
var defaults = {
input: "robots",
phantomjs: defaultPhantomJSExePath(),
snapshotScript: path.join(__dirname, snapshotScript),
outputDirClean: false,
pollInterval: 500,
processLimit: 4
};
/**
* The worker task that launches phantomjs
*/
function worker(input, options, notifier, qcb) {
var cp,
customModule,
snapshotScript = options.snapshotScript,
phantomjsOptions = Array.isArray(input.phantomjsOptions) ? input.phantomjsOptions : [input.phantomjsOptions];
// map snapshotScript object script to a real path
if (_.isObject(options.snapshotScript)) {
snapshotScript = path.join(__dirname, phantomDir, options.snapshotScript.script) + ".js";
customModule = options.snapshotScript.module;
}
cp = spawn(
options.phantomjs,
phantomjsOptions.concat(
[
snapshotScript,
input.outputFile,
input.url,
input.selector,
input.timeout,
input.checkInterval,
input.useJQuery,
customModule
]), { cwd: process.cwd(), stdio: "inherit", detached: true }
);
cp.on("error", function(e) {
notifier.remove(input.outputFile);
notifier.setError(e);
console.error(e);
qcb(e);
});
cp.on("exit", function(code) {
qcb(code);
});
// start counting
notifier.add(input.outputFile, input.timeout);
}
module.exports = {
/**
* Run all the snapshots using the requested inputGenerator
*/
run: function(options, listener) {
options = options || {};
// ensure our defaults are represented in the options
common.ensure(options, defaults);
// create the inputGenerator, default to robots
var inputGenerator = inputFactory.create(options.input);
// clean the snapshot output directory
if (options.outputDirClean) {
rimraf(options.outputDir);
}
// start async completion notification if a listener was supplied
var result = true, notifier = new async.Notifier();
if (listener) {
result = notifier.start(options.pollInterval, listener, inputGenerator);
}
if (result) {
// create a worker queue with a parallel process limit
var q = asyncLib.queue(function(task, callback) {
task(_.once(callback));
}, options.processLimit);
// have the queue call notifier.empty when last item
// from the queue is given to a worker.
q.empty = notifier.qEmpty;
// expose abort callback to input generators via options
options._abort = function(err) {
notifier.abort(q, err);
};
// generate input for the snapshots
result = inputGenerator.run(options, function(input) {
// give the worker the input and place into the queue
q.push(_.partial(worker, input, options, notifier));
});
}
return result;
}
};