@percy/cli 1.12.0 → 1.14.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/dist/commands.js +41 -40
- package/dist/update.js +25 -19
- package/package.json +11 -11
package/dist/commands.js
CHANGED
|
@@ -3,22 +3,22 @@ import fs from 'fs';
|
|
|
3
3
|
import url from 'url';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import Module from 'module';
|
|
6
|
-
import { command, legacyCommand, logger } from '@percy/cli-command';
|
|
6
|
+
import { command, legacyCommand, logger } from '@percy/cli-command';
|
|
7
7
|
|
|
8
|
+
// Helper to simplify reducing async functions
|
|
8
9
|
async function reduceAsync(iter, reducer, accum = []) {
|
|
9
10
|
for (let i of iter) accum = await reducer(accum, i);
|
|
10
|
-
|
|
11
11
|
return accum;
|
|
12
|
-
}
|
|
13
|
-
|
|
12
|
+
}
|
|
14
13
|
|
|
14
|
+
// Helper to read and reduce files within a directory
|
|
15
15
|
function reduceFiles(dir, reducer) {
|
|
16
16
|
return reduceAsync(fs.readdirSync(dir, {
|
|
17
17
|
withFileTypes: true
|
|
18
18
|
}), reducer);
|
|
19
|
-
}
|
|
20
|
-
|
|
19
|
+
}
|
|
21
20
|
|
|
21
|
+
// Returns the paths of potential percy packages found within node_modules
|
|
22
22
|
function findModulePackages(dir) {
|
|
23
23
|
try {
|
|
24
24
|
// not given node_modules or a directory that contains node_modules, look up
|
|
@@ -27,14 +27,14 @@ function findModulePackages(dir) {
|
|
|
27
27
|
let next = fs.existsSync(modulesPath) ? modulesPath : path.dirname(dir);
|
|
28
28
|
if (next === dir || next === os.homedir()) return [];
|
|
29
29
|
return findModulePackages(next);
|
|
30
|
-
}
|
|
31
|
-
|
|
30
|
+
}
|
|
32
31
|
|
|
32
|
+
// given node modules, look for percy packages
|
|
33
33
|
return reduceFiles(dir, async (roots, file) => {
|
|
34
34
|
let rootPath = path.join(dir, file.name);
|
|
35
|
-
|
|
36
35
|
if (file.name === '@percy') {
|
|
37
|
-
return roots.concat(await reduceFiles(rootPath, (dirs, f) =>
|
|
36
|
+
return roots.concat(await reduceFiles(rootPath, (dirs, f) =>
|
|
37
|
+
// specifically protect against files to allow linked directories
|
|
38
38
|
f.isFile() ? dirs : dirs.concat(path.join(rootPath, f.name)), []));
|
|
39
39
|
} else if (file.name.startsWith('percy-cli-')) {
|
|
40
40
|
return roots.concat(rootPath);
|
|
@@ -46,14 +46,14 @@ function findModulePackages(dir) {
|
|
|
46
46
|
logger('cli:plugins').debug(error);
|
|
47
47
|
return [];
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
+
// Used by `findPnpPackages` to filter Percy CLI plugins
|
|
52
|
+
const PERCY_PKG_REG = /^(@percy\/|percy-cli-)/;
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
// Returns the paths of potential percy packages found within yarn's pnp system
|
|
54
55
|
function findPnpPackages(dir) {
|
|
55
56
|
var _Module$findPnpApi;
|
|
56
|
-
|
|
57
57
|
let pnpapi = (_Module$findPnpApi = Module.findPnpApi) === null || _Module$findPnpApi === void 0 ? void 0 : _Module$findPnpApi.call(Module, `${dir}/`);
|
|
58
58
|
let pkgLoc = pnpapi === null || pnpapi === void 0 ? void 0 : pnpapi.findPackageLocator(`${dir}/`);
|
|
59
59
|
let pkgInfo = pkgLoc && (pnpapi === null || pnpapi === void 0 ? void 0 : pnpapi.getPackageInformation(pkgLoc));
|
|
@@ -64,21 +64,21 @@ function findPnpPackages(dir) {
|
|
|
64
64
|
let depInfo = pnpapi.getPackageInformation(depLoc);
|
|
65
65
|
return roots.concat(depInfo.packageLocation);
|
|
66
66
|
}, []);
|
|
67
|
-
}
|
|
68
|
-
|
|
67
|
+
}
|
|
69
68
|
|
|
69
|
+
// Helper to import and wrap legacy percy commands for reverse compatibility
|
|
70
70
|
function importLegacyCommands(commandsPath) {
|
|
71
71
|
return reduceFiles(commandsPath, async (cmds, file) => {
|
|
72
72
|
let filepath = path.join(commandsPath, file.name);
|
|
73
73
|
let {
|
|
74
74
|
name
|
|
75
75
|
} = path.parse(file.name);
|
|
76
|
-
|
|
77
76
|
if (file.isDirectory()) {
|
|
78
77
|
// recursively import nested commands and find the index command
|
|
79
78
|
let commands = await importLegacyCommands(filepath);
|
|
80
|
-
let index = commands.findIndex(cmd => cmd.name === 'index');
|
|
79
|
+
let index = commands.findIndex(cmd => cmd.name === 'index');
|
|
81
80
|
|
|
81
|
+
// modify or create an index command to hold nested commands
|
|
82
82
|
index = ~index ? commands.splice(index, 1)[0] : command();
|
|
83
83
|
Object.defineProperty(index, 'name', {
|
|
84
84
|
value: name
|
|
@@ -90,71 +90,72 @@ function importLegacyCommands(commandsPath) {
|
|
|
90
90
|
let exports = Object.values(await import(url.pathToFileURL(filepath).href));
|
|
91
91
|
let cmd = exports.find(e => {
|
|
92
92
|
var _e$prototype;
|
|
93
|
-
|
|
94
93
|
return typeof (e === null || e === void 0 ? void 0 : (_e$prototype = e.prototype) === null || _e$prototype === void 0 ? void 0 : _e$prototype.run) === 'function';
|
|
95
94
|
});
|
|
96
95
|
return cmd ? cmds.concat(legacyCommand(name, cmd)) : cmds;
|
|
97
96
|
}
|
|
98
97
|
});
|
|
99
|
-
}
|
|
100
|
-
|
|
98
|
+
}
|
|
101
99
|
|
|
100
|
+
// Imports and returns compatibile CLI commands from various sources
|
|
102
101
|
export async function importCommands() {
|
|
103
|
-
let root = path.resolve(url.fileURLToPath(import.meta.url), '../..');
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
let root = path.resolve(url.fileURLToPath(import.meta.url), '../..');
|
|
103
|
+
|
|
104
|
+
// start with a set to get built-in deduplication
|
|
105
|
+
let cmdPkgs = await reduceAsync(new Set([
|
|
106
|
+
// find included dependencies
|
|
107
|
+
root,
|
|
108
|
+
// find potential sibling packages
|
|
109
|
+
path.join(root, '..'),
|
|
110
|
+
// find any current project dependencies
|
|
108
111
|
process.cwd()]), async (roots, dir) => {
|
|
109
112
|
if (fs.existsSync(path.join(dir, 'package.json'))) roots.push(dir);
|
|
110
113
|
roots.push(...(await findModulePackages(dir)));
|
|
111
114
|
roots.push(...(await findPnpPackages(dir)));
|
|
112
115
|
return roots;
|
|
113
|
-
});
|
|
116
|
+
});
|
|
114
117
|
|
|
118
|
+
// reduce found packages to functions which import cli commands
|
|
115
119
|
let cmdImports = await reduceAsync(cmdPkgs, async (pkgs, pkgPath) => {
|
|
116
120
|
var _pkg$oclif, _pkg$PercyCli;
|
|
121
|
+
let pkg = JSON.parse(fs.readFileSync(path.join(pkgPath, 'package.json')));
|
|
122
|
+
// do not include self
|
|
123
|
+
if (pkg.name === '@percy/cli') return pkgs;
|
|
117
124
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (pkg.name === '@percy/cli') return pkgs; // support legacy oclif percy commands
|
|
121
|
-
|
|
125
|
+
// support legacy oclif percy commands
|
|
122
126
|
if (((_pkg$oclif = pkg.oclif) === null || _pkg$oclif === void 0 ? void 0 : _pkg$oclif.bin) === 'percy') {
|
|
123
127
|
pkgs.set(pkg.name, async () => {
|
|
124
128
|
var _pkg$oclif$hooks;
|
|
125
|
-
|
|
126
129
|
if ((_pkg$oclif$hooks = pkg.oclif.hooks) !== null && _pkg$oclif$hooks !== void 0 && _pkg$oclif$hooks.init) {
|
|
127
130
|
let initPath = path.join(pkgPath, pkg.oclif.hooks.init);
|
|
128
131
|
let init = await import(url.pathToFileURL(initPath).href);
|
|
129
132
|
await init.default();
|
|
130
133
|
}
|
|
131
|
-
|
|
132
134
|
if (pkg.oclif.commands) {
|
|
133
135
|
let commandsPath = path.join(pkgPath, pkg.oclif.commands);
|
|
134
136
|
return importLegacyCommands(commandsPath);
|
|
135
137
|
}
|
|
136
|
-
|
|
137
138
|
return [];
|
|
138
139
|
});
|
|
139
|
-
}
|
|
140
|
-
|
|
140
|
+
}
|
|
141
141
|
|
|
142
|
+
// overwrite any found package of the same name
|
|
142
143
|
if ((_pkg$PercyCli = pkg['@percy/cli']) !== null && _pkg$PercyCli !== void 0 && _pkg$PercyCli.commands) {
|
|
143
144
|
pkgs.set(pkg.name, () => Promise.all(pkg['@percy/cli'].commands.map(async cmdPath => {
|
|
144
145
|
var _module$default;
|
|
145
|
-
|
|
146
146
|
let modulePath = path.join(pkgPath, cmdPath);
|
|
147
147
|
let module = await import(url.pathToFileURL(modulePath).href);
|
|
148
148
|
(_module$default = module.default).packageInformation || (_module$default.packageInformation = pkg);
|
|
149
149
|
return module.default;
|
|
150
150
|
})));
|
|
151
151
|
}
|
|
152
|
-
|
|
153
152
|
return pkgs;
|
|
154
|
-
}, new Map());
|
|
153
|
+
}, new Map());
|
|
155
154
|
|
|
156
|
-
|
|
155
|
+
// actually import found commands
|
|
156
|
+
let cmds = await reduceAsync(cmdImports.values(), async (cmds, importCmds) => cmds.concat(await importCmds()));
|
|
157
157
|
|
|
158
|
+
// sort standalone commands before command topics
|
|
158
159
|
return cmds.sort((a, b) => {
|
|
159
160
|
if (a.callback && !b.callback) return -1;
|
|
160
161
|
if (b.callback && !a.callback) return 1;
|
package/dist/update.js
CHANGED
|
@@ -3,17 +3,18 @@ import url from 'url';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import logger from '@percy/logger';
|
|
5
5
|
import { colors } from '@percy/logger/utils';
|
|
6
|
-
import { getPackageJSON } from '@percy/cli-command/utils';
|
|
6
|
+
import { getPackageJSON } from '@percy/cli-command/utils';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// filepath where the cache will be read and written to
|
|
9
|
+
const CACHE_FILE = path.resolve(url.fileURLToPath(import.meta.url), '../../.releases');
|
|
10
|
+
// max age the cache should be used for (3 days)
|
|
11
|
+
const CACHE_MAX_AGE = 3 * 24 * 60 * 60 * 1000;
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
// Safely read from CACHE_FILE and return an object containing `data` mirroring what was previously
|
|
11
14
|
// written using `writeToCache(data)`. An empty object is returned when older than CACHE_MAX_AGE,
|
|
12
15
|
// and an `error` will be present if one was encountered.
|
|
13
|
-
|
|
14
16
|
function readFromCache() {
|
|
15
17
|
let cached = {};
|
|
16
|
-
|
|
17
18
|
try {
|
|
18
19
|
if (fs.existsSync(CACHE_FILE)) {
|
|
19
20
|
let {
|
|
@@ -27,11 +28,10 @@ function readFromCache() {
|
|
|
27
28
|
log.debug('Unable to read from cache');
|
|
28
29
|
log.debug(cached.error = error);
|
|
29
30
|
}
|
|
30
|
-
|
|
31
31
|
return cached;
|
|
32
|
-
}
|
|
33
|
-
|
|
32
|
+
}
|
|
34
33
|
|
|
34
|
+
// Safely write data to CACHE_FILE with the current timestamp.
|
|
35
35
|
function writeToCache(data) {
|
|
36
36
|
try {
|
|
37
37
|
fs.writeFileSync(CACHE_FILE, JSON.stringify({
|
|
@@ -43,30 +43,32 @@ function writeToCache(data) {
|
|
|
43
43
|
log.debug('Unable to write to cache');
|
|
44
44
|
log.debug(error);
|
|
45
45
|
}
|
|
46
|
-
}
|
|
47
|
-
|
|
46
|
+
}
|
|
48
47
|
|
|
48
|
+
// Fetch and return release information for @percy/cli.
|
|
49
49
|
async function fetchReleases(pkg) {
|
|
50
50
|
let {
|
|
51
51
|
request
|
|
52
|
-
} = await import('@percy/client/utils');
|
|
52
|
+
} = await import('@percy/client/utils');
|
|
53
53
|
|
|
54
|
+
// fetch releases from the github api without retries
|
|
54
55
|
let api = 'https://api.github.com/repos/percy/cli/releases';
|
|
55
56
|
let data = await request(api, {
|
|
56
57
|
headers: {
|
|
57
58
|
'User-Agent': pkg.name
|
|
58
59
|
},
|
|
59
60
|
retries: 0
|
|
60
|
-
});
|
|
61
|
+
});
|
|
61
62
|
|
|
63
|
+
// return relevant information
|
|
62
64
|
return data.map(r => ({
|
|
63
65
|
tag: r.tag_name,
|
|
64
66
|
prerelease: r.prerelease
|
|
65
67
|
}));
|
|
66
|
-
}
|
|
67
|
-
// is cached to speed up subsequent CLI usage.
|
|
68
|
-
|
|
68
|
+
}
|
|
69
69
|
|
|
70
|
+
// Check for updates by comparing latest releases with the current version. The result of the check
|
|
71
|
+
// is cached to speed up subsequent CLI usage.
|
|
70
72
|
export async function checkForUpdate() {
|
|
71
73
|
let {
|
|
72
74
|
data: releases,
|
|
@@ -74,18 +76,22 @@ export async function checkForUpdate() {
|
|
|
74
76
|
} = readFromCache();
|
|
75
77
|
let pkg = getPackageJSON(import.meta.url);
|
|
76
78
|
let log = logger('cli:update');
|
|
77
|
-
|
|
79
|
+
if (process.env.PERCY_SKIP_UPDATE_CHECK) {
|
|
80
|
+
log.debug('Skipping update check');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
78
83
|
try {
|
|
79
84
|
// request new release information if needed
|
|
80
85
|
if (!releases) {
|
|
81
86
|
releases = await fetchReleases(pkg);
|
|
82
87
|
if (!cacheError) writeToCache(releases, log);
|
|
83
|
-
}
|
|
84
|
-
|
|
88
|
+
}
|
|
85
89
|
|
|
90
|
+
// check the current package version against released versions
|
|
86
91
|
let versions = releases.map(r => r.tag.substr(1));
|
|
87
|
-
let age = versions.indexOf(pkg.version);
|
|
92
|
+
let age = versions.indexOf(pkg.version);
|
|
88
93
|
|
|
94
|
+
// a new version is available
|
|
89
95
|
if (age !== 0) {
|
|
90
96
|
log.warn(`\n${age > 0 && age < 10 ? 'A new version of @percy/cli is available!' : 'Heads up! The current version of @percy/cli is more than 10 releases behind!'} ${colors.red(pkg.version)} -> ${colors.green(versions[0])}\n`);
|
|
91
97
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"test:coverage": "yarn test --coverage"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@percy/cli-app": "1.
|
|
34
|
-
"@percy/cli-build": "1.
|
|
35
|
-
"@percy/cli-command": "1.
|
|
36
|
-
"@percy/cli-config": "1.
|
|
37
|
-
"@percy/cli-exec": "1.
|
|
38
|
-
"@percy/cli-snapshot": "1.
|
|
39
|
-
"@percy/cli-upload": "1.
|
|
40
|
-
"@percy/client": "1.
|
|
41
|
-
"@percy/logger": "1.
|
|
33
|
+
"@percy/cli-app": "1.14.0",
|
|
34
|
+
"@percy/cli-build": "1.14.0",
|
|
35
|
+
"@percy/cli-command": "1.14.0",
|
|
36
|
+
"@percy/cli-config": "1.14.0",
|
|
37
|
+
"@percy/cli-exec": "1.14.0",
|
|
38
|
+
"@percy/cli-snapshot": "1.14.0",
|
|
39
|
+
"@percy/cli-upload": "1.14.0",
|
|
40
|
+
"@percy/client": "1.14.0",
|
|
41
|
+
"@percy/logger": "1.14.0"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "fd72688e449d6dd3eafd346fc07879cb3bb01a4e"
|
|
44
44
|
}
|