@percy/cli-snapshot 1.0.0-beta.71 → 1.0.0-beta.72

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 CHANGED
@@ -4,39 +4,46 @@ Snapshot a list or static directory of web pages.
4
4
 
5
5
  ## Commands
6
6
  <!-- commands -->
7
- * [`percy snapshot DIR|FILE|SITEMAP`](#percy-snapshot-dirfilesitemap)
7
+ * [`percy snapshot`](#percy-snapshot)
8
8
 
9
- ## `percy snapshot DIR|FILE|SITEMAP`
9
+ ### `percy snapshot`
10
10
 
11
- Take snapshots from a static directory, snapshots file, or sitemap url
11
+ Snapshot a static directory, snapshots file, or sitemap URL
12
12
 
13
13
  ```
14
- USAGE
15
- $ percy snapshot DIR|FILE|SITEMAP
16
-
17
- ARGUMENTS
18
- DIR|FILE|SITEMAP static directory, snapshots file, or sitemap url
19
-
20
- OPTIONS
21
- -b, --base-url=base-url the base url pages are hosted at when snapshotting
22
- -c, --config=config configuration file path
23
- -d, --dry-run print logs only, do not run asset discovery or upload snapshots
24
- -h, --allowed-hostname=allowed-hostname allowed hostnames to capture in asset discovery
25
- -q, --quiet log errors only
26
- -t, --network-idle-timeout=network-idle-timeout asset discovery network idle timeout
27
- -v, --verbose log everything
28
- --clean-urls rewrite static index and filepath URLs to be clean
29
- --debug debug asset discovery and do not upload snapshots
30
- --disable-cache disable asset discovery caches
31
- --exclude=exclude one or more globs/patterns matching snapshots to exclude
32
- --include=include one or more globs/patterns matching snapshots to include
33
- --silent log nothing
34
-
35
- EXAMPLES
14
+ Usage:
15
+ $ percy snapshot [options] <dir|file|sitemap>
16
+
17
+ Arguments:
18
+ dir|file|sitemap Static directory, snapshots file, or sitemap url
19
+
20
+ Options:
21
+ -b, --base-url <string> The base url pages are hosted at when snapshotting
22
+ --include <pattern> One or more globs/patterns matching snapshots to include
23
+ --exclude <pattern> One or more globs/patterns matching snapshots to exclude
24
+
25
+ Static options:
26
+ --clean-urls Rewrite static index and filepath URLs to be clean
27
+
28
+ Percy options:
29
+ -c, --config <file> Config file path
30
+ -d, --dry-run Print snapshot names only
31
+ -h, --allowed-hostname <hostname> Allowed hostnames to capture in asset discovery
32
+ -t, --network-idle-timeout <ms> Asset discovery network idle timeout
33
+ --disable-cache Disable asset discovery caches
34
+ --debug Debug asset discovery and do not upload snapshots
35
+
36
+ Global options:
37
+ -v, --verbose Log everything
38
+ -q, --quiet Log errors only
39
+ -s, --silent Log nothing
40
+ --help Display command help
41
+
42
+ Examples:
36
43
  $ percy snapshot ./public
37
44
  $ percy snapshot snapshots.yml
45
+ $ percy snapshot https://percy.io/sitemap.xml
38
46
  ```
39
-
40
47
  <!-- commandsstop -->
41
48
 
42
49
  ## Usage
package/dist/config.js CHANGED
@@ -3,14 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.configSchema = exports.cliSchema = void 0;
7
- exports.migration = migration;
8
- exports.snapshotListSchema = exports.schemas = void 0;
6
+ exports.commonSchema = void 0;
7
+ exports.configMigration = configMigration;
8
+ exports.snapshotsFileSchema = exports.configSchema = void 0;
9
9
 
10
10
  var _config = require("@percy/core/dist/config");
11
11
 
12
12
  // Common schemas referenced by other schemas
13
- const cliSchema = {
13
+ const commonSchema = {
14
14
  $id: '/snapshot/cli',
15
15
  $refs: {
16
16
  predicate: {
@@ -30,7 +30,7 @@ const cliSchema = {
30
30
  }
31
31
  }; // Config schema for static directories
32
32
 
33
- exports.cliSchema = cliSchema;
33
+ exports.commonSchema = commonSchema;
34
34
  const configSchema = {
35
35
  static: {
36
36
  type: 'object',
@@ -94,11 +94,11 @@ const configSchema = {
94
94
  }
95
95
  }
96
96
  }
97
- }; // Page listing schema
97
+ }; // Snapshots file schema
98
98
 
99
99
  exports.configSchema = configSchema;
100
- const snapshotListSchema = {
101
- $id: '/snapshot/list',
100
+ const snapshotsFileSchema = {
101
+ $id: '/snapshot/file',
102
102
  oneOf: [{
103
103
  type: 'array',
104
104
  items: {
@@ -131,11 +131,9 @@ const snapshotListSchema = {
131
131
  }
132
132
  }]
133
133
  };
134
- exports.snapshotListSchema = snapshotListSchema;
135
- const schemas = [cliSchema, configSchema, snapshotListSchema];
136
- exports.schemas = schemas;
134
+ exports.snapshotsFileSchema = snapshotsFileSchema;
137
135
 
138
- function migration(config, util) {
136
+ function configMigration(config, util) {
139
137
  /* eslint-disable curly */
140
138
  if (config.version < 2) {
141
139
  // static-snapshots and options were renamed
package/dist/file.js ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadSnapshotsFile = loadSnapshotsFile;
7
+
8
+ var _fs = _interopRequireDefault(require("fs"));
9
+
10
+ var _path = _interopRequireDefault(require("path"));
11
+
12
+ var _config = _interopRequireDefault(require("@percy/config"));
13
+
14
+ var _utils = require("./utils");
15
+
16
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
+
18
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
19
+
20
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
21
+
22
+ // Loads snapshots from a js, json, or yaml file.
23
+ async function loadSnapshotsFile(file, flags, invalid) {
24
+ var _PercyConfig$validate;
25
+
26
+ let ext = _path.default.extname(file);
27
+
28
+ let config = {}; // load snapshots file
29
+
30
+ if (ext === '.js') {
31
+ ({
32
+ default: config
33
+ } = await Promise.resolve(`${_path.default.resolve(file)}`).then(s => _interopRequireWildcard(require(s))));
34
+ if (typeof config === 'function') config = await config();
35
+ } else if (ext === '.json') {
36
+ config = JSON.parse(_fs.default.readFileSync(file, {
37
+ encoding: 'utf-8'
38
+ }));
39
+ } else if (ext.match(/\.ya?ml$/)) {
40
+ let YAML = await Promise.resolve().then(() => _interopRequireWildcard(require('yaml')));
41
+ config = YAML.parse(_fs.default.readFileSync(file, {
42
+ encoding: 'utf-8'
43
+ }));
44
+ } else {
45
+ throw new Error(`Unsupported filetype: ${file}`);
46
+ } // validate base-url before config options
47
+
48
+
49
+ if (flags.baseUrl && !flags.baseUrl.startsWith('http')) {
50
+ throw new Error('The base-url must include a protocol ' + 'and hostname when providing a list of snapshots');
51
+ } // validate snapshot config options
52
+
53
+
54
+ (_PercyConfig$validate = _config.default.validate(config, '/snapshot/file')) === null || _PercyConfig$validate === void 0 ? void 0 : _PercyConfig$validate.forEach(invalid); // flags override config options
55
+
56
+ let {
57
+ baseUrl,
58
+ include,
59
+ exclude
60
+ } = { ...config,
61
+ ...flags
62
+ }; // support config that only contains a list of snapshots
63
+
64
+ return (Array.isArray(config) ? config : config.snapshots || []).reduce((snapshots, snap) => {
65
+ // reduce matching snapshots with default options
66
+ snap = (0, _utils.withDefaults)(snap, {
67
+ host: baseUrl
68
+ });
69
+ return (0, _utils.snapshotMatches)(snap, include, exclude) ? snapshots.concat(snap) : snapshots;
70
+ }, []);
71
+ }
package/dist/index.js CHANGED
@@ -1,3 +1,13 @@
1
1
  "use strict";
2
2
 
3
- module.exports = require('./commands/snapshot').Snapshot;
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "snapshot", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _snapshot.snapshot;
10
+ }
11
+ });
12
+
13
+ var _snapshot = require("./snapshot");
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadSitemapSnapshots = loadSitemapSnapshots;
7
+
8
+ var _static = require("./static");
9
+
10
+ var _utils = require("@percy/core/dist/utils");
11
+
12
+ // Fetches and maps sitemap URLs to snapshots.
13
+ async function loadSitemapSnapshots(sitemapUrl, config) {
14
+ // fetch sitemap URLs
15
+ let urls = await (0, _utils.request)(sitemapUrl, (body, res) => {
16
+ // validate sitemap content-type
17
+ let [contentType] = res.headers['content-type'].split(';');
18
+
19
+ if (!/^(application|text)\/xml$/.test(contentType)) {
20
+ throw new Error('The sitemap must be an XML document, ' + `but the content-type was "${contentType}"`);
21
+ } // parse XML content into a list of URLs
22
+
23
+
24
+ let urls = body.match(/(?<=<loc>)(.*)(?=<\/loc>)/ig); // filter out duplicate URLs that differ by a trailing slash
25
+
26
+ return urls.filter((url, i) => {
27
+ let match = urls.indexOf(url.replace(/\/$/, ''));
28
+ return match === -1 || match === i;
29
+ });
30
+ }); // map with inherited static options
31
+
32
+ return (0, _static.mapStaticSnapshots)(urls, config);
33
+ }
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.snapshot = exports.default = void 0;
7
+
8
+ var _fs = require("fs");
9
+
10
+ var _cliCommand = _interopRequireDefault(require("@percy/cli-command"));
11
+
12
+ var SnapshotConfig = _interopRequireWildcard(require("./config"));
13
+
14
+ var _package = _interopRequireDefault(require("../package.json"));
15
+
16
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
+
18
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
19
+
20
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
21
+
22
+ const snapshot = (0, _cliCommand.default)('snapshot', {
23
+ description: 'Snapshot a static directory, snapshots file, or sitemap URL',
24
+ args: [{
25
+ name: 'dir|file|sitemap',
26
+ description: 'Static directory, snapshots file, or sitemap url',
27
+ required: true,
28
+ attribute: val => {
29
+ if (/^https?:\/\//.test(val)) return 'sitemap';
30
+ if (!(0, _fs.existsSync)(val)) throw new Error(`Not found: ${val}`);
31
+ return (0, _fs.lstatSync)(val).isDirectory() ? 'dir' : 'file';
32
+ }
33
+ }],
34
+ flags: [{
35
+ name: 'base-url',
36
+ description: 'The base url pages are hosted at when snapshotting',
37
+ type: 'string',
38
+ short: 'b'
39
+ }, {
40
+ name: 'include',
41
+ description: 'One or more globs/patterns matching snapshots to include',
42
+ type: 'pattern',
43
+ multiple: true
44
+ }, {
45
+ name: 'exclude',
46
+ description: 'One or more globs/patterns matching snapshots to exclude',
47
+ type: 'pattern',
48
+ multiple: true
49
+ }, {
50
+ // static only
51
+ name: 'clean-urls',
52
+ description: 'Rewrite static index and filepath URLs to be clean',
53
+ percyrc: 'static.cleanUrls',
54
+ group: 'Static'
55
+ }, {
56
+ // deprecated
57
+ name: 'files',
58
+ deprecated: ['1.0.0', '--include'],
59
+ percyrc: 'static.include',
60
+ type: 'pattern'
61
+ }, {
62
+ name: 'ignore',
63
+ deprecated: ['1.0.0', '--exclude'],
64
+ percyrc: 'static.exclude',
65
+ type: 'pattern'
66
+ }],
67
+ examples: ['$0 ./public', '$0 snapshots.yml', '$0 https://percy.io/sitemap.xml'],
68
+ percy: {
69
+ clientInfo: `${_package.default.name}/${_package.default.version}`,
70
+ environmentInfo: `node/${process.version}`
71
+ },
72
+ config: {
73
+ schemas: [SnapshotConfig.commonSchema, SnapshotConfig.configSchema, SnapshotConfig.snapshotsFileSchema],
74
+ migrations: [SnapshotConfig.configMigration]
75
+ }
76
+ }, async function* ({
77
+ percy,
78
+ args,
79
+ flags,
80
+ log,
81
+ exit
82
+ }) {
83
+ if (!percy) exit(0, 'Percy is disabled'); // set and validate static or sitemap config flags
84
+
85
+ if (args.dir || args.sitemap) {
86
+ percy.setConfig({
87
+ [args.dir ? 'static' : 'sitemap']: {
88
+ include: flags.include,
89
+ exclude: flags.exclude
90
+ }
91
+ });
92
+ } // gather snapshots
93
+
94
+
95
+ let snapshots, server;
96
+
97
+ try {
98
+ if (args.sitemap) {
99
+ let {
100
+ loadSitemapSnapshots
101
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('./sitemap')));
102
+ let config = { ...percy.config.sitemap,
103
+ ...flags
104
+ };
105
+ snapshots = yield loadSitemapSnapshots(args.sitemap, config);
106
+ } else if (args.dir) {
107
+ let {
108
+ serve,
109
+ loadStaticSnapshots
110
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('./static')));
111
+ let config = { ...percy.config.static,
112
+ ...flags
113
+ };
114
+ server = yield serve(args.dir, config);
115
+ snapshots = yield loadStaticSnapshots(args.dir, { ...config,
116
+ server
117
+ });
118
+ } else {
119
+ let {
120
+ loadSnapshotsFile
121
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('./file')));
122
+ snapshots = yield loadSnapshotsFile(args.file, flags, (invalid, i) => {
123
+ if (i === 0) log.warn('Invalid snapshot options:');
124
+ log.warn(`- ${invalid.path}: ${invalid.message}`);
125
+ });
126
+ }
127
+
128
+ if (!snapshots.length) {
129
+ exit(1, 'No snapshots found');
130
+ } // start processing snapshots
131
+
132
+
133
+ yield* percy.start();
134
+ percy.snapshot(snapshots);
135
+ yield* percy.stop();
136
+ } catch (error) {
137
+ await percy.stop(true);
138
+ throw error;
139
+ } finally {
140
+ var _server;
141
+
142
+ await ((_server = server) === null || _server === void 0 ? void 0 : _server.close());
143
+ }
144
+ });
145
+ exports.snapshot = snapshot;
146
+ var _default = snapshot;
147
+ exports.default = _default;
package/dist/static.js ADDED
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadStaticSnapshots = loadStaticSnapshots;
7
+ exports.mapStaticSnapshots = mapStaticSnapshots;
8
+ exports.serve = serve;
9
+
10
+ var _path = _interopRequireDefault(require("path"));
11
+
12
+ var _http = require("http");
13
+
14
+ var _serveHandler = _interopRequireDefault(require("serve-handler"));
15
+
16
+ var pathToRegexp = _interopRequireWildcard(require("path-to-regexp"));
17
+
18
+ var _utils = require("./utils");
19
+
20
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
+
22
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
23
+
24
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
25
+
26
+ // Transforms a source-destination map into an array of source-destination objects
27
+ function mapRewrites(map, arr) {
28
+ return Object.entries(map).reduce((r, [source, destination]) => {
29
+ return (r || []).concat({
30
+ source,
31
+ destination
32
+ });
33
+ }, arr);
34
+ } // Serves a static directory with the provided options and returns an object containing adjusted
35
+ // rewrites (combined with any baseUrl), the server host, a close method, and the server
36
+ // instance. The `dryRun` option will prevent the server from actually starting.
37
+
38
+
39
+ async function serve(dir, {
40
+ dryRun,
41
+ baseUrl,
42
+ cleanUrls,
43
+ rewrites = {}
44
+ }) {
45
+ let host = 'http://localhost';
46
+ let connections = new Set(); // coerce any provided base-url into a base-url path
47
+
48
+ if (baseUrl && !baseUrl.startsWith('/')) {
49
+ baseUrl = (0, _utils.validURL)(baseUrl).path;
50
+ } // map rewrite options with the base-url
51
+
52
+
53
+ rewrites = mapRewrites(rewrites, baseUrl && [{
54
+ source: _path.default.posix.join(baseUrl, '/:path*'),
55
+ destination: '/:path*'
56
+ }]); // start the server
57
+
58
+ let server = !dryRun && (await new Promise(resolve => {
59
+ let server = (0, _http.createServer)((req, res) => (0, _serveHandler.default)(req, res, {
60
+ public: dir,
61
+ cleanUrls,
62
+ rewrites
63
+ })).listen(() => resolve(server)).on('connection', s => {
64
+ connections.add(s.on('close', () => connections.delete(s)));
65
+ });
66
+ })); // easy clean up
67
+
68
+ let close = () => server && new Promise(resolve => {
69
+ /* istanbul ignore next: sometimes needed when connections are hanging */
70
+ connections.forEach(s => s.destroy());
71
+ server.close(resolve);
72
+ }); // add the port to the host and return
73
+
74
+
75
+ if (server) host += `:${server.address().port}`;
76
+ return {
77
+ host,
78
+ rewrites,
79
+ server,
80
+ close
81
+ };
82
+ } // Maps an array of snapshots or paths to options ready to pass along to the core snapshot
83
+ // method. Paths are normalized before overrides are conditionally applied via their own include and
84
+ // exclude options. Snapshot URLs are then rewritten accordingly before default options are applied,
85
+ // including prepending the appropriate host. The returned set of snapshot options are sorted and
86
+ // filtered by the top-level include and exclude options.
87
+
88
+
89
+ function mapStaticSnapshots(snapshots,
90
+ /* istanbul ignore next: safe defaults */
91
+ {
92
+ host,
93
+ include,
94
+ exclude,
95
+ cleanUrls,
96
+ rewrites = [],
97
+ overrides = [],
98
+ server
99
+ } = {}) {
100
+ var _server$host, _server$rewrites;
101
+
102
+ // prioritize server properties
103
+ host = (_server$host = server === null || server === void 0 ? void 0 : server.host) !== null && _server$host !== void 0 ? _server$host : host;
104
+ rewrites = (_server$rewrites = server === null || server === void 0 ? void 0 : server.rewrites) !== null && _server$rewrites !== void 0 ? _server$rewrites : mapRewrites(rewrites, []); // reduce rewrites into a single function
105
+
106
+ let applyRewrites = [{
107
+ test: url => !/^(https?:\/)?\//.test(url) && url,
108
+ rewrite: url => _path.default.posix.normalize(_path.default.posix.join('/', url))
109
+ }, ...rewrites.map(({
110
+ source,
111
+ destination
112
+ }) => ({
113
+ test: pathToRegexp.match(destination),
114
+ rewrite: pathToRegexp.compile(source)
115
+ })), {
116
+ test: url => cleanUrls && url,
117
+ rewrite: url => url.replace(/(\/index)?\.html$/, '')
118
+ }].reduceRight((apply, {
119
+ test,
120
+ rewrite
121
+ }) => snap => {
122
+ var _snap$url, _res$params;
123
+
124
+ let res = test((_snap$url = snap.url) !== null && _snap$url !== void 0 ? _snap$url : snap);
125
+ if (res) snap = rewrite((_res$params = res.params) !== null && _res$params !== void 0 ? _res$params : res);
126
+ return apply(snap);
127
+ }, s => s); // reduce overrides into a single function
128
+
129
+ let applyOverrides = overrides.reduceRight((apply, {
130
+ include,
131
+ exclude,
132
+ ...opts
133
+ }) => snap => {
134
+ if ((0, _utils.snapshotMatches)(snap, include, exclude)) Object.assign(snap, opts);
135
+ return apply(snap);
136
+ }, s => s); // sort and reduce snapshots with overrides
137
+
138
+ return [...snapshots].sort().reduce((snapshots, snap) => {
139
+ snap = (0, _utils.withDefaults)(applyRewrites(snap), {
140
+ host
141
+ });
142
+ return (0, _utils.snapshotMatches)(snap, include, exclude) ? snapshots.concat(applyOverrides(snap)) : snapshots;
143
+ }, []);
144
+ } // Serves a static directory and returns a list of snapshots.
145
+
146
+
147
+ async function loadStaticSnapshots(dir, config) {
148
+ let {
149
+ default: globby
150
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('globby'))); // gather paths with globby, which only accepts string patterns
151
+
152
+ let isStr = s => typeof s === 'string';
153
+
154
+ let strOr = (a, b) => a.length && a.every(isStr) ? a : b;
155
+
156
+ let files = strOr([].concat(config.include || []), '**/*.html');
157
+ let ignore = strOr([].concat(config.exclude || []), []);
158
+ let paths = await globby(files, {
159
+ cwd: dir,
160
+ ignore
161
+ }); // map snapshots from paths and config
162
+
163
+ return mapStaticSnapshots(paths, config);
164
+ }
package/dist/utils.js CHANGED
@@ -3,21 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.mapStaticSnapshots = mapStaticSnapshots;
7
- exports.serve = serve;
8
6
  exports.snapshotMatches = snapshotMatches;
7
+ exports.validURL = validURL;
9
8
  exports.withDefaults = withDefaults;
10
9
 
11
- var _path = _interopRequireDefault(require("path"));
12
-
13
- var pathToRegexp = _interopRequireWildcard(require("path-to-regexp"));
14
-
15
10
  var _picomatch = _interopRequireDefault(require("picomatch"));
16
11
 
17
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
18
-
19
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
20
-
21
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
13
 
23
14
  // used to deserialize regular expression strings
@@ -48,59 +39,6 @@ function withDefaults(options, {
48
39
 
49
40
  options.url = url.href;
50
41
  return options;
51
- }
52
-
53
- function mapRewrites(map, arr) {
54
- return Object.entries(map).reduce((r, [source, destination]) => {
55
- return (r || []).concat({
56
- source,
57
- destination
58
- });
59
- }, arr);
60
- } // Serves a static directory with the provided options and returns an object containing adjusted
61
- // rewrites (combined with any baseUrl), the server host, a close method, and the server
62
- // instance. The `dryRun` option will prevent the server from actually starting.
63
-
64
-
65
- async function serve(dir, {
66
- dryRun,
67
- baseUrl,
68
- cleanUrls,
69
- rewrites = {}
70
- }) {
71
- let host = 'http://localhost'; // map rewrite options with any base-url
72
-
73
- rewrites = mapRewrites(rewrites, baseUrl && [{
74
- source: _path.default.posix.join(baseUrl, '/:path*'),
75
- destination: '/:path*'
76
- }]); // start the server
77
-
78
- let server = !dryRun && (await new Promise(resolve => {
79
- let server = require('http').createServer((req, res) => {
80
- require('serve-handler')(req, res, {
81
- public: dir,
82
- cleanUrls,
83
- rewrites
84
- });
85
- }).listen(() => resolve(server));
86
- })); // easy clean up
87
-
88
- let close = () => {
89
- if (server) {
90
- return new Promise(resolve => {
91
- server.close(resolve);
92
- });
93
- }
94
- }; // add the port to the host and return
95
-
96
-
97
- if (server) host += `:${server.address().port}`;
98
- return {
99
- host,
100
- rewrites,
101
- server,
102
- close
103
- };
104
42
  } // Returns true or false if a snapshot matches the provided include and exclude predicates. A
105
43
  // predicate can be an array of predicates, a regular expression, a glob pattern, or a function.
106
44
 
@@ -119,7 +57,7 @@ function snapshotMatches(snapshot, include, exclude) {
119
57
  try {
120
58
  let [, parsed = predicate, flags] = RE_REGEXP.exec(predicate) || [];
121
59
  result = new RegExp(parsed, flags).test(snapshot.name);
122
- } catch (e) {}
60
+ } catch {}
123
61
  }
124
62
 
125
63
  return result;
@@ -140,64 +78,4 @@ function snapshotMatches(snapshot, include, exclude) {
140
78
 
141
79
 
142
80
  return !test(exclude, false) && test(include, true);
143
- } // Maps an array of snapshots or paths to options ready to pass along to the core snapshot
144
- // method. Paths are normalized before overrides are conditionally applied via their own include and
145
- // exclude options. Snapshot URLs are then rewritten accordingly before default options are applied,
146
- // including prepending the appropriate host. The returned set of snapshot options are sorted and
147
- // filtered by the top-level include and exclude options.
148
-
149
-
150
- function mapStaticSnapshots(snapshots, {
151
- host,
152
- include,
153
- exclude,
154
- cleanUrls,
155
- rewrites = [],
156
- overrides = [],
157
- server
158
- } = {}) {
159
- var _server$host, _server$rewrites;
160
-
161
- // prioritize server properties
162
- host = (_server$host = server === null || server === void 0 ? void 0 : server.host) !== null && _server$host !== void 0 ? _server$host : host;
163
- rewrites = (_server$rewrites = server === null || server === void 0 ? void 0 : server.rewrites) !== null && _server$rewrites !== void 0 ? _server$rewrites : mapRewrites(rewrites, []); // reduce rewrites into a single function
164
-
165
- let applyRewrites = [{
166
- test: url => !/^(https?:\/)?\//.test(url) && url,
167
- rewrite: url => _path.default.posix.normalize(_path.default.posix.join('/', url))
168
- }, ...rewrites.map(({
169
- source,
170
- destination
171
- }) => ({
172
- test: pathToRegexp.match(destination),
173
- rewrite: pathToRegexp.compile(source)
174
- })), {
175
- test: url => cleanUrls && url,
176
- rewrite: url => url.replace(/(\/index)?\.html$/, '')
177
- }].reduceRight((apply, {
178
- test,
179
- rewrite
180
- }) => snap => {
181
- var _snap$url, _res$params;
182
-
183
- let res = test((_snap$url = snap.url) !== null && _snap$url !== void 0 ? _snap$url : snap);
184
- if (res) snap = rewrite((_res$params = res.params) !== null && _res$params !== void 0 ? _res$params : res);
185
- return apply(snap);
186
- }, s => s); // reduce overrides into a single function
187
-
188
- let applyOverrides = overrides.reduceRight((apply, {
189
- include,
190
- exclude,
191
- ...opts
192
- }) => snap => {
193
- if (snapshotMatches(snap, include, exclude)) Object.assign(snap, opts);
194
- return apply(snap);
195
- }, s => s); // sort and reduce snapshots with overrides
196
-
197
- return [...snapshots].sort().reduce((snapshots, snap) => {
198
- snap = withDefaults(applyRewrites(snap), {
199
- host
200
- });
201
- return snapshotMatches(snap, include, exclude) ? snapshots.concat(applyOverrides(snap)) : snapshots;
202
- }, []);
203
81
  }
package/package.json CHANGED
@@ -1,11 +1,18 @@
1
1
  {
2
2
  "name": "@percy/cli-snapshot",
3
- "version": "1.0.0-beta.71",
3
+ "version": "1.0.0-beta.72",
4
4
  "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/percy/cli",
8
+ "directory": "packages/cli-snapshot"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
5
13
  "main": "dist/index.js",
6
14
  "files": [
7
- "dist",
8
- "oclif.manifest.json"
15
+ "dist"
9
16
  ],
10
17
  "engines": {
11
18
  "node": ">=12"
@@ -13,37 +20,24 @@
13
20
  "scripts": {
14
21
  "build": "node ../../scripts/build",
15
22
  "lint": "eslint --ignore-path ../../.gitignore .",
16
- "postbuild": "oclif-dev manifest",
17
- "readme": "oclif-dev readme",
23
+ "readme": "percy-cli-readme",
18
24
  "test": "node ../../scripts/test",
19
25
  "test:coverage": "yarn test --coverage"
20
26
  },
21
- "publishConfig": {
22
- "access": "public"
23
- },
24
- "oclif": {
25
- "bin": "percy",
26
- "commands": "./dist/commands",
27
- "hooks": {
28
- "init": "./dist/hooks/init"
29
- }
27
+ "@percy/cli": {
28
+ "commands": [
29
+ "./dist/snapshot.js"
30
+ ]
30
31
  },
31
32
  "dependencies": {
32
- "@percy/cli-command": "1.0.0-beta.71",
33
- "@percy/config": "1.0.0-beta.71",
34
- "@percy/core": "1.0.0-beta.71",
35
- "@percy/dom": "1.0.0-beta.71",
36
- "@percy/logger": "1.0.0-beta.71",
33
+ "@percy/cli-command": "1.0.0-beta.72",
34
+ "@percy/config": "1.0.0-beta.72",
35
+ "@percy/core": "1.0.0-beta.72",
37
36
  "globby": "^11.0.4",
38
37
  "path-to-regexp": "^6.2.0",
39
38
  "picomatch": "^2.3.0",
40
39
  "serve-handler": "^6.1.3",
41
40
  "yaml": "^1.10.0"
42
41
  },
43
- "repository": {
44
- "type": "git",
45
- "url": "https://github.com/percy/cli",
46
- "directory": "packages/cli-snapshot"
47
- },
48
- "gitHead": "364d1df717fb19a26ccb024458df6e78a9c11f99"
42
+ "gitHead": "6219287e18a0cacb609d0c2696a5785abc9009b9"
49
43
  }
@@ -1,249 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.Snapshot = void 0;
7
-
8
- var _fs = _interopRequireDefault(require("fs"));
9
-
10
- var _path = _interopRequireDefault(require("path"));
11
-
12
- var _config = _interopRequireDefault(require("@percy/config"));
13
-
14
- var _cliCommand = _interopRequireWildcard(require("@percy/cli-command"));
15
-
16
- var _request = _interopRequireDefault(require("@percy/client/dist/request"));
17
-
18
- var _core = _interopRequireDefault(require("@percy/core"));
19
-
20
- var _logger = _interopRequireDefault(require("@percy/logger"));
21
-
22
- var _globby = _interopRequireDefault(require("globby"));
23
-
24
- var _yaml = _interopRequireDefault(require("yaml"));
25
-
26
- var _package = _interopRequireDefault(require("../../package.json"));
27
-
28
- var _utils = require("../utils");
29
-
30
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
31
-
32
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
33
-
34
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35
-
36
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
37
-
38
- class Snapshot extends _cliCommand.default {
39
- constructor(...args) {
40
- super(...args);
41
-
42
- _defineProperty(this, "log", (0, _logger.default)('cli:snapshot'));
43
- }
44
-
45
- async run() {
46
- // skip snapshots
47
- if (!this.isPercyEnabled()) {
48
- return this.log.info('Percy is disabled. Skipping snapshots');
49
- }
50
-
51
- let {
52
- 'dir|file|sitemap': arg
53
- } = this.args;
54
- let isSitemap = /^https?:\/\//.test(arg); // validate directory or file existence
55
-
56
- if (!isSitemap && !_fs.default.existsSync(arg)) this.error(`Not found: ${arg}`);
57
-
58
- let isStatic = !isSitemap && _fs.default.lstatSync(arg).isDirectory(); // initialize percy
59
-
60
-
61
- this.percy = new _core.default({ ...this.percyrc({
62
- [isSitemap ? 'sitemap' : 'static']: isSitemap || isStatic ? {
63
- include: this.flags.include,
64
- exclude: this.flags.exclude
65
- } : undefined
66
- }),
67
- clientInfo: `${_package.default.name}/${_package.default.version}`,
68
- environmentInfo: `node/${process.version}`,
69
- server: false
70
- }); // gather snapshots
71
-
72
- let snapshots = isSitemap && (await this.loadSitemapSnapshots(arg)) || isStatic && (await this.loadStaticSnapshots(arg)) || (await this.loadSnapshotsFile(arg));
73
-
74
- if (!snapshots.length) {
75
- this.error('No snapshots found');
76
- } // start processing snapshots
77
-
78
-
79
- await this.percy.start();
80
- this.percy.snapshot(snapshots);
81
- } // Called on error, interupt, or after running
82
-
83
-
84
- async finally(error) {
85
- var _this$percy, _this$server;
86
-
87
- await ((_this$percy = this.percy) === null || _this$percy === void 0 ? void 0 : _this$percy.stop(!!error));
88
- await ((_this$server = this.server) === null || _this$server === void 0 ? void 0 : _this$server.close());
89
- } // Fetches and maps sitemap URLs to snapshots.
90
-
91
-
92
- async loadSitemapSnapshots(sitemap) {
93
- let config = this.percy.config.sitemap; // fetch sitemap URLs
94
-
95
- let urls = await (0, _request.default)(sitemap, (body, res) => {
96
- // validate sitemap content-type
97
- let [contentType] = res.headers['content-type'].split(';');
98
-
99
- if (!/^(application|text)\/xml$/.test(contentType)) {
100
- this.error('The sitemap must be an XML document, ' + `but the content-type was "${contentType}"`);
101
- } // parse XML content into a list of URLs
102
-
103
-
104
- let urls = body.match(/(?<=<loc>)(.*)(?=<\/loc>)/ig); // filter out duplicate URLs that differ by a trailing slash
105
-
106
- return urls.filter((url, i) => {
107
- let match = urls.indexOf(url.replace(/\/$/, ''));
108
- return match === -1 || match === i;
109
- });
110
- }); // map with inherited static options
111
-
112
- return (0, _utils.mapStaticSnapshots)(urls, config);
113
- } // Serves a static directory and returns a list of snapshots.
114
-
115
-
116
- async loadStaticSnapshots(dir) {
117
- let config = this.percy.config.static;
118
- let baseUrl = this.flags['base-url'] || config.baseUrl;
119
- let dryRun = this.flags['dry-run']; // validate any provided base-url
120
-
121
- if (baseUrl && !baseUrl.startsWith('/')) {
122
- this.error('The base-url must begin with a forward slash (/) ' + 'when snapshotting static directories');
123
- } // start the server
124
-
125
-
126
- this.server = await (0, _utils.serve)(dir, { ...config,
127
- baseUrl,
128
- dryRun
129
- }); // gather paths
130
-
131
- let isStr = s => typeof s === 'string';
132
-
133
- let strOr = (a, b) => a.length && a.every(isStr) ? a : b;
134
-
135
- let files = strOr([].concat(config.include || []), '**/*.html');
136
- let ignore = strOr([].concat(config.exclude || []), []);
137
- let paths = await (0, _globby.default)(files, {
138
- cwd: dir,
139
- ignore
140
- }); // map snapshots from paths and config
141
-
142
- return (0, _utils.mapStaticSnapshots)(paths, { ...config,
143
- server: this.server
144
- });
145
- } // Loads snapshots from a js, json, or yaml file.
146
-
147
-
148
- async loadSnapshotsFile(file) {
149
- let ext = _path.default.extname(file);
150
-
151
- let config = {}; // load snapshots file
152
-
153
- if (ext === '.js') {
154
- config = require(_path.default.resolve(file));
155
- if (typeof config === 'function') config = await config();
156
- } else if (ext === '.json') {
157
- config = JSON.parse(_fs.default.readFileSync(file, {
158
- encoding: 'utf-8'
159
- }));
160
- } else if (ext.match(/\.ya?ml$/)) {
161
- config = _yaml.default.parse(_fs.default.readFileSync(file, {
162
- encoding: 'utf-8'
163
- }));
164
- } else {
165
- this.error(`Unsupported filetype: ${file}`);
166
- }
167
-
168
- let {
169
- // flags override config options
170
- 'base-url': baseUrl = config.baseUrl,
171
- include = config.include,
172
- exclude = config.exclude
173
- } = this.flags; // validate base-url before config options
174
-
175
- if (baseUrl && !baseUrl.startsWith('http')) {
176
- this.error('The base-url must include a protocol and hostname ' + 'when providing a list of snapshots');
177
- } // validate snapshot config options
178
-
179
-
180
- let errors = _config.default.validate(config, '/snapshot/list');
181
-
182
- if (errors) {
183
- this.log.warn('Invalid snapshot options:');
184
-
185
- for (let e of errors) this.log.warn(`- ${e.path}: ${e.message}`);
186
- } // support config that only contains a list of snapshots
187
-
188
-
189
- return (Array.isArray(config) ? config : config.snapshots || []).reduce((snapshots, snap) => {
190
- // reduce matching snapshots with default options
191
- snap = (0, _utils.withDefaults)(snap, {
192
- host: baseUrl
193
- });
194
- return (0, _utils.snapshotMatches)(snap, include, exclude) ? snapshots.concat(snap) : snapshots;
195
- }, []);
196
- }
197
-
198
- }
199
-
200
- exports.Snapshot = Snapshot;
201
-
202
- _defineProperty(Snapshot, "description", 'Take snapshots from a static directory, snapshots file, or sitemap url');
203
-
204
- _defineProperty(Snapshot, "args", [{
205
- name: 'dir|file|sitemap',
206
- description: 'static directory, snapshots file, or sitemap url',
207
- required: true
208
- }]);
209
-
210
- _defineProperty(Snapshot, "flags", { ..._cliCommand.flags.logging,
211
- ..._cliCommand.flags.discovery,
212
- ..._cliCommand.flags.config,
213
- 'base-url': _cliCommand.flags.string({
214
- description: 'the base url pages are hosted at when snapshotting',
215
- char: 'b'
216
- }),
217
- include: _cliCommand.flags.string({
218
- description: 'one or more globs/patterns matching snapshots to include',
219
- multiple: true
220
- }),
221
- exclude: _cliCommand.flags.string({
222
- description: 'one or more globs/patterns matching snapshots to exclude',
223
- multiple: true
224
- }),
225
- // static only flags
226
- 'clean-urls': _cliCommand.flags.boolean({
227
- description: 'rewrite static index and filepath URLs to be clean',
228
- percyrc: 'static.cleanUrls'
229
- }),
230
- // deprecated flags
231
- files: _cliCommand.flags.string({
232
- deprecated: {
233
- map: 'include',
234
- until: '1.0.0'
235
- },
236
- percyrc: 'static.include',
237
- hidden: true
238
- }),
239
- ignore: _cliCommand.flags.string({
240
- deprecated: {
241
- map: 'exclude',
242
- until: '1.0.0'
243
- },
244
- percyrc: 'static.exclude',
245
- hidden: true
246
- })
247
- });
248
-
249
- _defineProperty(Snapshot, "examples", ['$ percy snapshot ./public', '$ percy snapshot snapshots.yml']);
@@ -1,26 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = _default;
7
-
8
- var _config = _interopRequireDefault(require("@percy/config"));
9
-
10
- var CoreConfig = _interopRequireWildcard(require("@percy/core/dist/config"));
11
-
12
- var SnapshotConfig = _interopRequireWildcard(require("../config"));
13
-
14
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
-
16
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
-
18
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
-
20
- function _default() {
21
- _config.default.addSchema(CoreConfig.schemas);
22
-
23
- _config.default.addSchema(SnapshotConfig.schemas);
24
-
25
- _config.default.addMigration(SnapshotConfig.migration);
26
- }
@@ -1 +0,0 @@
1
- {"version":"1.0.0-beta.71","commands":{"snapshot":{"id":"snapshot","description":"Take snapshots from a static directory, snapshots file, or sitemap url","pluginName":"@percy/cli-snapshot","pluginType":"core","aliases":[],"examples":["$ percy snapshot ./public","$ percy snapshot snapshots.yml"],"flags":{"verbose":{"name":"verbose","type":"boolean","char":"v","description":"log everything","allowNo":false},"quiet":{"name":"quiet","type":"boolean","char":"q","description":"log errors only","allowNo":false},"silent":{"name":"silent","type":"boolean","description":"log nothing","allowNo":false},"allowed-hostname":{"name":"allowed-hostname","type":"option","char":"h","description":"allowed hostnames to capture in asset discovery"},"network-idle-timeout":{"name":"network-idle-timeout","type":"option","char":"t","description":"asset discovery network idle timeout"},"disable-cache":{"name":"disable-cache","type":"boolean","description":"disable asset discovery caches","allowNo":false},"dry-run":{"name":"dry-run","type":"boolean","char":"d","description":"print logs only, do not run asset discovery or upload snapshots","allowNo":false},"debug":{"name":"debug","type":"boolean","description":"debug asset discovery and do not upload snapshots","allowNo":false},"config":{"name":"config","type":"option","char":"c","description":"configuration file path"},"base-url":{"name":"base-url","type":"option","char":"b","description":"the base url pages are hosted at when snapshotting"},"include":{"name":"include","type":"option","description":"one or more globs/patterns matching snapshots to include"},"exclude":{"name":"exclude","type":"option","description":"one or more globs/patterns matching snapshots to exclude"},"clean-urls":{"name":"clean-urls","type":"boolean","description":"rewrite static index and filepath URLs to be clean","allowNo":false},"files":{"name":"files","type":"option","hidden":true},"ignore":{"name":"ignore","type":"option","hidden":true}},"args":[{"name":"dir|file|sitemap","description":"static directory, snapshots file, or sitemap url","required":true}]}}}