Skip to content

Commit

Permalink
♻️ Update cli-snapshot command to use new core snapshot syntaxes (#819)
Browse files Browse the repository at this point in the history
* ♻ Update cli-snapshot command to use new core snapshot syntaxes

* ➖ Remove unneeded dependencies
  • Loading branch information
wwilsman authored Mar 16, 2022
1 parent 67a794c commit f5f974c
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 417 deletions.
4 changes: 0 additions & 4 deletions packages/cli-snapshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@
"@percy/cli-command": "1.0.0-beta.76",
"@percy/config": "1.0.0-beta.76",
"@percy/core": "1.0.0-beta.76",
"globby": "^11.0.4",
"path-to-regexp": "^6.2.0",
"picomatch": "^2.3.0",
"serve-handler": "^6.1.3",
"yaml": "^1.10.0"
}
}
20 changes: 7 additions & 13 deletions packages/cli-snapshot/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,19 @@ export const configSchema = {
baseUrl: { $ref: '/snapshot/server#/properties/baseUrl' },
cleanUrls: { $ref: '/snapshot/server#/properties/cleanUrls' },
rewrites: { $ref: '/snapshot/server#/properties/rewrites' },
overrides: { $ref: '/snapshot#/$defs/options' }
options: { $ref: '/snapshot#/$defs/options' }
}
},
sitemap: {
type: 'object',
$ref: '/snapshot#/$defs/filter',
unevaluatedProperties: false,
properties: {
overrides: { $ref: '/snapshot#/$defs/options' }
options: { $ref: '/snapshot#/$defs/options' }
}
}
};

// Snapshots file schema
export const snapshotsFileSchema = {
$id: '/snapshot/file',
oneOf: [
{ $ref: '/snapshot#/$defs/snapshots' },
{ $ref: '/snapshot/list' }
]
};

export function configMigration(config, util) {
/* eslint-disable curly */
if (config.version < 2) {
Expand All @@ -43,9 +34,12 @@ export function configMigration(config, util) {
// static files and ignore options were renamed
util.deprecate('static.files', { map: 'static.include', ...notice });
util.deprecate('static.ignore', { map: 'static.exclude', ...notice });
// static and sitemap option overrides were renamed
util.deprecate('static.overrides', { map: 'static.options', ...notice });
util.deprecate('sitemap.overrides', { map: 'sitemap.options', ...notice });

for (let i in (config.static?.overrides || [])) {
let k = `static.overrides[${i}]`;
for (let i in (config.static?.options || [])) {
let k = `static.options[${i}]`;
util.deprecate(`${k}.files`, { map: `${k}.include`, ...notice });
util.deprecate(`${k}.ignore`, { map: `${k}.exclude`, ...notice });
}
Expand Down
45 changes: 0 additions & 45 deletions packages/cli-snapshot/src/file.js

This file was deleted.

28 changes: 0 additions & 28 deletions packages/cli-snapshot/src/sitemap.js

This file was deleted.

118 changes: 70 additions & 48 deletions packages/cli-snapshot/src/snapshot.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { existsSync, lstatSync } from 'fs';
import fs from 'fs';
import path from 'path';
import command from '@percy/cli-command';
import * as SnapshotConfig from './config';
import pkg from '../package.json';
Expand All @@ -12,8 +13,8 @@ export const snapshot = command('snapshot', {
required: true,
attribute: val => {
if (/^https?:\/\//.test(val)) return 'sitemap';
if (!existsSync(val)) throw new Error(`Not found: ${val}`);
return lstatSync(val).isDirectory() ? 'dir' : 'file';
if (!fs.existsSync(val)) throw new Error(`Not found: ${val}`);
return fs.lstatSync(val).isDirectory() ? 'serve' : 'file';
}
}],

Expand Down Expand Up @@ -58,70 +59,91 @@ export const snapshot = command('snapshot', {
],

percy: {
deferUploads: true,
clientInfo: `${pkg.name}/${pkg.version}`,
environmentInfo: `node/${process.version}`
},

config: {
schemas: [
SnapshotConfig.configSchema,
SnapshotConfig.snapshotsFileSchema
],
migrations: [
SnapshotConfig.configMigration
]
schemas: [SnapshotConfig.configSchema],
migrations: [SnapshotConfig.configMigration]
}
}, async function*({ percy, args, flags, log, exit }) {
if (!percy) exit(0, 'Percy is disabled');

// set and validate static or sitemap config flags
if (args.dir || args.sitemap) {
percy.setConfig({
[args.dir ? 'static' : 'sitemap']: {
include: flags.include,
exclude: flags.exclude
}
});
}
let { include, exclude, baseUrl, cleanUrls } = flags;
let { file, serve, sitemap } = args;

// gather snapshots
let snapshots, server;
// parse and validate the --base-url flag after args are parsed
if (file || serve) baseUrl &&= parseBaseUrl(baseUrl, !!serve);
// only continue if percy is not disabled
if (!percy) exit(0, 'Percy is disabled');

try {
if (args.sitemap) {
let { loadSitemapSnapshots } = await import('./sitemap');
let config = { ...percy.config.sitemap, ...flags };

snapshots = yield loadSitemapSnapshots(args.sitemap, config);
} else if (args.dir) {
let { serve, loadStaticSnapshots } = await import('./static');
let config = { ...percy.config.static, ...flags };

server = yield serve(args.dir, config);
snapshots = yield loadStaticSnapshots(args.dir, { ...config, server });
} else {
let { loadSnapshotsFile } = await import('./file');

snapshots = yield loadSnapshotsFile(args.file, flags, (invalid, i) => {
if (i === 0) log.warn('Invalid snapshot options:');
log.warn(`- ${invalid.path}: ${invalid.message}`);
});
}
let options;

if (!snapshots.length) {
exit(1, 'No snapshots found');
/* istanbul ignore else: arg is required and always one of these */
if (file) {
// load snapshots file
let snapshots = yield loadSnapshotFile(file);
// accept a config object instead of an array of snapshots
let config = Array.isArray(snapshots) ? { snapshots } : snapshots;
options = merge(config, { baseUrl, include, exclude });
} else if (serve) {
// serve and snapshot a static directory
let config = { serve, cleanUrls, baseUrl, include, exclude };
options = merge(percy.config.static, config);
} else if (sitemap) {
// fetch urls to snapshot from a sitemap
let config = { sitemap, include, exclude };
options = merge(percy.config.sitemap, config);
}

// start processing snapshots
yield* percy.start();
percy.snapshot(snapshots);
yield percy.snapshot(options);
yield* percy.stop();
} catch (error) {
await percy.stop(true);
throw error;
} finally {
await server?.close();
}
});

// Validates the provided `--base-url` flag and returns a `baseUrl` string if valid. The flag is
// validated and parsed differently for static directories and snapshot files.
function parseBaseUrl(baseUrl, pathOnly) {
try {
let needsHost = pathOnly && baseUrl.startsWith('/');
let url = new URL(baseUrl, needsHost ? 'http://localhost' : undefined);
return pathOnly ? url.pathname : url.href;
} catch (e) {
throw new Error("The '--base-url' flag must " + (pathOnly
? 'start with a forward slash (/) when providing a static directory'
: 'include a protocol and hostname when providing a list of snapshots'
));
}
}

// Small shallow merge util that does not merge null or undefined values.
function merge(...objs) {
return objs.reduce((target, obj) => {
for (let k in obj) target[k] = obj[k] ?? target[k];
return target;
}, {});
}

// Loads snapshot options from a js, json, or yaml file.
async function loadSnapshotFile(file) {
let ext = path.extname(file);

if (ext === '.js') {
let { default: module } = await import(path.resolve(file));
return typeof module === 'function' ? await module() : module;
} else if (ext === '.json') {
return JSON.parse(fs.readFileSync(file, { encoding: 'utf-8' }));
} else if (ext.match(/\.ya?ml$/)) {
let { parse } = await import('yaml');
return parse(fs.readFileSync(file, { encoding: 'utf-8' }));
} else {
throw new Error(`Unsupported filetype: ${file}`);
}
}

export default snapshot;
Loading

0 comments on commit f5f974c

Please sign in to comment.