html-snapshots 0.18.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.eslintrc.json +4 -5
  2. package/.github/workflows/verify.yml +2 -2
  3. package/HISTORY.md +42 -0
  4. package/README.md +109 -256
  5. package/examples/README.md +149 -0
  6. package/examples/custom/myFilter.js +2 -2
  7. package/examples/custom/package-lock.json +2021 -5
  8. package/examples/custom/package.json +1 -1
  9. package/examples/custom/snapshot.js +8 -10
  10. package/examples/debug-phantomjs/package-lock.json +6 -6
  11. package/examples/debug-phantomjs/package.json +1 -1
  12. package/examples/debug-phantomjs/snapshot.js +13 -14
  13. package/examples/debug-puppeteer/README.md +18 -0
  14. package/examples/debug-puppeteer/package-lock.json +2804 -0
  15. package/examples/debug-puppeteer/package.json +12 -0
  16. package/examples/debug-puppeteer/snapshot.js +30 -0
  17. package/examples/html5rocks/package-lock.json +1306 -5
  18. package/examples/html5rocks/package.json +1 -1
  19. package/examples/html5rocks/snapshot.js +14 -23
  20. package/examples/process-limit/package-lock.json +1306 -5
  21. package/examples/process-limit/package.json +1 -1
  22. package/examples/process-limit/snapshot.js +8 -10
  23. package/examples/simple-promise/package-lock.json +1306 -5
  24. package/examples/simple-promise/package.json +1 -1
  25. package/examples/simple-promise/snapshot.js +6 -8
  26. package/examples/sitemap-index/package-lock.json +1306 -5
  27. package/examples/sitemap-index/package.json +1 -1
  28. package/examples/sitemap-index/snapshot.js +6 -6
  29. package/examples/utils/index.js +2 -4
  30. package/examples/verbose/package-lock.json +1306 -5
  31. package/examples/verbose/package.json +1 -1
  32. package/examples/verbose/snapshot.js +12 -14
  33. package/lib/async/index.js +0 -1
  34. package/lib/common/index.js +1 -1
  35. package/lib/common/sitemap/index.js +16 -0
  36. package/lib/common/sitemap/sitemap-collection.js +94 -0
  37. package/lib/common/sitemap/sitemap-index.js +68 -0
  38. package/lib/common/{sitemap.js → sitemap/sitemap.js} +68 -57
  39. package/lib/html-snapshots.js +87 -5
  40. package/lib/input-generators/_base.js +7 -1
  41. package/lib/input-generators/array.js +0 -1
  42. package/lib/input-generators/robots.js +98 -58
  43. package/lib/input-generators/sitemap-index.js +3 -124
  44. package/lib/input-generators/sitemap.js +3 -7
  45. package/lib/input-generators/textfile.js +2 -6
  46. package/lib/puppeteer/index.js +98 -0
  47. package/lib/puppeteer/removeScripts.js +8 -0
  48. package/package.json +9 -4
  49. package/test/helpers/options.js +4 -2
  50. package/test/mocha/browsers/puppeteer.js +106 -0
  51. package/test/mocha/browsers/server/index.html +9 -0
  52. package/test/mocha/browsers/test.js +11 -0
  53. package/test/mocha/html-snapshots/basics.js +60 -47
  54. package/test/mocha/html-snapshots/phantomjs-options.js +54 -60
  55. package/test/mocha/html-snapshots/process-limit.js +126 -120
  56. package/test/mocha/html-snapshots/puppeteer.js +47 -0
  57. package/test/mocha/html-snapshots/robots.js +156 -121
  58. package/test/mocha/html-snapshots/server/public/page-sitemap.xml +16 -0
  59. package/test/mocha/html-snapshots/server/test_robots.txt +13 -0
  60. package/test/mocha/html-snapshots/server/test_robots_sitemap.txt +15 -0
  61. package/test/mocha/html-snapshots/server/test_robots_sitemap.xml +22 -0
  62. package/test/mocha/html-snapshots/sitemap-index.js +100 -90
  63. package/test/mocha/html-snapshots/sitemap.js +75 -31
  64. package/test/mocha/html-snapshots/snapshot-scripts.js +150 -145
  65. package/test/mocha/html-snapshots/test.js +23 -14
  66. package/test/mocha/html-snapshots/test_robots.txt +0 -2
  67. package/test/mocha/html-snapshots/use-jquery.js +65 -64
  68. package/test/mocha/html-snapshots/utils.js +29 -30
  69. package/test/mocha/input-generators/server/test_robots.txt +0 -2
  70. package/test/mocha/input-generators/server/test_robots_bad.txt +0 -2
  71. package/test/mocha/input-generators/server/test_robots_sitemap.txt +17 -0
  72. package/test/mocha/input-generators/server/test_robots_sitemap_bad.txt +17 -0
  73. package/test/mocha/input-generators/server/test_robots_sitemap_multi.txt +18 -0
  74. package/test/mocha/input-generators/server/test_sitemap_index_empty.xml +3 -0
  75. package/test/mocha/input-generators/server/test_sitemap_index_malformed.xml +2 -0
  76. package/test/mocha/input-generators/sitemap-index.js +65 -9
  77. package/test/mocha/input-generators/test.js +276 -280
  78. package/test/mocha/input-generators/test_robots.txt +0 -2
  79. package/test/mocha/input-generators/test_robots_bad.txt +0 -2
  80. package/test/mocha/input-generators/test_robots_sitemap.txt +17 -0
  81. package/test/mocha/input-generators/test_robots_sitemap_bad.txt +17 -0
  82. package/test/mocha/input-generators/test_robots_sitemap_multi.txt +18 -0
  83. package/lib/common/node.js +0 -26
  84. package/test/mocha/common/node.js +0 -100
@@ -7,6 +7,6 @@
7
7
  "start": "node ./snapshot.js"
8
8
  },
9
9
  "dependencies": {
10
- "html-snapshots": "^0.18.2"
10
+ "html-snapshots": "^1.0.0"
11
11
  }
12
12
  }
@@ -5,32 +5,30 @@
5
5
  * Use sitemap.xml to snapshot an entire site.
6
6
  * Use the verbose option to debug the loading of one of the pages.
7
7
  */
8
- var htmlSnapshots = require("html-snapshots");
8
+ const htmlSnapshots = require("html-snapshots");
9
9
 
10
- var origin = "http://enigmatic-refuge-9006.herokuapp.com:80";
11
-
12
- var pageToDebug = origin + "/sample-page";
10
+ const origin = "https://www.sitemaps.org";
11
+ const pageToDebug = `${origin}/en_GB/`;
13
12
 
14
13
  // Make the verbose option as an object keyed with the page to debug.
15
- var verboseOption = {
16
- __default: false
14
+ const verboseOption = {
15
+ __default: false,
16
+ [pageToDebug]: true
17
17
  };
18
- verboseOption[pageToDebug] = true;
19
18
 
20
19
  htmlSnapshots.run({
21
20
  input: "sitemap",
22
- source: origin + "/sitemap.xml",
21
+ source: `${origin}/sitemap.xml`,
23
22
  outputDir: require("path").join(__dirname, "./tmp"),
24
23
  outputDirClean: true,
25
- selector: ".page-content",
26
- timeout: 15000,
24
+ selector: "#mainContent",
25
+ timeout: 10000,
26
+ browser: "phantomjs",
27
27
  processLimit: 1, // to clarify output
28
28
  verbose: verboseOption
29
29
  })
30
- .then(function (completed) {
30
+ .then(completed => {
31
31
  console.log("completed snapshots:");
32
32
  console.log(require("util").inspect(completed));
33
33
  })
34
- .catch(function (err) {
35
- console.error(err);
36
- });
34
+ .catch(console.error);
@@ -7,7 +7,6 @@
7
7
  * Copyright (c) 2013 - 2022, Alex Grant, LocalNerve, contributors
8
8
  * Licensed under the MIT license.
9
9
  */
10
- /* global Promise */
11
10
 
12
11
  const crypto = require("crypto");
13
12
  const AsyncLock = require("async-lock");
@@ -48,7 +48,7 @@ function isUrl (obj) {
48
48
  * If undefined, then pass through undefined.
49
49
  * @param {String} [message] - The message to prepend.
50
50
  * @param {Boolean} [quoteInput] - True if the message should be quoted.
51
- * @returns {Error} The updated Error instance, undefined if no error.
51
+ * @returns {Error|Undefined} The updated Error instance, *undefined if no error*.
52
52
  */
53
53
  function prependMsgToErr (error, message, quoteInput) {
54
54
  let result, prepend;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Reused sitemap modules.
3
+ * Used by robots, sitemap, and sitemap-index input generators.
4
+ *
5
+ * Copyright (c) 2013 - 2022 Alex Grant, LocalNerve, contributors
6
+ * Licensed under the MIT license.
7
+ */
8
+ const sitemap = require("./sitemap");
9
+ const smc = require("./sitemap-collection");
10
+ const smi = require("./sitemap-index");
11
+
12
+ module.exports = {
13
+ sitemapCollection: { process: smc.processSitemaps.bind(null, sitemap) },
14
+ sitemapIndex: { process: smi.sitemapIndex.bind(null, sitemap) },
15
+ sitemap
16
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * sitemap-index.js (common functions)
3
+ *
4
+ * Copyright (c) 2013 - 2022 Alex Grant, LocalNerve, contributors
5
+ * Licensed under the MIT license.
6
+ */
7
+ const fs = require("fs");
8
+ const pathLib = require("path");
9
+ const mkdirp = require("mkdirp");
10
+ const common = require("../index");
11
+ const base = require("../../input-generators/_base");
12
+
13
+ /**
14
+ * Make sure path exists so a sitemap write can succeed.
15
+ *
16
+ * @param {String} outputPath - File path and name.
17
+ * @param {Function} callback - Completion callback.
18
+ */
19
+ function prepareWrite (outputPath, callback) {
20
+ const path = pathLib.parse(outputPath);
21
+ const dir = pathLib.join(path.root, path.dir);
22
+ mkdirp(dir).then(() => callback()).catch(callback);
23
+ }
24
+
25
+ /**
26
+ * Parse a sitemap, generate input, and optionally update the local file
27
+ * for sitemapPolicy purposes.
28
+ * This writes the sitemap files out EVEN IF sitemapPolicy is false.
29
+ * The thinking is that it's better/cheaper to do this b/c if sitemapPolicy is
30
+ * enabled later, then there is something to compare against, for more savings.
31
+ *
32
+ * @param {Object} options - Sitemap options.
33
+ * @param {Object} options.__sitemapIndex - Sitemap Index options.
34
+ * @param {String} document - xml document string.
35
+ * @param {Function} callback - A completion callback.
36
+ */
37
+ function processSitemap (smLib, options, document, callback) {
38
+ smLib.parse(options, document, err => {
39
+ const sitemapIndexOpts = options.__sitemapIndex;
40
+ const outputPath = base.outputFile(sitemapIndexOpts, options.source);
41
+
42
+ if (!err && sitemapIndexOpts.sitemapOutputDir) {
43
+ prepareWrite(outputPath, err => {
44
+ if (!err) {
45
+ fs.writeFile(outputPath, document, callback);
46
+ } else {
47
+ callback(common.prependMsgToErr(err, outputPath, true));
48
+ }
49
+ });
50
+ } else {
51
+ callback(err);
52
+ }
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Get and process a sitemaps collection.
58
+ *
59
+ * @param {Object} smLib - Sitemap common module.
60
+ * @param {Object} options - Sitemap options.
61
+ * @param {Array} sitemapUrls - collection of sitemap urls to process.
62
+ * @param {Object} [sitemapIndexOptions] - Sitemap index options.
63
+ * @returns {Promise} resolves when all sitemaps are processed, reject as soon as one fails
64
+ */
65
+ function processSitemaps (smLib, options, sitemapUrls, sitemapIndexOptions) {
66
+ // Edge case message for clarity
67
+ if (sitemapUrls.length === 0) {
68
+ console.log("[*] No sitemaps qualified for processing");
69
+ return Promise.resolve([]);
70
+ }
71
+
72
+ const smiOptions = sitemapIndexOptions || {
73
+ ...options,
74
+ ...{ outputPath: undefined }
75
+ };
76
+
77
+ // Get all the sitemaps, parse them, and generate input for each
78
+ return Promise.all(sitemapUrls.map(sitemapUrl => {
79
+ const sitemapOptions = Object.assign({}, options, {
80
+ source: sitemapUrl,
81
+ input: "sitemap",
82
+ sitemapOutputDir: false,
83
+ __sitemapIndex: smiOptions
84
+ });
85
+
86
+ console.log(`[+] Loading the sitemap: '${sitemapUrl}'`);
87
+
88
+ return smLib.getUrl(sitemapOptions, processSitemap.bind(null, smLib));
89
+ }));
90
+ }
91
+
92
+ module.exports = {
93
+ processSitemaps
94
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * sitemap-index.js
3
+ *
4
+ * Generates the urls for a sitemap-index.
5
+ *
6
+ * Copyright (c) 2013 - 2022 Alex Grant, LocalNerve, contributors
7
+ * Licensed under the MIT license.
8
+ */
9
+ const _ = require("lodash");
10
+ const smc = require("./sitemap-collection");
11
+
12
+ /**
13
+ * Create a collection of sitemap urls to process by policy, form sitemapIndexOptions.
14
+ *
15
+ * @param {Object} smLib - Sitemap module
16
+ * @param {Object} options - sitemap index options
17
+ * @param {Boolean} options.sitemapPolicy - True if sitemap policy should be used.
18
+ * @param {Object} parseResult - xml2js sax parse result object
19
+ * @returns {Object} The sitemapUrls collection and sitemapIndexOptions
20
+ */
21
+ function sitemapIndexUrls (smLib, options, parseResult) {
22
+ const sitemapUrls = [];
23
+ const sitemapIndexOptions = {
24
+ ...options,
25
+ ...{ outputPath: undefined }
26
+ };
27
+
28
+ // Check if we should process each sitemap in the index.
29
+ _.forEach(
30
+ // if the sitemap index is malformed, just blow up
31
+ parseResult.sitemapindex.sitemap,
32
+ sitemapNode => {
33
+ // optionally ignore current sitemaps by sitemap policy
34
+ const shouldProcess = !options.sitemapPolicy ||
35
+ !smLib.stillCurrent(sitemapNode, sitemapIndexOptions);
36
+
37
+ if (shouldProcess) {
38
+ // if sitemap index is malformed, just blow up
39
+ sitemapUrls.push(sitemapNode.loc[0]);
40
+ }
41
+ }
42
+ );
43
+
44
+ return {
45
+ sitemapUrls,
46
+ sitemapIndexOptions
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Collect sitemap urls from a sitemap index file for processing.
52
+ *
53
+ * @param {Object} smLib - sitemap module
54
+ * @param {Object} options - sitemapIndex options
55
+ * @param {Object} parseResult - xml2js sax parse result object
56
+ * @returns {Promise} resolves when all sitemaps are processed, reject as soon as one fails
57
+ */
58
+ function sitemapIndex (smLib, options, parseResult) {
59
+ const {
60
+ sitemapUrls,
61
+ sitemapIndexOptions
62
+ } = sitemapIndexUrls(smLib, options, parseResult);
63
+ return smc.processSitemaps(smLib, options, sitemapUrls, sitemapIndexOptions);
64
+ }
65
+
66
+ module.exports = {
67
+ sitemapIndex
68
+ };
@@ -9,14 +9,15 @@
9
9
  */
10
10
 
11
11
  const fs = require("fs");
12
+ const util = require("util");
12
13
  const path = require("path");
13
14
  const urlm = require("url");
14
15
  const zlib = require("zlib");
15
16
  const _ = require("lodash");
16
17
  const xml2js = require("xml2js");
17
- const request = require("request");
18
- const common = require("./index");
19
- const base = require("../input-generators/_base");
18
+ const smi = require("./sitemap-index");
19
+ const common = require("../index");
20
+ const base = require("../../input-generators/_base");
20
21
 
21
22
  // the out of date fallback
22
23
  const unixStart = "1970-01-01T00:00:00.000Z";
@@ -44,6 +45,7 @@ const changeFreq = {
44
45
  */
45
46
  function stillCurrent (urlNode, options) {
46
47
  let lesser, greater, oPath;
48
+ const statOpts = {throwIfNoEntry: false};
47
49
  const now = Date.now();
48
50
  const lMod = _.first(urlNode.lastmod);
49
51
  const cFreq = _.first(urlNode.changefreq) ? _.first(urlNode.changefreq).toLowerCase() : null;
@@ -53,7 +55,7 @@ function stillCurrent (urlNode, options) {
53
55
  // if sitemap is malformed, just blow up
54
56
  oPath = base.outputFile(options, urlNode.loc[0]);
55
57
 
56
- lesser = now - ( (fs.existsSync(oPath) && fs.statSync(oPath).mtime.getTime()) || unixStart );
58
+ lesser = now - (fs.statSync(oPath, statOpts)?.mtime?.getTime() ?? unixStart);
57
59
  greater = now - Date.parse(lMod);
58
60
  }
59
61
  // only changefreq specified
@@ -61,7 +63,7 @@ function stillCurrent (urlNode, options) {
61
63
  // if sitemap is malformed, just blow up
62
64
  oPath = base.outputFile(options, urlNode.loc[0]);
63
65
 
64
- lesser = now - ( (fs.existsSync(oPath) && fs.statSync(oPath).mtime.getTime()) || unixStart );
66
+ lesser = now - (fs.statSync(oPath, statOpts)?.mtime?.getTime() ?? unixStart);
65
67
  greater = changeFreq[cFreq] || changeFreq.always;
66
68
  }
67
69
  // both or neither were specified
@@ -91,43 +93,49 @@ function parse (options, document, callback) {
91
93
  normalize: true
92
94
  }, (err, result) => {
93
95
  let source = options.source;
94
-
95
96
  if (!err) {
96
-
97
- // Process the url input, but break if base.input returns false.
98
- // In other words, _.find is looking for a non-falsy err.
99
- // For now, this can only happen if no outputDir is defined,
100
- // which is a fatal bad option problem and will happen immediately.
101
- _.find(
102
- // if the sitemap is malformed, just blow up
103
- result.urlset.url,
104
- urlNode => {
105
- // optionally ignore current urls by sitemap policy
106
- let url;
107
- const process = !options.sitemapPolicy ||
108
- !stillCurrent(urlNode, options);
109
-
110
- if (process) {
111
- // if sitemap is malformed, just blow up
112
- url = urlm.parse(urlNode.loc[0]);
113
- if (!base.input(_.extend({}, options, {
114
- protocol: url.protocol,
115
- auth: url.auth,
116
- hostname: url.hostname,
117
- port: url.port
118
- }),
119
- urlNode.loc[0])
120
- ) {
121
- source = urlNode.loc[0];
122
- err = base.generatorError();
97
+ if (result.sitemapindex) {
98
+ // actually a sitemap index
99
+ return smi.sitemapIndex(module.exports, options, result)
100
+ .then(() => callback())
101
+ .catch(callback);
102
+ } else {
103
+ // Process the url input, but break if base.input returns false.
104
+ // In other words, _.find is looking for a non-falsy err.
105
+ // For now, this can only happen if no outputDir is defined,
106
+ // which is a fatal bad option problem and will happen immediately.
107
+ _.find(
108
+ // if the sitemap is malformed, just blow up
109
+ result.urlset.url,
110
+ urlNode => {
111
+ // optionally ignore current urls by sitemap policy
112
+ let url;
113
+ const process = !options.sitemapPolicy ||
114
+ !stillCurrent(urlNode, options);
115
+
116
+ if (process) {
117
+ // if sitemap is malformed, just blow up
118
+ url = urlm.parse(urlNode.loc[0]);
119
+ if (!base.input(_.extend({}, options, {
120
+ protocol: url.protocol,
121
+ auth: url.auth,
122
+ hostname: url.hostname,
123
+ port: url.port
124
+ }),
125
+ urlNode.loc[0])
126
+ ) {
127
+ source = urlNode.loc[0];
128
+ err = base.generatorError();
129
+ }
123
130
  }
124
- }
125
131
 
126
- return err;
127
- }
128
- );
132
+ return err;
133
+ }
134
+ );
135
+ }
129
136
  }
130
137
 
138
+ // NOTE: if !err, then prependMsgToErr returns `undefined`
131
139
  callback(common.prependMsgToErr(err, source, true));
132
140
  });
133
141
  }
@@ -165,21 +173,25 @@ function convert (options, buffer, next, callback) {
165
173
  * @param {String} options.source - Sitemap URL.
166
174
  * @param {Function} options.timeout - User supplied run timeout.
167
175
  * @param {Function} parseFn - A data parser function.
168
- * @param {Function} callback - A completion callback function.
176
+ * @return {Promise} resolves to data on completion.
169
177
  */
170
- function getUrl (options, parseFn, callback) {
171
- request({
178
+ async function getUrl (options, parseFn) {
179
+ const { default:got } = await import("got");
180
+ return got({
172
181
  url: options.source,
173
- encoding: null,
174
- timeout: options.timeout() // get the default timeout
175
- }, (err, res, body) => {
176
- var error = err || common.checkResponse(res, ["text/xml", "application/xml"]);
177
-
182
+ responseType: "buffer",
183
+ timeout: {
184
+ request: options.timeout() // get the default timeout
185
+ }
186
+ }).then(res => {
187
+ let error = common.checkResponse(res, ["text/xml", "application/xml"]);
178
188
  if (error) {
179
- callback(common.prependMsgToErr(error, options.source, true));
180
- } else {
181
- convert(options, body, parseFn, callback);
189
+ throw new Error(error);
182
190
  }
191
+ const conv = util.promisify(convert);
192
+ return conv(options, res.body, parseFn);
193
+ }).catch(err => {
194
+ throw new Error(common.prependMsgToErr(err, options.source, true));
183
195
  });
184
196
  }
185
197
 
@@ -189,16 +201,15 @@ function getUrl (options, parseFn, callback) {
189
201
  * @param {Object} options - Sitemap options.
190
202
  * @param {String} options.source - Sitemap file path.
191
203
  * @param {Function} parseFn - A data parser function.
192
- * @param {Function} callback - A completion callback.
204
+ * @return {Promise} resolves to data on completion.
193
205
  */
194
- function getFile (options, parseFn, callback) {
195
- fs.readFile(options.source, (err, data) => {
196
- if (err) {
197
- callback(common.prependMsgToErr(err, options.source, true));
198
- } else {
199
- convert(options, data, parseFn, callback);
200
- }
201
- });
206
+ function getFile (options, parseFn) {
207
+ const readFile = util.promisify(fs.readFile);
208
+ const conv = util.promisify(convert);
209
+ return readFile(options.source).then(data => conv(options, data, parseFn))
210
+ .catch(err => {
211
+ throw new Error(common.prependMsgToErr(err, options.source, true));
212
+ });
202
213
  }
203
214
 
204
215
  module.exports = {
@@ -9,7 +9,6 @@
9
9
  * Copyright (c) 2013 - 2022 Alex Grant, LocalNerve, contributors
10
10
  * Licensed under the MIT license.
11
11
  */
12
- /* global Promise */
13
12
 
14
13
  const spawn = require("child_process").spawn;
15
14
  const path = require("path");
@@ -21,8 +20,6 @@ const _ = require("lodash");
21
20
  const common = require("./common");
22
21
  const inputFactory = require("./input-generators");
23
22
  const Notifier = require("./async").Notifier;
24
- const phantomDir = "./phantom";
25
- const snapshotScript = path.join(phantomDir, "default.js");
26
23
 
27
24
  /**
28
25
  * Determine the default phantomJS module path. This is overridden by the
@@ -57,13 +54,21 @@ function defaultPhantomJSExePath () {
57
54
  return phantomSource;
58
55
  }
59
56
 
57
+ const phantomDir = "./phantom";
58
+ const phantomSnapshotScript = path.join(phantomDir, "default.js");
59
+ const puppeteerDir = "./puppeteer";
60
+ const puppeteerSnapshotScript = path.join(puppeteerDir, "index.js");
61
+
60
62
  /**
61
63
  * This module's defaults
62
64
  */
63
65
  const defaults = Object.freeze({
64
66
  input: "robots",
67
+ browser: "puppeteer",
65
68
  phantomjs: defaultPhantomJSExePath(),
66
- snapshotScript: path.join(__dirname, snapshotScript),
69
+ phantomSnapshotScript: path.join(__dirname, phantomSnapshotScript),
70
+ puppeteer: path.join(__dirname, puppeteerSnapshotScript),
71
+ snapshotScript: path.join(__dirname, puppeteerSnapshotScript),
67
72
  outputDirClean: false,
68
73
  pollInterval: 500,
69
74
  processLimit: 4
@@ -93,7 +98,7 @@ const defaults = Object.freeze({
93
98
  * @param {Function} notifier.known - Check if a file is or has been processed.
94
99
  * @param {Function} qcb - async.queue callback function.
95
100
  */
96
- function worker (input, options, notifier, qcb) {
101
+ function phantomjsWorker (input, options, notifier, qcb) {
97
102
  let cp, customModule;
98
103
  let snapshotScript = options.snapshotScript;
99
104
  const phantomjsOptions =
@@ -143,6 +148,73 @@ function worker (input, options, notifier, qcb) {
143
148
  }
144
149
  }
145
150
 
151
+ /**
152
+ * @param {Object} input - An input object from an input generator.
153
+ * @param {String} input.outputFile - The output file that the html is written to.
154
+ * @param {String} input.url - The url to get the html from.
155
+ * @param {String} input.selector - The selector to wait for.
156
+ * @param {String} input.timeout - The entire process timeout.
157
+ * @param {String} input.debug - The debug object.
158
+ * @param {Object} options - Input options.
159
+ * @param {Object} options.snapshotScript - Snapshot script options.
160
+ * @param {String} options.snapshotScript.script - Snapshot script name.
161
+ * @param {String} options.snapshotScript.module - Snapshot script module.
162
+ * @param {String} options.puppeteer - Full path to the puppeteer exec.
163
+ * @param {Object} notifier - The async Notifier instance for this run.
164
+ * @param {Function} notifier.remove - Remove an outputFile from notifier.
165
+ * @param {Function} notifier.setError - Set an error on the notifier.
166
+ * @param {Function} notifier.add - Add a file, timeout to the notifier.
167
+ * @param {Function} notifier.known - Check if a file is or has been processed.
168
+ * @param {Function} qcb - async.queue callback function.
169
+ */
170
+ function puppeteerWorker (input, options, notifier, qcb) {
171
+ let filter = "false";
172
+ let snapshotScript = options.snapshotScript;
173
+
174
+ // If the outputFile has NOT already been seen by the notifier, process.
175
+ if (!notifier.known(input.outputFile)) {
176
+
177
+ // map snapshotScript object script
178
+ if (_.isObject(options.snapshotScript)) {
179
+ snapshotScript = options.puppeteer;
180
+ if (options.snapshotScript.script === "removeScripts") {
181
+ filter = "./removeScripts";
182
+ } else {
183
+ filter = options.snapshotScript.module;
184
+ }
185
+ }
186
+
187
+ const cp = spawn(
188
+ snapshotScript,
189
+ [ input.outputFile,
190
+ input.url,
191
+ input.selector,
192
+ input.timeout,
193
+ filter,
194
+ input.debug.flag,
195
+ input.debug.slowMo
196
+ ], { cwd: process.cwd(), stdio: "inherit", detached: true }
197
+ );
198
+
199
+ cp.on("error", function (e) {
200
+ notifier.remove(input.outputFile);
201
+ notifier.setError(e);
202
+ console.error(e);
203
+ qcb(e);
204
+ });
205
+
206
+ cp.on("exit", function (code) {
207
+ qcb(code);
208
+ });
209
+
210
+ // start counting
211
+ notifier.add(input.outputFile, input.timeout);
212
+ } else {
213
+ // The input.outputFile is being or has been processed this run.
214
+ qcb(0);
215
+ }
216
+ }
217
+
146
218
  /**
147
219
  * Prepare html snapshots options.
148
220
  *
@@ -157,6 +229,13 @@ function prepOptions (options) {
157
229
  if (Array.isArray(options.source)) {
158
230
  options.input = "array";
159
231
  }
232
+
233
+ // If phantomjs browser, make sure the default snapshotScript is correctly set.
234
+ if (options.browser === 'phantomjs') {
235
+ if (!options.snapshotScript || options.snapshotScript === options.puppeteer) {
236
+ options.snapshotScript = options.phantomSnapshotScript;
237
+ }
238
+ }
160
239
  }
161
240
 
162
241
  module.exports = {
@@ -227,6 +306,9 @@ module.exports = {
227
306
  // expose abort callback to input generators via options.
228
307
  options._abort = notifier.abort.bind(notifier, q);
229
308
 
309
+ const worker =
310
+ options.browser === 'puppeteer' ? puppeteerWorker : phantomjsWorker;
311
+
230
312
  // generate input for the snapshots.
231
313
  return inputGenerator.run(options, input => {
232
314
  q.push(_.partial(worker, input, options, notifier));
@@ -26,7 +26,11 @@ const defaults = Object.freeze({
26
26
  checkInterval: 250,
27
27
  useJQuery: false,
28
28
  verbose: false,
29
- phantomjsOptions: ""
29
+ phantomjsOptions: "",
30
+ debug: {
31
+ flag: "false",
32
+ slowMo: 500
33
+ }
30
34
  });
31
35
 
32
36
  /**
@@ -224,6 +228,7 @@ module.exports = {
224
228
  * @param {Function} options.userJQuery - Produces a jquery bool given a page url.
225
229
  * @param {Function} options.verbose - Produces a verbose bool given a page url.
226
230
  * @param {Function} options.phantomjsOptions - Produces phantomjs options given a page url.
231
+ * @param {Function} options.debug - Produces debug options given a page url.
227
232
  * @param {Function} options._inputEmitter - The EventEmitter to generate input.
228
233
  * @param {String} page - The url to the page resource.
229
234
  * @returns {String} The full output file path.
@@ -258,6 +263,7 @@ module.exports = {
258
263
  // map the input page to phantomJS options
259
264
  phantomjsOptions: options.phantomjsOptions(page),
260
265
  // useful for testing, debugging
266
+ debug: options.debug,
261
267
  __page: page
262
268
  });
263
269
 
@@ -7,7 +7,6 @@
7
7
  * Copyright (c) 2013 - 2022 Alex Grant, LocalNerve, contributors
8
8
  * Licensed under the MIT license.
9
9
  */
10
- /* global Promise */
11
10
 
12
11
  const urlm = require("url");
13
12
  const common = require("../common");