html-snapshots 0.18.3 → 0.19.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.
- package/.eslintrc.json +4 -5
- package/README.md +73 -68
- package/examples/custom/myFilter.js +2 -2
- package/examples/custom/package-lock.json +939 -4
- package/examples/custom/package.json +1 -1
- package/examples/custom/snapshot.js +8 -10
- package/examples/debug-phantomjs/package-lock.json +6 -6
- package/examples/debug-phantomjs/package.json +1 -1
- package/examples/debug-phantomjs/snapshot.js +12 -14
- package/examples/html5rocks/package-lock.json +1306 -5
- package/examples/html5rocks/package.json +1 -1
- package/examples/html5rocks/snapshot.js +15 -18
- package/examples/process-limit/package-lock.json +1306 -5
- package/examples/process-limit/package.json +1 -1
- package/examples/process-limit/snapshot.js +8 -10
- package/examples/simple-promise/package-lock.json +1306 -5
- package/examples/simple-promise/package.json +1 -1
- package/examples/simple-promise/snapshot.js +6 -8
- package/examples/sitemap-index/package-lock.json +1306 -5
- package/examples/sitemap-index/package.json +1 -1
- package/examples/sitemap-index/snapshot.js +6 -6
- package/examples/utils/index.js +2 -4
- package/examples/verbose/package-lock.json +1307 -5
- package/examples/verbose/package.json +1 -1
- package/examples/verbose/snapshot.js +11 -14
- package/lib/async/index.js +0 -1
- package/lib/common/index.js +1 -1
- package/lib/common/sitemap/index.js +16 -0
- package/lib/common/sitemap/sitemap-collection.js +94 -0
- package/lib/common/sitemap/sitemap-index.js +68 -0
- package/lib/common/{sitemap.js → sitemap/sitemap.js} +68 -57
- package/lib/html-snapshots.js +0 -1
- package/lib/input-generators/array.js +0 -1
- package/lib/input-generators/robots.js +98 -58
- package/lib/input-generators/sitemap-index.js +3 -124
- package/lib/input-generators/sitemap.js +3 -7
- package/lib/input-generators/textfile.js +2 -6
- package/package.json +5 -4
- package/test/helpers/options.js +4 -2
- package/test/mocha/html-snapshots/process-limit.js +46 -48
- package/test/mocha/html-snapshots/robots.js +156 -138
- package/test/mocha/html-snapshots/server/test_robots.txt +13 -0
- package/test/mocha/html-snapshots/server/test_robots_sitemap.txt +15 -0
- package/test/mocha/html-snapshots/server/test_robots_sitemap.xml +22 -0
- package/test/mocha/html-snapshots/sitemap-index.js +58 -57
- package/test/mocha/html-snapshots/snapshot-scripts.js +55 -54
- package/test/mocha/html-snapshots/test.js +16 -16
- package/test/mocha/html-snapshots/test_robots.txt +0 -2
- package/test/mocha/html-snapshots/utils.js +0 -1
- package/test/mocha/input-generators/server/test_robots.txt +0 -2
- package/test/mocha/input-generators/server/test_robots_bad.txt +0 -2
- package/test/mocha/input-generators/server/test_robots_sitemap.txt +17 -0
- package/test/mocha/input-generators/server/test_robots_sitemap_bad.txt +17 -0
- package/test/mocha/input-generators/server/test_robots_sitemap_multi.txt +18 -0
- package/test/mocha/input-generators/server/test_sitemap_index_empty.xml +3 -0
- package/test/mocha/input-generators/server/test_sitemap_index_malformed.xml +2 -0
- package/test/mocha/input-generators/sitemap-index.js +65 -9
- package/test/mocha/input-generators/test.js +276 -280
- package/test/mocha/input-generators/test_robots.txt +0 -2
- package/test/mocha/input-generators/test_robots_bad.txt +0 -2
- package/test/mocha/input-generators/test_robots_sitemap.txt +17 -0
- package/test/mocha/input-generators/test_robots_sitemap_bad.txt +17 -0
- package/test/mocha/input-generators/test_robots_sitemap_multi.txt +18 -0
- package/lib/common/node.js +0 -26
- package/test/mocha/common/node.js +0 -100
|
@@ -5,32 +5,29 @@
|
|
|
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
|
-
|
|
8
|
+
const htmlSnapshots = require("html-snapshots");
|
|
9
9
|
|
|
10
|
-
|
|
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
|
-
|
|
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
|
|
21
|
+
source: `${origin}/sitemap.xml`,
|
|
23
22
|
outputDir: require("path").join(__dirname, "./tmp"),
|
|
24
23
|
outputDirClean: true,
|
|
25
|
-
selector: "
|
|
26
|
-
timeout:
|
|
24
|
+
selector: "#mainContent",
|
|
25
|
+
timeout: 10000,
|
|
27
26
|
processLimit: 1, // to clarify output
|
|
28
27
|
verbose: verboseOption
|
|
29
28
|
})
|
|
30
|
-
.then(
|
|
29
|
+
.then(completed => {
|
|
31
30
|
console.log("completed snapshots:");
|
|
32
31
|
console.log(require("util").inspect(completed));
|
|
33
32
|
})
|
|
34
|
-
.catch(
|
|
35
|
-
console.error(err);
|
|
36
|
-
});
|
|
33
|
+
.catch(console.error);
|
package/lib/async/index.js
CHANGED
package/lib/common/index.js
CHANGED
|
@@ -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
|
|
18
|
-
const common = require("
|
|
19
|
-
const base = require("
|
|
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 - (
|
|
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 - (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
* @
|
|
176
|
+
* @return {Promise} resolves to data on completion.
|
|
169
177
|
*/
|
|
170
|
-
function getUrl (options, parseFn
|
|
171
|
-
|
|
178
|
+
async function getUrl (options, parseFn) {
|
|
179
|
+
const { default:got } = await import("got");
|
|
180
|
+
return got({
|
|
172
181
|
url: options.source,
|
|
173
|
-
|
|
174
|
-
timeout:
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
-
* @
|
|
204
|
+
* @return {Promise} resolves to data on completion.
|
|
193
205
|
*/
|
|
194
|
-
function getFile (options, parseFn
|
|
195
|
-
fs.readFile
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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 = {
|
package/lib/html-snapshots.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* robots.js
|
|
3
3
|
*
|
|
4
4
|
* An input generator for html-snapshots that uses a simple robots.txt file.
|
|
5
|
-
* Creates the snapshot arguments driven from robots.txt
|
|
6
|
-
* Does not support wildcards.
|
|
7
|
-
* If you need wildcards, use the sitemap input generator.
|
|
5
|
+
* Creates the snapshot arguments driven from robots.txt Sitemap or Allow directives.
|
|
8
6
|
*
|
|
9
7
|
* Copyright (c) 2013 - 2022 Alex Grant, LocalNerve, contributors
|
|
10
8
|
* Licensed under the MIT license.
|
|
11
9
|
*/
|
|
12
10
|
|
|
13
|
-
const fs = require("fs");
|
|
14
|
-
const request = require("request");
|
|
11
|
+
const fs = require("fs").promises;
|
|
15
12
|
const common = require("../common");
|
|
16
|
-
const
|
|
13
|
+
const { sitemapCollection: smc } = require("../common/sitemap");
|
|
17
14
|
const base = require("./_base");
|
|
18
15
|
|
|
19
16
|
// The default options
|
|
@@ -39,19 +36,81 @@ const robots = {
|
|
|
39
36
|
};
|
|
40
37
|
|
|
41
38
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
39
|
+
* Get all unique `Allow:` url paths from robots.txt.
|
|
40
|
+
* Reads the allow values up to whitespace (end-of-line), or wildcard.
|
|
41
|
+
*
|
|
42
|
+
* @param {String} body - The robots.txt file
|
|
43
|
+
* @returns {Array} collection of allow url paths
|
|
44
44
|
*/
|
|
45
|
-
function
|
|
46
|
-
const
|
|
47
|
-
|
|
45
|
+
function getAllowUrls (body) {
|
|
46
|
+
const allowUrls = new Set();
|
|
47
|
+
const re = /\ballow\b\s*:\s*(?<allowUrl>[^*\s]+)/ig;
|
|
48
|
+
const matches = body.matchAll(re);
|
|
49
|
+
let allowUrl;
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
for (const match of matches) {
|
|
52
|
+
allowUrl = match?.groups?.allowUrl;
|
|
53
|
+
if (allowUrl) {
|
|
54
|
+
allowUrls.add(allowUrl);
|
|
55
|
+
}
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
return
|
|
58
|
+
return allowUrls.size > 0 ? Array.from(allowUrls) : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get all the unique sitemap urls from robots.txt.
|
|
63
|
+
*
|
|
64
|
+
* @param {String} body - The robots.txt file
|
|
65
|
+
* @returns {Array} collection of unqiue sitemap urls
|
|
66
|
+
*/
|
|
67
|
+
function getSitemapUrls (body) {
|
|
68
|
+
const sitemapUrls = new Set();
|
|
69
|
+
const re = /\bsitemap\b\s*:\s*(?<sitemapUrl>[^\s]+)/ig;
|
|
70
|
+
const matches = body.matchAll(re);
|
|
71
|
+
let sitemapUrl;
|
|
72
|
+
|
|
73
|
+
for (const match of matches) {
|
|
74
|
+
sitemapUrl = match?.groups?.sitemapUrl;
|
|
75
|
+
if (sitemapUrl) {
|
|
76
|
+
sitemapUrls.add(sitemapUrl);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return sitemapUrls.size > 0 ? Array.from(sitemapUrls) : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parse urls from robots.txt and generate input.
|
|
85
|
+
* Look for sitemaps first, then look for allow urls.
|
|
86
|
+
*
|
|
87
|
+
* @param {Object} options - robots.txt options
|
|
88
|
+
* @param {String} body - robots.txt content
|
|
89
|
+
* @returns {Promise} resolves on success
|
|
90
|
+
*/
|
|
91
|
+
function processRobotsTxt (options, body) {
|
|
92
|
+
let allowUrls = false;
|
|
93
|
+
const urls = getSitemapUrls(body) || (allowUrls = getAllowUrls(body));
|
|
94
|
+
|
|
95
|
+
if (allowUrls) {
|
|
96
|
+
let result;
|
|
97
|
+
for (const url of urls) {
|
|
98
|
+
result = base.input(options, url);
|
|
99
|
+
// if base.input returns false, then no outputDir was defined,
|
|
100
|
+
// which is a fatal bad option problem and will happen immediately.
|
|
101
|
+
if (!result) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
common.prependMsgToErr(base.generatorError(), url, true)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return Promise.resolve();
|
|
108
|
+
} else {
|
|
109
|
+
if (urls && urls.length > 0) {
|
|
110
|
+
return smc.process(options, urls);
|
|
111
|
+
}
|
|
112
|
+
throw new Error(`No urls found in ${options.source}`);
|
|
113
|
+
}
|
|
55
114
|
}
|
|
56
115
|
|
|
57
116
|
/**
|
|
@@ -60,54 +119,37 @@ function oneline (line, options) {
|
|
|
60
119
|
* @param {Object} options - Robots.txt options
|
|
61
120
|
* @param {String} options.source - The URL to a robots.txt
|
|
62
121
|
* @param {Function} options.timeout - Returns the robots.txt request timeout.
|
|
63
|
-
* @
|
|
122
|
+
* @returns {Promise} resolves to undefined.
|
|
64
123
|
*/
|
|
65
|
-
function getRobotsUrl (options
|
|
66
|
-
|
|
124
|
+
async function getRobotsUrl (options) {
|
|
125
|
+
const { default:got } = await import("got");
|
|
126
|
+
return got({
|
|
67
127
|
url: options.source,
|
|
68
|
-
timeout:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
128
|
+
timeout: {
|
|
129
|
+
request: options.timeout()
|
|
130
|
+
}
|
|
131
|
+
}).then(res => {
|
|
132
|
+
const error = common.checkResponse(res, "text/plain");
|
|
72
133
|
if (error) {
|
|
73
|
-
|
|
74
|
-
} else {
|
|
75
|
-
body.toString().split('\n').every(line => {
|
|
76
|
-
// Process the line input, but break if base.input returns false.
|
|
77
|
-
// For now, this can only happen if no outputDir is defined,
|
|
78
|
-
// which is a fatal bad option problem and will happen immediately.
|
|
79
|
-
if (!oneline(line, options)) {
|
|
80
|
-
error = common.prependMsgToErr(base.generatorError(), line, true);
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
return true;
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
callback(error);
|
|
134
|
+
throw new Error(error);
|
|
87
135
|
}
|
|
136
|
+
return processRobotsTxt(options, res.body.toString());
|
|
137
|
+
}).catch(err => {
|
|
138
|
+
throw new Error(common.prependMsgToErr(err, options.source, true));
|
|
88
139
|
});
|
|
89
140
|
}
|
|
90
141
|
|
|
91
142
|
/**
|
|
92
143
|
* Reads the robots.txt file and parses it.
|
|
144
|
+
*
|
|
145
|
+
* @param {Object} options - Robots.txt options
|
|
93
146
|
*/
|
|
94
|
-
function getRobotsFile (options
|
|
95
|
-
fs.readFile(options.source
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// which is a fatal bad option problem and will happen immediately.
|
|
101
|
-
if (!oneline(line, options)) {
|
|
102
|
-
err = common.prependMsgToErr(base.generatorError(), line, true);
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
return true;
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
callback(err);
|
|
110
|
-
});
|
|
147
|
+
function getRobotsFile (options) {
|
|
148
|
+
return fs.readFile(options.source)
|
|
149
|
+
.then(data => processRobotsTxt(options, data.toString()))
|
|
150
|
+
.catch(err => {
|
|
151
|
+
throw new Error(common.prependMsgToErr(err, options.source, true));
|
|
152
|
+
});
|
|
111
153
|
}
|
|
112
154
|
|
|
113
155
|
/**
|
|
@@ -122,10 +164,8 @@ function getRobotsFile (options, callback) {
|
|
|
122
164
|
* @returns {Promise} Resolves to undefined on completion.
|
|
123
165
|
*/
|
|
124
166
|
function generateInput (options) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
options
|
|
128
|
-
)
|
|
167
|
+
const retrieve = common.isUrl(options.source) ? getRobotsUrl : getRobotsFile;
|
|
168
|
+
return retrieve(options)
|
|
129
169
|
.catch(err => {
|
|
130
170
|
options._abort(err);
|
|
131
171
|
})
|