@percy/core 1.0.0-beta.8 → 1.0.1
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/README.md +226 -67
- package/dist/api.js +94 -0
- package/dist/browser.js +292 -0
- package/dist/config.js +512 -30
- package/dist/discovery.js +118 -0
- package/dist/index.js +5 -29
- package/dist/install.js +156 -0
- package/dist/network.js +298 -0
- package/dist/page.js +264 -0
- package/dist/percy.js +373 -306
- package/dist/queue.js +122 -73
- package/dist/server.js +424 -76
- package/dist/session.js +103 -0
- package/dist/snapshot.js +433 -0
- package/dist/utils.js +127 -0
- package/package.json +42 -28
- package/post-install.js +20 -0
- package/test/helpers/server.js +33 -0
- package/types/index.d.ts +69 -39
- package/dist/discoverer.js +0 -367
- package/dist/percy-css.js +0 -33
- package/dist/utils/assert.js +0 -50
- package/dist/utils/bytes.js +0 -24
- package/dist/utils/idle.js +0 -15
- package/dist/utils/install-browser.js +0 -76
- package/dist/utils/resources.js +0 -75
- package/dist/utils/url.js +0 -64
package/README.md
CHANGED
|
@@ -2,35 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
The core component of Percy's CLI and SDKs that handles creating builds, discovering snapshot
|
|
4
4
|
assets, uploading snapshots, and finalizing builds. Uses `@percy/client` for API communication, a
|
|
5
|
-
|
|
5
|
+
Chromium browser for asset discovery, and starts a local API server for posting snapshots from
|
|
6
6
|
other processes.
|
|
7
7
|
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [`#start()`](#start)
|
|
10
|
+
- [`#stop()`](#stopforce)
|
|
11
|
+
- [`#idle()`](#idle)
|
|
12
|
+
- [`#snapshot()`](#snapshotoptions)
|
|
13
|
+
- [Advanced](#advanced)
|
|
14
|
+
- [Download discovery browser on install](#download-discovery-browser-on-install)
|
|
15
|
+
- [Skipping discovery browser download](#skipping-discovery-browser-download)
|
|
16
|
+
|
|
8
17
|
## Usage
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
A `Percy` class instance can manage a Percy build, take page snapshots, and perform snapshot asset
|
|
20
|
+
discovery. It also hosts a local API server for Percy SDKs to communicate with.
|
|
12
21
|
|
|
13
22
|
``` js
|
|
14
23
|
import Percy from '@percy/core'
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
concurrency: 5, // concurrency of the #capture() method
|
|
22
|
-
snapshot: {}, // global snapshot options (see snapshots section)
|
|
23
|
-
discovery: { // asset discovery options
|
|
24
|
-
allowedHostnames: [], // list of hostnames allowed to capture from
|
|
25
|
-
networkIdleTimeout: 100, // how long before network is considered idle
|
|
26
|
-
disableAssetCache: false, // disable discovered asset caching
|
|
27
|
-
concurrency: 5, // asset discovery concurrency
|
|
28
|
-
launchOptions: {} // browser launch options
|
|
29
|
-
},
|
|
30
|
-
...config // additional config options accessible by SDKs
|
|
31
|
-
})
|
|
25
|
+
// create a new instance
|
|
26
|
+
const percy = new Percy(percyOptions)
|
|
27
|
+
|
|
28
|
+
// create a new instance and start it
|
|
29
|
+
const percy = await Percy.start(percyOptions)
|
|
32
30
|
```
|
|
33
31
|
|
|
32
|
+
#### Options
|
|
33
|
+
|
|
34
|
+
- `token` — Your project's `PERCY_TOKEN` (**default** `process.env.PERCY_TOKEN`)
|
|
35
|
+
- `loglevel` — Logger level, one of `"info"`, `"warn"`, `"error"`, `"debug"` (**default** `"info"`)
|
|
36
|
+
- `server` — Controls whether an API server is created (**default** `true`)
|
|
37
|
+
- `port` — API server port (**default** `5338`)
|
|
38
|
+
- `clientInfo` — Client info sent to Percy via a user-agent string
|
|
39
|
+
- `environmentInfo` — Environment info also sent with the user-agent string
|
|
40
|
+
- `deferUploads` — Defer creating a build and uploading snapshots until later
|
|
41
|
+
- `skipUploads` — Skip creating a build and uploading snapshots altogether
|
|
42
|
+
|
|
43
|
+
The following options can also be defined within a Percy config file
|
|
44
|
+
|
|
45
|
+
- `snapshot` — Snapshot options applied to each snapshot
|
|
46
|
+
- `widths` — Widths to take screenshots at (**default** `[375, 1280]`)
|
|
47
|
+
- `minHeight` — Minimum screenshot height (**default** `1024`)
|
|
48
|
+
- `percyCSS` — Percy specific CSS to inject into the snapshot
|
|
49
|
+
- `enableJavaScript` — Enable JavaScript for screenshots (**default** `false`)
|
|
50
|
+
- `discovery` — Asset discovery options
|
|
51
|
+
- `allowedHostnames` — Array of allowed hostnames to capture assets from
|
|
52
|
+
- `disallowedHostnames` — Array of hostnames where requests will be aborted
|
|
53
|
+
- `requestHeaders` — Request headers used when discovering snapshot assets
|
|
54
|
+
- `authorization` — Basic auth `username` and `password` for protected snapshot assets
|
|
55
|
+
- `disableCache` — Disable asset caching (**default** `false`)
|
|
56
|
+
- `userAgent` — Custom user-agent string used when requesting assets
|
|
57
|
+
- `cookies` — Browser cookies to use when requesting assets
|
|
58
|
+
- `networkIdleTimeout` — Milliseconds to wait for the network to idle (**default** `100`)
|
|
59
|
+
- `concurrency` — Asset discovery concerrency (**default** `5`)
|
|
60
|
+
- `launchOptions` — Asset discovery browser launch options
|
|
61
|
+
- `executable` — Browser executable path (**default** `process.env.PERCY_BROWSER_EXECUTABLE`)
|
|
62
|
+
- `timeout` — Discovery launch timeout, in milliseconds (**default** `30000`)
|
|
63
|
+
- `args` — Additional browser process arguments
|
|
64
|
+
- `headless` — Runs the browser headlessy (**default** `true`)
|
|
65
|
+
|
|
66
|
+
Additional Percy config file options are also allowed and will override any options defined by a
|
|
67
|
+
local config file. These config file options are also made available to SDKs via the local API
|
|
68
|
+
health check endpoint.
|
|
69
|
+
|
|
34
70
|
### `#start()`
|
|
35
71
|
|
|
36
72
|
Starting a `Percy` instance will start a local API server, start the asset discovery browser, and
|
|
@@ -39,81 +75,204 @@ create a new Percy build. If an asset discovery browser is not found, one will b
|
|
|
39
75
|
``` js
|
|
40
76
|
await percy.start()
|
|
41
77
|
// [percy] Percy has started!
|
|
42
|
-
// [percy] Created build #1: https://percy.io/org/project/123
|
|
43
78
|
```
|
|
44
79
|
|
|
45
80
|
#### API Server
|
|
46
81
|
|
|
47
82
|
Starting a `Percy` instance will start a local API server unless `server` is `false`. The server can
|
|
48
|
-
be found at `http://localhost:5338/` or at the provided `port` number.
|
|
49
|
-
JSON body with the `application/json` content-type.
|
|
83
|
+
be found at `http://localhost:5338/` or at the provided `port` number.
|
|
50
84
|
|
|
51
85
|
- GET `/percy/healthcheck` – Responds with information about the running instance
|
|
52
86
|
- GET `/percy/dom.js` – Responds with the [`@percy/dom`](./packages/dom) library
|
|
87
|
+
- GET `/percy/idle` - Responds when the running instance is [idle](#idle)
|
|
53
88
|
- POST `/percy/snapshot` – Calls [`#snapshot()`](#snapshotoptions) with provided snapshot options
|
|
54
|
-
- POST `/percy/stop` - Remotely [stops](#
|
|
89
|
+
- POST `/percy/stop` - Remotely [stops](#stopforce) the running `Percy` instance
|
|
90
|
+
|
|
91
|
+
### `#stop([force])`
|
|
92
|
+
|
|
93
|
+
Stopping a `Percy` instance will wait for any pending snapshots, close the asset discovery browser,
|
|
94
|
+
close the local API server, and finalize the current Percy build. When uploads are deferred,
|
|
95
|
+
stopping the instance will also trigger processing of the upload queue. When `force` is `true`,
|
|
96
|
+
queues are cleared and closed to prevent queued snapshots from running.
|
|
97
|
+
|
|
98
|
+
``` js
|
|
99
|
+
await percy.stop()
|
|
100
|
+
// [percy] Processing 3 snapshots...
|
|
101
|
+
// [percy] Snapshot taken: Snapshot one
|
|
102
|
+
// [percy] Snapshot taken: Snapshot two
|
|
103
|
+
// [percy] Snapshot taken: Snapshot three
|
|
104
|
+
// [percy] Uploading 3 snapshots...
|
|
105
|
+
// [percy] Finalized build #1: https://percy.io/org/project/123
|
|
106
|
+
|
|
107
|
+
await percy.stop(true)
|
|
108
|
+
// [percy] Stopping percy...
|
|
109
|
+
// [percy] Finalized build #1: https://percy.io/org/project/123
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `#idle()`
|
|
113
|
+
|
|
114
|
+
This method will resolve shortly after pending snapshots and uploads have completed and no more have
|
|
115
|
+
started. Queued tasks are not considered pending unless they are actively running, so deferred
|
|
116
|
+
uploads will not be awaited on with this method.
|
|
117
|
+
|
|
118
|
+
``` js
|
|
119
|
+
percy.snapshot(...);
|
|
120
|
+
percy.snapshot(...);
|
|
121
|
+
percy.snapshot(...);
|
|
122
|
+
|
|
123
|
+
await percy.idle()
|
|
124
|
+
// [percy] Snapshot taken: ...
|
|
125
|
+
// [percy] Snapshot taken: ...
|
|
126
|
+
// [percy] Snapshot taken: ...
|
|
127
|
+
```
|
|
55
128
|
|
|
56
129
|
### `#snapshot(options)`
|
|
57
130
|
|
|
58
|
-
|
|
59
|
-
|
|
131
|
+
Takes one or more snapshots of a page while discovering resources to upload with the snapshot. Once
|
|
132
|
+
asset discovery has completed, the queued snapshot will resolve and an upload task will be queued
|
|
133
|
+
separately. Accepts several different syntaxes for taking snapshots in various ways.
|
|
134
|
+
|
|
135
|
+
All available syntaxes will push snapshots into the snapshot queue without the need to await on the
|
|
136
|
+
method directly. This method resolves after the snapshot upload is queued, but does not await on the
|
|
137
|
+
upload to complete.
|
|
60
138
|
|
|
61
139
|
``` js
|
|
62
140
|
// snapshots can be handled concurrently, no need to await
|
|
63
141
|
percy.snapshot({
|
|
64
|
-
name: '
|
|
65
|
-
url: 'http://localhost:3000',
|
|
66
|
-
domSnapshot: domSnapshot,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
142
|
+
name: 'Snapshot 1',
|
|
143
|
+
url: 'http://localhost:3000',
|
|
144
|
+
domSnapshot: domSnapshot,
|
|
145
|
+
clientInfo: 'my-sdk',
|
|
146
|
+
environmentInfo: 'my-lib'
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// without a domSnapshot, capture options will be used to take one
|
|
150
|
+
percy.snapshot({
|
|
151
|
+
name: 'Snapshot 2',
|
|
152
|
+
url: 'http://localhost:3000',
|
|
153
|
+
waitForTimeout: 1000,
|
|
154
|
+
waitForSelector: '.done-loading',
|
|
155
|
+
execute: async () => {},
|
|
156
|
+
additionalSnapshots: [{
|
|
157
|
+
name: 'Snapshot 2.1',
|
|
158
|
+
execute: () => {}
|
|
159
|
+
}]
|
|
73
160
|
})
|
|
74
|
-
```
|
|
75
161
|
|
|
76
|
-
|
|
162
|
+
// alternate shorthand syntax
|
|
163
|
+
percy.snapshot({
|
|
164
|
+
baseUrl: 'http://localhost:3000',
|
|
165
|
+
snapshots: ['/', '/about', '/contact'],
|
|
166
|
+
options: {
|
|
167
|
+
widths: [600, 1200]
|
|
168
|
+
}
|
|
169
|
+
})
|
|
77
170
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
171
|
+
// gather snapshots from an external sitemap
|
|
172
|
+
percy.snapshot({
|
|
173
|
+
sitemap: 'https://example.com/sitemap.xml',
|
|
174
|
+
exclude: ['/blog/*']
|
|
175
|
+
})
|
|
81
176
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
url: 'http://localhost:3000/', // required page URL
|
|
87
|
-
waitFor: selectorOrTimeout, // selector or timeout to wait for before snapshotting
|
|
88
|
-
execute: async (page) => {}, // async page function to execute before snapshotting
|
|
89
|
-
snapshots: [{ // additional snapshots to take on this page
|
|
90
|
-
name: 'Second Snapshot', // additional snapshot name
|
|
91
|
-
execute: async (page) => {}, // additional snapshot page function
|
|
92
|
-
...options // ...additional snapshot options
|
|
93
|
-
}],
|
|
94
|
-
...options // ...other snapshot options
|
|
177
|
+
// start a server and take static snapshots
|
|
178
|
+
percy.snapshot({
|
|
179
|
+
serve: './public',
|
|
180
|
+
cleanUrls: true,
|
|
95
181
|
})
|
|
96
182
|
```
|
|
97
183
|
|
|
98
|
-
|
|
184
|
+
#### Options
|
|
99
185
|
|
|
100
|
-
|
|
101
|
-
|
|
186
|
+
When capturing a single snapshot, the snapshot URL may be provided as the only argument rather than
|
|
187
|
+
a snapshot options object. When providing an options object, a few alternate syntaxes are available
|
|
188
|
+
depending on the provided properties ([see alternate syntaxes below](#alternate-syntaxes)).
|
|
102
189
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
190
|
+
**Common options** accepted for each snapshot:
|
|
191
|
+
|
|
192
|
+
- `url` — Snapshot URL (**required**)
|
|
193
|
+
- `name` — Snapshot name
|
|
194
|
+
- `domSnapshot` — Snapshot DOM string
|
|
195
|
+
- `discovery` - Limited snapshot specific discovery options
|
|
196
|
+
- `allowedHostnames`, `disallowedHostnames`, `requestHeaders`, `authorization`, `disableCache`, `userAgent`
|
|
197
|
+
|
|
198
|
+
Common snapshot options are also accepted and will override instance snapshot options. [See instance
|
|
199
|
+
options](#options) for common snapshot and discovery options.
|
|
200
|
+
|
|
201
|
+
**Capture options** can only be provided when `domSnapshot` is missing:
|
|
202
|
+
|
|
203
|
+
- `waitForTimeout` — Milliseconds to wait before taking a snapshot
|
|
204
|
+
- `waitForSelector` — CSS selector to wait for before taking a snapshot
|
|
205
|
+
- `execute` — Function or function body to execute within the page before taking a snapshot
|
|
206
|
+
- `additionalSnapshots` — Array of additional sequential snapshots to take of the page
|
|
207
|
+
- `name` — Snapshot name (**required** if no `prefix` or `suffix`)
|
|
208
|
+
- `prefix` — Snapshot name prefix (**required** if no `name` or `suffix`)
|
|
209
|
+
- `suffix` — Snapshot name suffix (**required** if no `name` or `prefix`)
|
|
210
|
+
- `waitForTimeout`, `waitForSelector`, `execute` — See above
|
|
211
|
+
|
|
212
|
+
#### Alternate syntaxes
|
|
213
|
+
|
|
214
|
+
All snapshot syntaxes can be provided as items within an array. For example, a single method call
|
|
215
|
+
can upload multiple DOM snapshots, capture multiple external snapshots, crawl a sitemap for
|
|
216
|
+
snapshots, and host a local static server for snapshots.
|
|
217
|
+
|
|
218
|
+
**Shared options** accepted by all syntaxes:
|
|
219
|
+
|
|
220
|
+
- `clientInfo` — Client info to include with the build
|
|
221
|
+
- `environmentInfo` — Environment info to include with the build
|
|
222
|
+
|
|
223
|
+
The following alternate syntaxes may **not** be combined with snapshot options, but rather offer
|
|
224
|
+
alternate methods for taking multiple snapshots.
|
|
225
|
+
|
|
226
|
+
**List options** can only be provided when a top-level `snapshots` is present:
|
|
227
|
+
|
|
228
|
+
- `snapshots` — An array of snapshot URLs or snapshot options (**required**)
|
|
229
|
+
- `baseUrl` — The full base URL (including protocol) used when snapshot URLs only include a pathname
|
|
230
|
+
- `include`/`exclude` — Include and exclude matching snapshot names
|
|
231
|
+
- `options` — Additional options to apply to snapshots
|
|
232
|
+
- `include`/`exclude` — Include and exclude snapshots to apply these options to
|
|
233
|
+
- [Common snapshot and capture options](#options) (**excluding** `url`, `domSnapshot`)
|
|
234
|
+
|
|
235
|
+
**Sitemap options** can only be provided when a top-level `sitemap` is present:
|
|
236
|
+
|
|
237
|
+
- `sitemap` — The URL where an XML sitemap can be located (**required**)
|
|
238
|
+
- `include`/`exclude` — Include and exclude matching snapshot names
|
|
239
|
+
- `options` — Additional options to apply to snapshots
|
|
240
|
+
- `include`/`exclude` — Include and exclude snapshots to apply these options to
|
|
241
|
+
- [Common snapshot and capture options](#options) (**excluding** `url`, `domSnapshot`)
|
|
242
|
+
|
|
243
|
+
**Server options** can only be provided when a top-level `serve` is present:
|
|
244
|
+
|
|
245
|
+
- `serve` — The static directory to serve relative to the current working directory (**required**)
|
|
246
|
+
- `baseUrl` — The base URL to serve the directory at, starting with a forward slash (/)
|
|
247
|
+
- `cleanUrls` — Set to `true` to strip `.html` and `index.html` from served URLs
|
|
248
|
+
- `rewrites` — A source-destination map for rewriting source URLs into destination pathnames
|
|
249
|
+
- `snapshots` — An array of specific snapshots to take while serving the static directory
|
|
250
|
+
- `include`/`exclude` — Include and exclude matching snapshot names
|
|
251
|
+
- `options` — Additional options to apply to snapshots
|
|
252
|
+
- `include`/`exclude` — Include and exclude snapshots to apply these options to
|
|
253
|
+
- [Common snapshot and capture options](#options) (**excluding** `url`, `domSnapshot`)
|
|
111
254
|
|
|
112
255
|
## Advanced
|
|
113
256
|
|
|
114
|
-
###
|
|
257
|
+
### Download discovery browser on install
|
|
258
|
+
|
|
259
|
+
By default, the browser is only downloaded when asset discovery is started for the first time. This
|
|
260
|
+
is because many features of the CLI do not require a browser at all, and automatically downloading a
|
|
261
|
+
browser creates a much heavier footprint than needed for those features. However, if your CI caches
|
|
262
|
+
dependencies after the install step, the browser will not be cached and will be downloaded every
|
|
263
|
+
time Percy runs without it.
|
|
264
|
+
|
|
265
|
+
If the environment variable `PERCY_POSTINSTALL_BROWSER` is present and truthy, then the browser will
|
|
266
|
+
be downloaded after the package is installed to allow it to be cached. You can also require
|
|
267
|
+
`@percy/core/post-install` within another node module to trigger the browser download manually.
|
|
268
|
+
|
|
269
|
+
### Skipping discovery browser download
|
|
115
270
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
271
|
+
If your CI comes with a Chromium binary pre-installed and you wish to skip Percy's own browser
|
|
272
|
+
installation, you can set the respective `discovery.launchOptions.executable` config option. When
|
|
273
|
+
the executable at the provided path exists, the default download will be skipped and the provided
|
|
274
|
+
binary will be used instead. This option can also be set using the `PERCY_BROWSER_EXECUTABLE`
|
|
275
|
+
environment variable.
|
|
119
276
|
|
|
277
|
+
> **Warning!** Percy is only tested against the browser it downloads automatically. When providing a
|
|
278
|
+
> custom browser executable, you may experience unexpected issues.
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import logger from '@percy/logger';
|
|
5
|
+
import { getPackageJSON } from './utils.js';
|
|
6
|
+
import Server from './server.js'; // need require.resolve until import.meta.resolve can be transpiled
|
|
7
|
+
|
|
8
|
+
export const PERCY_DOM = createRequire(import.meta.url).resolve('@percy/dom'); // Create a Percy CLI API server instance
|
|
9
|
+
|
|
10
|
+
export function createPercyServer(percy, port) {
|
|
11
|
+
let pkg = getPackageJSON(import.meta.url);
|
|
12
|
+
return new Server({
|
|
13
|
+
port
|
|
14
|
+
}) // facilitate logger websocket connections
|
|
15
|
+
.websocket(ws => logger.connect(ws)) // general middleware
|
|
16
|
+
.route((req, res, next) => {
|
|
17
|
+
// treat all request bodies as json
|
|
18
|
+
if (req.body) try {
|
|
19
|
+
req.body = JSON.parse(req.body);
|
|
20
|
+
} catch {} // add version header
|
|
21
|
+
|
|
22
|
+
res.setHeader('Access-Control-Expose-Headers', '*, X-Percy-Core-Version');
|
|
23
|
+
res.setHeader('X-Percy-Core-Version', pkg.version); // return json errors
|
|
24
|
+
|
|
25
|
+
return next().catch(e => res.json(e.status ?? 500, {
|
|
26
|
+
build: percy.build,
|
|
27
|
+
error: e.message,
|
|
28
|
+
success: false
|
|
29
|
+
}));
|
|
30
|
+
}) // healthcheck returns basic information
|
|
31
|
+
.route('get', '/percy/healthcheck', (req, res) => res.json(200, {
|
|
32
|
+
loglevel: percy.loglevel(),
|
|
33
|
+
config: percy.config,
|
|
34
|
+
build: percy.build,
|
|
35
|
+
success: true
|
|
36
|
+
})) // get or set config options
|
|
37
|
+
.route(['get', 'post'], '/percy/config', async (req, res) => res.json(200, {
|
|
38
|
+
config: req.body ? await percy.setConfig(req.body) : percy.config,
|
|
39
|
+
success: true
|
|
40
|
+
})) // responds once idle (may take a long time)
|
|
41
|
+
.route('get', '/percy/idle', async (req, res) => res.json(200, {
|
|
42
|
+
success: await percy.idle().then(() => true)
|
|
43
|
+
})) // convenient @percy/dom bundle
|
|
44
|
+
.route('get', '/percy/dom.js', (req, res) => {
|
|
45
|
+
return res.file(200, PERCY_DOM);
|
|
46
|
+
}) // legacy agent wrapper for @percy/dom
|
|
47
|
+
.route('get', '/percy-agent.js', async (req, res) => {
|
|
48
|
+
logger('core:server').deprecated(['It looks like you’re using @percy/cli with an older SDK.', 'Please upgrade to the latest version to fix this warning.', 'See these docs for more info: https:docs.percy.io/docs/migrating-to-percy-cli'].join(' '));
|
|
49
|
+
let content = await fs.promises.readFile(PERCY_DOM, 'utf-8');
|
|
50
|
+
let wrapper = '(window.PercyAgent = class { snapshot(n, o) { return PercyDOM.serialize(o); } });';
|
|
51
|
+
return res.send(200, 'applicaton/javascript', content.concat(wrapper));
|
|
52
|
+
}) // post one or more snapshots
|
|
53
|
+
.route('post', '/percy/snapshot', async (req, res) => {
|
|
54
|
+
let snapshot = percy.snapshot(req.body);
|
|
55
|
+
if (!req.url.searchParams.has('async')) await snapshot;
|
|
56
|
+
return res.json(200, {
|
|
57
|
+
success: true
|
|
58
|
+
});
|
|
59
|
+
}) // stops percy at the end of the current event loop
|
|
60
|
+
.route('/percy/stop', (req, res) => {
|
|
61
|
+
setImmediate(() => percy.stop());
|
|
62
|
+
return res.json(200, {
|
|
63
|
+
success: true
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
} // Create a static server instance with an automatic sitemap
|
|
67
|
+
|
|
68
|
+
export function createStaticServer(options) {
|
|
69
|
+
let {
|
|
70
|
+
serve,
|
|
71
|
+
port,
|
|
72
|
+
baseUrl = '/',
|
|
73
|
+
...opts
|
|
74
|
+
} = options;
|
|
75
|
+
let server = new Server({
|
|
76
|
+
port
|
|
77
|
+
}).serve(baseUrl, serve, opts); // used when generating an automatic sitemap
|
|
78
|
+
|
|
79
|
+
let toURL = Server.createRewriter( // reverse rewrites' src, dest, & order
|
|
80
|
+
Object.entries((options === null || options === void 0 ? void 0 : options.rewrites) ?? {}).reduce((acc, rw) => [rw.reverse(), ...acc], []), (filename, rewrite) => new URL(path.posix.join(baseUrl, // cleanUrls will trim trailing .html/index.html from paths
|
|
81
|
+
!options.cleanUrls ? rewrite(filename) : rewrite(filename).replace(/(\/index)?\.html$/, '')), server.address())); // include automatic sitemap route
|
|
82
|
+
|
|
83
|
+
server.route('get', '/sitemap.xml', async (req, res) => {
|
|
84
|
+
let {
|
|
85
|
+
default: glob
|
|
86
|
+
} = await import('fast-glob');
|
|
87
|
+
let files = await glob('**/*.html', {
|
|
88
|
+
cwd: serve,
|
|
89
|
+
fs
|
|
90
|
+
});
|
|
91
|
+
return res.send(200, 'application/xml', ['<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">', ...files.map(name => ` <url><loc>${toURL(name)}</loc></url>`), '</urlset>'].join('\n'));
|
|
92
|
+
});
|
|
93
|
+
return server;
|
|
94
|
+
}
|