html-snapshots 5.11.0 → 6.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.
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 24.16
package/HISTORY.md CHANGED
@@ -3,6 +3,12 @@ An overview of the [Breaking Changes](#breaking-changes) and [Node Support](#nod
3
3
 
4
4
  ## Breaking Changes
5
5
 
6
+ ### Introduced in v6.0.0
7
+ #### Dropped support for Node 20
8
+ #### Dropped support for Phantomjs
9
+ #### Added Playwright as a secondary browser choice
10
+ #### Added standard AggregateError usage for multi-error reporting
11
+
6
12
  ### Introduced in v5.0.0
7
13
  #### Dropped support for Node 18
8
14
 
package/README.md CHANGED
@@ -7,34 +7,46 @@
7
7
  > Takes html snapshots of your site's crawlable pages when an element you select is rendered.
8
8
 
9
9
  ## Contents
10
- + [Overview](#overview)
11
- + [Getting Started](#getting-started)
12
- + [Grunt Task](https://github.com/localnerve/grunt-html-snapshots)
13
- + [More Information](#more-information)
14
- + [API Reference](#api)
15
- + [Example Usage](#example-usage)
16
- + [Option Reference](#options)
17
- + [Input Options](#input-control-options)
18
- + [Output Options](#output-control-options)
19
- + [Snapshot Options](#snapshot-control-options)
20
- + [Process Options](#process-control-options)
21
- + [Rewrite and Middleware Examples](#example-rewrite-rule)
22
- + [Worker Process and Middleware Article and Examples](/docs/example-heroku-redis.md)
23
- + [License](#license)
10
+ - [html-snapshots](#html-snapshots)
11
+ - [Contents](#contents)
12
+ - [Overview](#overview)
13
+ - [Getting Started](#getting-started)
14
+ - [Installation](#installation)
15
+ - [Grunt Task](#grunt-task)
16
+ - [More Information](#more-information)
17
+ - [Process Model](#process-model)
18
+ - [API](#api)
19
+ - [*Promise* run (options\[, callback\])](#promise-run-options-callback)
20
+ - [Callback](#callback)
21
+ - [Example Usage](#example-usage)
22
+ - [Quick Example](#quick-example)
23
+ - [Options](#options)
24
+ - [Input Control Options](#input-control-options)
25
+ - [Sitemap Only Input Options](#sitemap-only-input-options)
26
+ - [Origin Options](#origin-options)
27
+ - [Output Control Options](#output-control-options)
28
+ - [Snapshot Control Options](#snapshot-control-options)
29
+ - [Process Control Options](#process-control-options)
30
+ - [Example Rewrite Rule](#example-rewrite-rule)
31
+ - [Connect-modrewrite](#connect-modrewrite)
32
+ - [Middleware Example](#middleware-example)
33
+ - [License](#license)
24
34
 
25
35
  ## Overview
26
36
  html-snapshots is a flexible html snapshot library that uses a headless browser to take html snapshots of your webpages served from your site. A snapshot is only taken when a specified selector is detected visible in the output html. This tool is useful when your site is largely ajax content, or an SPA, and you want your dynamic content indexed by search engines.
27
37
 
28
38
  html-snapshots gets urls to process from either a robots.txt, sitemap.xml, or sitemap-index.xml. Alternatively, you can supply an array with completely arbitrary urls, or a line delimited textfile with arbitrary host-relative paths.
29
39
 
40
+ ## Change History
41
+ This is an old project that has evolved since 2013. I've endeavored to keep it easy to use and upgrade. However, before upgrading, please review the release notes and the [history](HISTORY.md) for breaking changes.
42
+
30
43
  ## Getting Started
31
44
 
32
45
  ### Installation
33
- The simplest way to install html-snapshots is to use [npm](http://npmjs.org), just `npm
34
- install html-snapshots` will download html-snapshots and all dependencies.
46
+ The simplest way to install html-snapshots is to use [npm](http://npmjs.org). `npm
47
+ install html-snapshots` will download html-snapshots and all dependencies. To install the browsers required by playwright, you must also run `npx playwright install`. See [Playwright Docs](https://playwright.dev/docs/browsers) for additional details.
35
48
 
36
- ### Gulp Task
37
- This is a node library that just works with gulp as-is.
49
+ By default, html-snapshots uses **Puppeteer** under the hood. You can optionally configure it to use **Playwright** instead by setting the [`browser`](#process-control-options) option to `"playwright"`.
38
50
 
39
51
  ### Grunt Task
40
52
  If you are interested in the grunt task that uses this library, check out [grunt-html-snapshots](http://github.com/localnerve/grunt-html-snapshots).
@@ -45,6 +57,7 @@ Here are some [background and other notes](/docs/notes.md) regarding this projec
45
57
  + [How To Use Without Knowing About Page Content](/docs/notes.md#what-if-i-dont-know-about-the-rendered-page-content)
46
58
  + [Caveats](/docs/notes.md#caveats)
47
59
  + [Support History](HISTORY.md)
60
+ + [Web Automation & Building AI agents](/docs/web-automation-building.md)
48
61
 
49
62
  ### Process Model
50
63
  html-snapshots takes snapshots in parallel, each page getting its own browser process. Each browser process dies after snapshotting one page. You can limit the number of browser processes that can ever run at once with the `processLimit` option. This effectively sets up a process pool for browser instances. The default processLimit is 4 browser instances. When a browser process dies, and another snapshot needs to be taken, a new browser process is spawned to take the vacant slot. This continues until a `processLimit` number of processes are running at once.
@@ -63,7 +76,7 @@ htmlSnapshots.run(options[, callback])
63
76
  // `completed` is an array of paths to the completed snapshots.
64
77
  })
65
78
  .catch(errorObject => {
66
- // `errorObject` is an instance of Error
79
+ // `errorObject` is an instance of Error or AggregateError
67
80
  // `errorObject.completed` is an array of paths to the snapshots that did successfully complete.
68
81
  // `errorObject.notCompleted` is an array of paths to files that DID NOT successfully complete.
69
82
  });
@@ -73,9 +86,9 @@ The callback is optional because the run method returns a Promise that resolves
73
86
 
74
87
  Signature of the optional callback:
75
88
  ```javascript
76
- callback (errorObject, arrayOfPathsToCompletedSnapshots)
89
+ callback (errorOrAggregateErrorObject, arrayOfPathsToCompletedSnapshots)
77
90
  ```
78
- *For the callback, in the error case, the errorObject does not have the new extra properties `completed` and `notCompleted`. However, `arrayOfPathsToCompletedSnapshots` is supplied, and contains the paths to the snapshots that successfully completed.*
91
+ *For the callback, in the error \(or AggregateError\) case, the errorObject does not have the new extra properties `completed` and `notCompleted`. However, `arrayOfPathsToCompletedSnapshots` is supplied, and contains the paths to the snapshots that successfully completed.*
79
92
 
80
93
  ## Example Usage
81
94
  This example reads the pages from a mix of sitemap or sitemap-index files found in the robots.txt and produces snapshots in the ./snapshots directory. In this example, a selector named "#dynamic-content" appears in all pages across the site. Once this selector is visible in a page, the html snapshot is taken and saved to ./snapshots.
@@ -202,7 +215,7 @@ An older (version 0.13.2), more in depth usage example is located in this [artic
202
215
 
203
216
  `"function"` If the value is a function, it is called for every page and passed a single argument that is the url (or path in the case of robots.txt style) found in the input. The function must return a value to use for this option for the page it is given. The value returned for a given page must be a string.
204
217
 
205
- NOTE: By default, selectors must conform to [this spec](http://www.w3.org/TR/selectors-api/#grammar), as they are used by [querySelector](https://developer.mozilla.org/en-US/docs/Web/API/document.querySelector). If you need selectors not supported by this, you must specify the `useJQuery` option, and load jQuery in your page.
218
+ NOTE: By default, selectors must conform to [this spec](http://www.w3.org/TR/selectors-api/#grammar), as they are used by [querySelector](https://developer.mozilla.org/en-US/docs/Web/API/document.querySelector). If you need selectors not supported by this, you must load jQuery in your page and use a custom snapshot script.
206
219
 
207
220
  * **snapshotScript** {String|Object}
208
221
  + default: This library's default snapshot script. Which one is used is determined by the [`browser`](#process-control-options) option.
@@ -212,18 +225,6 @@ An older (version 0.13.2), more in depth usage example is located in this [artic
212
225
 
213
226
  `"string"` If the value is a string, it must an absolute path to a custom script you supply.
214
227
 
215
- + `browser: "phantomjs"`:
216
- html-snapshots will spawn a separate phantomjs process to run your snapshot script and give it the following [arguments](http://phantomjs.org/api/system/property/args.html):
217
- + `system.args[0]` The path to your PhantomJS script.
218
- + `system.args[1]` The output file path.
219
- + `system.args[2]` The url to snapshot.
220
- + `system.args[3]` The selector to watch for to signal page completion.
221
- + `system.args[4]` The overall timeout \(milliseconds\).
222
- + `system.args[5]` The interval \(milliseconds\) to watch for the selector.
223
- + `system.args[6]` A flag indicating jQuery selectors should be supported.
224
- + `system.args[7]` A flag indicating verbose output is desired.
225
- + `system.args[8]` A custom module to load.
226
-
227
228
  + `browser: "puppeteer"`:
228
229
  html-snapshots will spawn your script as a separate process and give it the following arguments:
229
230
  + `process.argv[0]` The path to NodeJS.
@@ -237,11 +238,24 @@ An older (version 0.13.2), more in depth usage example is located in this [artic
237
238
  + `process.argv[8]` A slowMo time \(milliseconds\) to slow the browser down.
238
239
  + `process.argv[9]` Stringified Puppeteer launch options.
239
240
 
241
+ + `browser: "playwright"`:
242
+ html-snapshots will spawn your script as a separate process and give it the following arguments:
243
+ + `process.argv[0]` The path to NodeJS.
244
+ + `process.argv[1]` The path to your snapshot script.
245
+ + `process.argv[2]` The output file path.
246
+ + `process.argv[3]` The url to snapshot.
247
+ + `process.argv[4]` The selector to wait for to signal page completion.
248
+ + `process.argv[5]` The overall timeout \(milliseconds\).
249
+ + `process.argv[6]` The path to a custom NodeJS module that returns a filter function.
250
+ + `process.argv[7]` A debug flag to kick the browser into headed, slowMo mode.
251
+ + `process.argv[8]` A slowMo time \(milliseconds\) to slow the browser down.
252
+ + `process.argv[9]` Stringified Playwright launch options. May include a `browserType` key (`"chromium"`, `"firefox"`, or `"webkit"`). Defaults to `"chromium"`.
253
+
240
254
  `"object"` If an object is supplied, it has the following properties:
241
255
  + `script` This must be one of the following values:
242
- + `"removeScripts"` This runs the default snapshot script with an output filter that removes all script tags are removed from the html snapshot before it is saved.
256
+ + `"removeScripts"` This runs the default snapshot script with an output filter that removes all script tags from the html snapshot before it is saved.
243
257
  + `"customFilter"` This runs the default snapshot script, but allows you to supply any output filter.
244
- + `module` This property is required only if you supplied a value of `"customFilter"` for the `script` property. This must be an absolute path to a PhantomJS module you supply. Your module will be `require`d and called as a function to filter the html snapshot output. Your module's function will receive the entire raw html content as a single input string, and must return the filtered html content.
258
+ + `module` This property is required only if you supplied a value of `"customFilter"` for the `script` property. This must be an absolute path to a NodeJS module you supply. Your module will be `require`d and called as a function to filter the html snapshot output. Your module's function will receive the entire raw html content as a single input string, and must return the filtered html content.
245
259
 
246
260
  customFilter Example:
247
261
  ```javascript
@@ -261,43 +275,14 @@ An older (version 0.13.2), more in depth usage example is located in this [artic
261
275
  A more complete example using custom options is available [here](/examples/custom).
262
276
 
263
277
  * **debug** {Object}
264
- > This options is only supported with the puppeteer browser script.
265
278
  + default: `{ flag: false, slowMo: 500 }`
266
- + Setting the `debug.flag` to true starts chrome in headed mode with devtools open. `debug.slowMo` is a time in milliseconds to reduce browser processing speed (larger numbers slows down chrome more). Recommended use is with a single problem page input using an Array source.
267
-
268
- * **useJQuery** {Boolean|Object|Function}
269
- > This option is only supported with the phantomjs browser script.
270
- + default: `false`
271
- + Specifies to use jQuery selectors to detect when to snapshot a page. Please note that you cannot use these selectors if the page to be snapshotted does not load jQuery itself. To return to the behavior prior to v0.6.x, set this to `true`.
272
-
273
- The value can be one of these *javascript types*:
274
-
275
- `"boolean"` If the value is a boolean, it is used for every page. Note that if it is any scalar type such as "string" or "number", it will be interpreted as a boolean using javascript rules. Coerced string values "true", "yes", and "1" are specifically true, all others are false.
276
-
277
- `"object"` If the value is an object, it is interpreted as key/value pairs where the key must match the url (or path in the case of robots.txt style) found by the input generator. This allows you to specify the use of jQuery for individual pages. The reserved key "__default" allows you to specify a default jQuery usage so you don't have to specify usage for every individual page.
278
-
279
- `"function"` If the value is a function, it is called for every page and passed a single argument that is the url (or path in the case of robots.txt style) found in the input. The function must return a value to use for this option for the page it is given. The value returned for a given page must be a boolean.
280
-
281
- NOTE: You do not *have to* use this option if your page uses jQuery. You only need this if your selector is not supported by [querySelector](https://developer.mozilla.org/en-US/docs/Web/API/document.querySelector). However, if you do use this option, the page being snapshotted must load jQuery itself.
282
-
283
- * **verbose** {Boolean|Object|Function}
284
- > This option is only used with the phantomjs browser script
285
- + default: `false`
286
- + Specifies to turn on extended console output in the PhantomJS process for debugging purposes. Can be applied to all pages, or just specific page(s). It is recommended to do this one page at a time, as the output can be large, and interleaved with parallel processes. See following explanation of types for how to debug just one page, and also [this example](/examples/verbose).
287
-
288
- The value can be one of these *javascript types*:
289
-
290
- `"boolean"` If the value is a boolean, it is used for every page. Note that if it is any scalar type such as "string" or "number", it will be interpreted as a boolean using javascript rules. Coerced string values "true", "yes", and "1" are specifically true, all others are false.
291
-
292
- `"object"` If the value is an object, it is interpreted as key/value pairs where the key must match the url (or path in the case of robots.txt style) found by the input generator. This allows you to specify the use of verbose output for individual pages. The reserved key "__default" allows you to specify the default `verbose` usage so you don't have to specify usage for every individual page.
293
-
294
- `"function"` If the value is a function, it is called for every page and passed a single argument that is the url (or path in the case of robots.txt style) found in the input. The function must return a value to use for this option for the page it is given. The value returned for a given page must be a boolean.
279
+ + Supported with both puppeteer and playwright browser scripts. Setting the `debug.flag` to true starts the browser in headed mode with devtools open (devtools is only supported for chromium-based browsers). `debug.slowMo` is a time in milliseconds to reduce browser processing speed (larger numbers slows down the browser more). Recommended use is with a single problem page input using an Array source.
295
280
 
296
281
  ### Process Control Options
297
282
 
298
283
  * **browser** {String}
299
284
  + default: `"puppeteer"`
300
- + Specifies which browser process to use in the crawl. Can be one of "phantomjs" or "puppeteer".
285
+ + Specifies which browser process to use in the crawl. Can be one of `"puppeteer"` or `"playwright"`.
301
286
 
302
287
  * **timeout** {Number|Object|Function}
303
288
  + default: 10000 \(milliseconds\)
@@ -313,19 +298,14 @@ An older (version 0.13.2), more in depth usage example is located in this [artic
313
298
 
314
299
  * **processLimit** {Number}
315
300
  + default: 4
316
- + Limits the number of child PhantomJS processes that can ever be actively running in parallel. A value of 1 effectively forces the snapshots to be taken in series (only one at a time). Useful if you need to limit the number of processes spawned by this library. Experiment with what works best. One guideline suggests about [4 per CPU](http://stackoverflow.com/questions/9961254/how-to-manage-a-pool-of-phantomjs-instances).
301
+ + Limits the number of child browser processes that can ever be actively running in parallel. A value of 1 effectively forces the snapshots to be taken in series (only one at a time). Useful if you need to limit the number of processes spawned by this library. Experiment with what works best. One guideline suggests about [4 per CPU](http://stackoverflow.com/questions/9961254/how-to-manage-a-pool-of-phantomjs-instances).
317
302
 
318
303
  * **pollInterval** {Number}
319
304
  + default: 500 \(milliseconds\)
320
305
  + Specifies the rate at which html-snapshots checks to see if a browser script has completed. Applies to all pages.
321
306
 
322
- * **checkInterval** {Number}
323
- > This option is only used with the phantomjs browser script
324
- + default: 250 (milliseconds)
325
- + Specifies the rate at which the PhantomJS script checks to see if the selector is visible yet. Applies to all pages.
326
-
327
307
  * **puppeteerLaunchOptions** {Object|Function}
328
- > This options is only used with the puppeteer browser script
308
+ > This option is only used with the puppeteer browser script
329
309
  + default: {}
330
310
  + Specifies launch options to give to Puppeteer. Can specify per page or for all pages. Puppeteer function options (like targetFilter) are not supported. Launch options will override any puppeteer debug options supplied.
331
311
 
@@ -335,46 +315,26 @@ An older (version 0.13.2), more in depth usage example is located in this [artic
335
315
 
336
316
  "`function`" If the value is a function, it is called for every page and passed a single argument that is the url found in the input. The function must return puppeteer launch options to use for the page it is given. The value returned for a given page must be an object to be given directly to `puppeter.launch`.
337
317
 
338
- * **phantomjsOptions** {String|Array|Object|Function}
339
- > This option is only used with the phantomjs browser script
340
- + default: ""
341
- + Specifies options to give to PhantomJS. Can specify per page or for all pages. Since PhantomJS instances run per page, it is possible to specify different PhantomJS options per page. Useful for debugging PhantomJS scripts on a specific page.
342
- For PhantomJS options syntax, checkout the [current options](http://phantomjs.org/api/command-line.html).
343
- Checkout [the source](https://github.com/ariya/phantomjs/blob/master/src/config.cpp#L49) for PhantomJS options coming next.
344
-
318
+ * **playwrightLaunchOptions** {Object|Function}
319
+ > This option is only used with the playwright browser script
320
+ + default: {}
321
+ + Specifies launch options to give to Playwright. Can specify per page or for all pages. The object may include a `browserType` key (`"chromium"`, `"firefox"`, or `"webkit"`) to select which browser engine to use. Defaults to `"chromium"`. Launch options will override any playwright debug options supplied.
322
+
345
323
  The value can be one of these *javascript types*:
346
324
 
347
- `"string"` If the value is a string, it is a single option string used for every page.
348
-
349
- `"array"` If the value is an array, it can contain multiple option strings used for every page.
350
-
351
- `"object"` If the value is an object, it is interpreted as key/value pairs where the key must match the url (or path in the case of robots.txt style) found by the input generator. This allows you to specify PhantomJS options for individual pages. The reserved key "__default" allows you to specify default options so you don't have to specify options for every individual page. The values can be either a string \(for a single option\), or an array \(for multiple options \).
325
+ `"object"` If the value is an object, it can contain any playwright launch options, and is applied for all pages. The object (minus `browserType`) will be given directly to `browserType.launch`.
352
326
 
353
- `"function"` If the value is a function, it is called for every page and passed a single argument that is the url (or path in the case of robots.txt style) found in the input. The function must return a value to use for this option for the page it is given. The value returned for a given page must be either a string or an array.
327
+ "`function`" If the value is a function, it is called for every page and passed a single argument that is the url found in the input. The function must return playwright launch options to use for the page it is given. The value returned for a given page must be an object to be given directly to `browserType.launch`.
354
328
 
355
- Multiple Options Examples:
329
+ Example using Firefox:
356
330
  ```javascript
357
- // option snippet showing multiple options for all pages
358
331
  {
359
- phantomjsOptions: ["--load-images=false", "--ignore-ssl-errors=true"]
360
- }
361
-
362
- // option snippet showing multiple options for one page only (object notation)
363
- {
364
- phantomjsOptions: {
365
- // key must exactly match the page as defined in the input (sitemap, array, robots, etc)
366
- "http://mysite.com/mypage": ["--load-images=false", "--ignore-ssl-errors=true"],
332
+ browser: "playwright",
333
+ playwrightLaunchOptions: {
334
+ browserType: "firefox"
367
335
  }
368
336
  }
369
337
  ```
370
- An example demonstrating how to **debug** a PhantomJS script is available [here](/examples/debug-phantomjs). It also demonstrates per-page option usage.
371
-
372
- * **phantomjs** {String}
373
- > This option is only used with the phantomjs browser script
374
- + default: A package local reference to PhantomJS.
375
- + Specifies the PhantomJS executable to run. Applies to all pages. Override this if you want to supply a path to a different version of PhantomJS. To reference PhantomJS globally in your environment, just use the value, "phantomjs". Remember, it must be found in your environment path to execute.
376
- See [PhantomJS](http://phantomjs.org/) for more information.
377
- Also [PhantomJS 2](https://github.com/ariya/phantomjs/wiki/PhantomJS-2).
378
338
 
379
339
  ## Example Rewrite Rule
380
340
  Here is an example apache rewrite rule for rewriting \_escaped\_fragment\_ requests to the snapshots directory on your server.
@@ -400,4 +360,4 @@ Here is the [article](/docs/example-heroku-redis.md) on how this middleware work
400
360
  ## License
401
361
  This software is free to use under the LocalNerve, LLC MIT license. See the [LICENSE file](/LICENSE) for license text and copyright information.
402
362
 
403
- Third-party open source code used are listed in the [package.json file](/package.json).
363
+ Third-party open source code used are listed in the [package.json file](/package.json).
package/eslint.config.js CHANGED
@@ -1,16 +1,16 @@
1
- const js = require('@eslint/js');
2
- const globals = require('globals');
1
+ const js = require("@eslint/js");
2
+ const globals = require("globals");
3
3
 
4
4
  module.exports = [{
5
5
  ignores: [
6
- 'tmp/**',
7
- 'coverage/**',
8
- 'examples/**',
9
- 'test/mocha/html-snapshots/server/**'
6
+ "tmp/**",
7
+ "coverage/**",
8
+ "examples/**",
9
+ "test/suites/html-snapshots/server/**"
10
10
  ]
11
11
  }, {
12
12
  languageOptions: {
13
- sourceType: 'commonjs',
13
+ sourceType: "commonjs",
14
14
  globals: {
15
15
  ...globals.node
16
16
  }
@@ -18,6 +18,7 @@ module.exports = [{
18
18
  files: ["**/*.js"],
19
19
  rules: {
20
20
  ...js.configs.recommended.rules,
21
- "no-console": 0
21
+ "no-console": 0,
22
+ "quotes": ["error", "double"]
22
23
  }
23
24
  }];
@@ -9,7 +9,7 @@
9
9
  * Licensed under the MIT license.
10
10
  */
11
11
 
12
- const fs = require("fs").promises;
12
+ const { promises: fs } = require("node:fs");
13
13
 
14
14
  /**
15
15
  * Check if a path exists.
@@ -8,9 +8,8 @@
8
8
  * Licensed under the MIT license.
9
9
  */
10
10
 
11
- const crypto = require("crypto");
11
+ const crypto = require("node:crypto");
12
12
  const AsyncLock = require("async-lock");
13
- const combineErrors = require("combine-errors");
14
13
  const pathExists = require("./exists");
15
14
 
16
15
  const L_WAIT = 10000;
@@ -332,7 +331,9 @@ class Notifier {
332
331
  * @returns {Object} The error property.
333
332
  */
334
333
  getError () {
335
- return this.errors.length > 0 ? combineErrors(this.errors) : undefined;
334
+ return this.errors.length > 0
335
+ ? this.errors.length > 1 ? new AggregateError(this.errors) : this.errors[0]
336
+ : undefined;
336
337
  }
337
338
 
338
339
  /**
@@ -4,8 +4,8 @@
4
4
  * Copyright (c) 2013 - 2025 Alex Grant, LocalNerve, contributors
5
5
  * Licensed under the MIT license.
6
6
  */
7
- const fs = require("fs");
8
- const pathLib = require("path");
7
+ const fs = require("node:fs");
8
+ const pathLib = require("node:path");
9
9
  const common = require("../index");
10
10
  const base = require("../../input-generators/_base");
11
11
 
@@ -8,11 +8,11 @@
8
8
  * Licensed under the MIT license.
9
9
  */
10
10
 
11
- const fs = require("fs");
12
- const util = require("util");
13
- const path = require("path");
14
- const urlm = require("url");
15
- const zlib = require("zlib");
11
+ const fs = require("node:fs");
12
+ const util = require("node:util");
13
+ const path = require("node:path");
14
+ const urlm = require("node:url");
15
+ const zlib = require("node:zlib");
16
16
  const xml2js = require("xml2js");
17
17
  const smi = require("./sitemap-index");
18
18
  const common = require("../index");
@@ -10,52 +10,19 @@
10
10
  * Licensed under the MIT license.
11
11
  */
12
12
 
13
- const spawn = require("child_process").spawn;
14
- const path = require("path");
15
- const EventEmitter = require("events").EventEmitter;
16
- const fs = require("fs");
13
+ const { spawn } = require("node:child_process");
14
+ const path = require("node:path");
15
+ const { EventEmitter } = require("node:events");
16
+ const fs = require("node:fs");
17
17
  const asyncLib = require("async");
18
18
  const common = require("./common");
19
19
  const inputFactory = require("./input-generators");
20
- const Notifier = require("./async").Notifier;
20
+ const { Notifier } = require("./async");
21
21
 
22
- /**
23
- * Determine the default phantomJS module path. This is overridden by the
24
- * phatomjs option.
25
- *
26
- * This function, (technique and concerns) originated from karma-phantomjs-launcher:
27
- *
28
- * The MIT License
29
- * Copyright (C) 2011-2013 Google, Inc.
30
- *
31
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
32
- * this software and associated documentation files (the "Software"), to deal in
33
- * the Software without restriction, including without limitation the rights to
34
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
35
- * of the Software, and to permit persons to whom the Software is furnished to do
36
- * so, subject to the following conditions:
37
- *
38
- * The above copyright notice and this permission notice shall be included in all
39
- * copies or substantial portions of the Software.
40
- */
41
- function defaultPhantomJSExePath () {
42
- // If the path we're given by phantomjs is to a .cmd, it is pointing to a global copy.
43
- // Using the cmd as the process to execute causes problems cleaning up the processes
44
- // so we walk from the cmd to the phantomjs.exe and use that instead.
45
-
46
- const phantomSource = require("phantomjs-prebuilt").path;
47
-
48
- if (path.extname(phantomSource).toLowerCase() === ".cmd") {
49
- return path.join(path.dirname( phantomSource ), "//node_modules//phantomjs-prebuilt//lib//phantom//bin//phantomjs.exe");
50
- }
51
-
52
- return phantomSource;
53
- }
54
-
55
- const phantomDir = "./phantom";
56
- const phantomSnapshotScript = path.join(phantomDir, "default.js");
57
22
  const puppeteerDir = "./puppeteer";
58
23
  const puppeteerSnapshotScript = path.join(puppeteerDir, "index.js");
24
+ const playwrightDir = "./playwright";
25
+ const playwrightSnapshotScript = path.join(playwrightDir, "index.js");
59
26
 
60
27
  /**
61
28
  * This module's defaults
@@ -63,9 +30,8 @@ const puppeteerSnapshotScript = path.join(puppeteerDir, "index.js");
63
30
  const defaults = Object.freeze({
64
31
  input: "robots",
65
32
  browser: "puppeteer",
66
- phantomjs: defaultPhantomJSExePath(),
67
- phantomSnapshotScript: path.join(__dirname, phantomSnapshotScript),
68
33
  puppeteer: path.join(__dirname, puppeteerSnapshotScript),
34
+ playwright: path.join(__dirname, playwrightSnapshotScript),
69
35
  snapshotScript: path.join(__dirname, puppeteerSnapshotScript),
70
36
  outputDirClean: false,
71
37
  pollInterval: 500,
@@ -73,22 +39,18 @@ const defaults = Object.freeze({
73
39
  });
74
40
 
75
41
  /**
76
- * The worker task that launches phantomjs.
77
- *
78
42
  * @param {Object} input - An input object from an input generator.
79
- * @param {Array} input.phantomjsOptions - Actual Phantomjs options.
80
43
  * @param {String} input.outputFile - The output file that the html is written to.
81
44
  * @param {String} input.url - The url to get the html from.
82
45
  * @param {String} input.selector - The selector to wait for.
83
- * @param {String} input.timeout - The phantomjs timeout.
84
- * @param {String} input.checkInterval - The interval to poll for results.
85
- * @param {Boolean} input.useJQuery - True if selector is jquery selector.
86
- * @param {Boolean} input.verbose - True if verbose output is desired.
46
+ * @param {String} input.timeout - The entire process timeout.
47
+ * @param {Object} input.debug - The debug object.
48
+ * @param {Object} input.puppeteerLaunchOptions - overriding puppeteer launch options.
87
49
  * @param {Object} options - Input options.
88
50
  * @param {Object} options.snapshotScript - Snapshot script options.
89
51
  * @param {String} options.snapshotScript.script - Snapshot script name.
90
52
  * @param {String} options.snapshotScript.module - Snapshot script module.
91
- * @param {String} options.phantomjs - Full path to the Phantomjs exe.
53
+ * @param {String} options.puppeteer - Full path to the puppeteer exec.
92
54
  * @param {Object} notifier - The async Notifier instance for this run.
93
55
  * @param {Function} notifier.remove - Remove an outputFile from notifier.
94
56
  * @param {Function} notifier.setError - Set an error on the notifier.
@@ -96,34 +58,34 @@ const defaults = Object.freeze({
96
58
  * @param {Function} notifier.known - Check if a file is or has been processed.
97
59
  * @param {Function} qcb - async.queue callback function.
98
60
  */
99
- function phantomjsWorker (input, options, notifier, qcb) {
100
- let cp, customModule;
61
+ function puppeteerWorker (input, options, notifier, qcb) {
62
+ let filter = "false";
101
63
  let snapshotScript = options.snapshotScript;
102
- const phantomjsOptions =
103
- Array.isArray(input.phantomjsOptions) ? input.phantomjsOptions : [input.phantomjsOptions];
104
64
 
105
65
  // If the outputFile has NOT already been seen by the notifier, process.
106
66
  if (!notifier.known(input.outputFile)) {
107
67
 
108
- // map snapshotScript object script to a real path
68
+ // map snapshotScript object script
109
69
  if (common.isObject(options.snapshotScript)) {
110
- snapshotScript = `${path.join(__dirname, phantomDir, options.snapshotScript.script)}.js`;
111
- customModule = options.snapshotScript.module;
70
+ snapshotScript = options.puppeteer;
71
+ if (options.snapshotScript.script === "removeScripts") {
72
+ filter = "./removeScripts";
73
+ } else {
74
+ filter = options.snapshotScript.module;
75
+ }
112
76
  }
113
77
 
114
- cp = spawn(
115
- options.phantomjs,
116
- phantomjsOptions.concat([
117
- snapshotScript,
118
- input.outputFile,
78
+ const cp = spawn(
79
+ snapshotScript,
80
+ [ input.outputFile,
119
81
  input.url,
120
82
  input.selector,
121
83
  input.timeout,
122
- input.checkInterval,
123
- input.useJQuery,
124
- input.verbose,
125
- customModule
126
- ]), { cwd: process.cwd(), stdio: "inherit", detached: true }
84
+ filter,
85
+ input.debug.flag,
86
+ input.debug.slowMo,
87
+ JSON.stringify(input.puppeteerLaunchOptions)
88
+ ], { cwd: process.cwd(), stdio: "inherit", detached: true }
127
89
  );
128
90
 
129
91
  cp.on("error", function (e) {
@@ -139,8 +101,7 @@ function phantomjsWorker (input, options, notifier, qcb) {
139
101
 
140
102
  // start counting
141
103
  notifier.add(input.outputFile, input.timeout);
142
- }
143
- else {
104
+ } else {
144
105
  // The input.outputFile is being or has been processed this run.
145
106
  qcb(0);
146
107
  }
@@ -153,12 +114,14 @@ function phantomjsWorker (input, options, notifier, qcb) {
153
114
  * @param {String} input.selector - The selector to wait for.
154
115
  * @param {String} input.timeout - The entire process timeout.
155
116
  * @param {Object} input.debug - The debug object.
156
- * @param {Object} input.puppeteerLaunchOptions - overriding puppeteer launch options.
117
+ * @param {Object} input.playwrightLaunchOptions - overriding playwright launch options.
118
+ * May include a `browserType` key ("chromium" | "firefox" | "webkit") to select
119
+ * the browser engine. Defaults to "chromium".
157
120
  * @param {Object} options - Input options.
158
121
  * @param {Object} options.snapshotScript - Snapshot script options.
159
122
  * @param {String} options.snapshotScript.script - Snapshot script name.
160
123
  * @param {String} options.snapshotScript.module - Snapshot script module.
161
- * @param {String} options.puppeteer - Full path to the puppeteer exec.
124
+ * @param {String} options.playwright - Full path to the playwright exec.
162
125
  * @param {Object} notifier - The async Notifier instance for this run.
163
126
  * @param {Function} notifier.remove - Remove an outputFile from notifier.
164
127
  * @param {Function} notifier.setError - Set an error on the notifier.
@@ -166,7 +129,7 @@ function phantomjsWorker (input, options, notifier, qcb) {
166
129
  * @param {Function} notifier.known - Check if a file is or has been processed.
167
130
  * @param {Function} qcb - async.queue callback function.
168
131
  */
169
- function puppeteerWorker (input, options, notifier, qcb) {
132
+ function playwrightWorker (input, options, notifier, qcb) {
170
133
  let filter = "false";
171
134
  let snapshotScript = options.snapshotScript;
172
135
 
@@ -175,7 +138,7 @@ function puppeteerWorker (input, options, notifier, qcb) {
175
138
 
176
139
  // map snapshotScript object script
177
140
  if (common.isObject(options.snapshotScript)) {
178
- snapshotScript = options.puppeteer;
141
+ snapshotScript = options.playwright;
179
142
  if (options.snapshotScript.script === "removeScripts") {
180
143
  filter = "./removeScripts";
181
144
  } else {
@@ -192,7 +155,7 @@ function puppeteerWorker (input, options, notifier, qcb) {
192
155
  filter,
193
156
  input.debug.flag,
194
157
  input.debug.slowMo,
195
- JSON.stringify(input.puppeteerLaunchOptions)
158
+ JSON.stringify(input.playwrightLaunchOptions)
196
159
  ], { cwd: process.cwd(), stdio: "inherit", detached: true }
197
160
  );
198
161
 
@@ -230,10 +193,10 @@ function prepOptions (options) {
230
193
  options.input = "array";
231
194
  }
232
195
 
233
- // If phantomjs browser, make sure the default snapshotScript is correctly set.
234
- if (options.browser === 'phantomjs') {
196
+ // If playwright browser, make sure the default snapshotScript is correctly set.
197
+ if (options.browser === "playwright") {
235
198
  if (!options.snapshotScript || options.snapshotScript === options.puppeteer) {
236
- options.snapshotScript = options.phantomSnapshotScript;
199
+ options.snapshotScript = options.playwright;
237
200
  }
238
201
  }
239
202
  }
@@ -306,7 +269,7 @@ function run (options, listener) {
306
269
  options._abort = notifier.abort.bind(notifier, q);
307
270
 
308
271
  const worker =
309
- options.browser === 'puppeteer' ? puppeteerWorker : phantomjsWorker;
272
+ options.browser === "playwright" ? playwrightWorker : puppeteerWorker;
310
273
 
311
274
  // generate input for the snapshots.
312
275
  return inputGenerator.run(options, input => {