mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 21:07:46 +00:00
[cli] Replace update-notifier dependency with build in (#9098)
This PR replaces the `update-notifier` dependency with a custom implementation. There are a few reasons: the dependency is quite large, it requires ESM in order to update, can sometimes suggest an update to an older version, and used dependencies with known security issues. The result looks like: <img width="768" alt="image" src="https://user-images.githubusercontent.com/97262/208452226-b7508299-f830-4d42-a96a-7646ec8227aa.png"> Note: This PR is the successor to https://github.com/vercel/vercel/pull/8090.
This commit is contained in:
@@ -50,8 +50,7 @@
|
|||||||
"@vercel/redwood": "1.0.44",
|
"@vercel/redwood": "1.0.44",
|
||||||
"@vercel/remix": "1.1.6",
|
"@vercel/remix": "1.1.6",
|
||||||
"@vercel/ruby": "1.3.49",
|
"@vercel/ruby": "1.3.49",
|
||||||
"@vercel/static-build": "1.1.0",
|
"@vercel/static-build": "1.1.0"
|
||||||
"update-notifier": "5.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@alex_neo/jest-expect-message": "1.0.5",
|
"@alex_neo/jest-expect-message": "1.0.5",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import cpy from 'cpy';
|
import cpy from 'cpy';
|
||||||
import execa from 'execa';
|
import execa from 'execa';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { remove, writeFile } from 'fs-extra';
|
import { remove, readJSON, writeFile } from 'fs-extra';
|
||||||
|
|
||||||
const dirRoot = join(__dirname, '..');
|
const dirRoot = join(__dirname, '..');
|
||||||
const distRoot = join(dirRoot, 'dist');
|
const distRoot = join(dirRoot, 'dist');
|
||||||
@@ -43,15 +43,15 @@ async function main() {
|
|||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pkg = await readJSON(join(dirRoot, 'package.json'));
|
||||||
|
const dependencies = Object.keys(pkg?.dependencies ?? {});
|
||||||
// Do the initial `ncc` build
|
// Do the initial `ncc` build
|
||||||
console.log();
|
console.log('Dependencies:', dependencies);
|
||||||
const args = [
|
const externs = [];
|
||||||
'ncc',
|
for (const dep of dependencies) {
|
||||||
'build',
|
externs.push('--external', dep);
|
||||||
'--external',
|
}
|
||||||
'update-notifier',
|
const args = ['ncc', 'build', 'src/index.ts', ...externs];
|
||||||
'src/index.ts',
|
|
||||||
];
|
|
||||||
await execa('yarn', args, { stdio: 'inherit', cwd: dirRoot });
|
await execa('yarn', args, { stdio: 'inherit', cwd: dirRoot });
|
||||||
|
|
||||||
// `ncc` has some issues with `@vercel/fun`'s runtime files:
|
// `ncc` has some issues with `@vercel/fun`'s runtime files:
|
||||||
@@ -78,6 +78,10 @@ async function main() {
|
|||||||
// Band-aid to bundle stuff that `ncc` neglects to bundle
|
// Band-aid to bundle stuff that `ncc` neglects to bundle
|
||||||
await cpy(join(dirRoot, 'src/util/projects/VERCEL_DIR_README.txt'), distRoot);
|
await cpy(join(dirRoot, 'src/util/projects/VERCEL_DIR_README.txt'), distRoot);
|
||||||
await cpy(join(dirRoot, 'src/util/dev/builder-worker.js'), distRoot);
|
await cpy(join(dirRoot, 'src/util/dev/builder-worker.js'), distRoot);
|
||||||
|
await cpy(
|
||||||
|
join(dirRoot, 'src/util/get-latest-version/get-latest-worker.js'),
|
||||||
|
distRoot
|
||||||
|
);
|
||||||
|
|
||||||
console.log('Finished building Vercel CLI');
|
console.log('Finished building Vercel CLI');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import sourceMap from '@zeit/source-map-support';
|
|||||||
import { mkdirp } from 'fs-extra';
|
import { mkdirp } from 'fs-extra';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import epipebomb from 'epipebomb';
|
import epipebomb from 'epipebomb';
|
||||||
import updateNotifier from 'update-notifier';
|
import getLatestVersion from './util/get-latest-version';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import hp from './util/humanize-path';
|
import hp from './util/humanize-path';
|
||||||
@@ -55,13 +55,6 @@ import { VercelConfig } from '@vercel/client';
|
|||||||
|
|
||||||
const isCanary = pkg.version.includes('canary');
|
const isCanary = pkg.version.includes('canary');
|
||||||
|
|
||||||
// Checks for available update and returns an instance
|
|
||||||
const notifier = updateNotifier({
|
|
||||||
pkg,
|
|
||||||
distTag: isCanary ? 'canary' : 'latest',
|
|
||||||
updateCheckInterval: 1000 * 60 * 60 * 24 * 7, // 1 week
|
|
||||||
});
|
|
||||||
|
|
||||||
const VERCEL_DIR = getGlobalPathConfig();
|
const VERCEL_DIR = getGlobalPathConfig();
|
||||||
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
|
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
|
||||||
const VERCEL_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath();
|
const VERCEL_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath();
|
||||||
@@ -149,23 +142,27 @@ const main = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print update information, if available
|
// Print update information, if available
|
||||||
if (notifier.update && notifier.update.latest !== pkg.version && isTTY) {
|
if (isTTY && !process.env.NO_UPDATE_NOTIFIER) {
|
||||||
const { latest } = notifier.update;
|
// Check if an update is available. If so, `latest` will contain a string
|
||||||
console.log(
|
// of the latest version, otherwise `undefined`.
|
||||||
info(
|
const latest = getLatestVersion({
|
||||||
|
distTag: isCanary ? 'canary' : 'latest',
|
||||||
|
output,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
if (latest) {
|
||||||
|
output.log(
|
||||||
`${chalk.black.bgCyan('UPDATE AVAILABLE')} ` +
|
`${chalk.black.bgCyan('UPDATE AVAILABLE')} ` +
|
||||||
`Run ${cmd(
|
`Run ${cmd(
|
||||||
await getUpdateCommand()
|
await getUpdateCommand()
|
||||||
)} to install ${getTitleName()} CLI ${latest}`
|
)} to install ${getTitleName()} CLI ${latest}`
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
output.log(
|
||||||
info(
|
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${latest}\n`
|
||||||
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${latest}`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The second argument to the command can be:
|
// The second argument to the command can be:
|
||||||
//
|
//
|
||||||
|
|||||||
223
packages/cli/src/util/get-latest-version/get-latest-worker.js
Normal file
223
packages/cli/src/util/get-latest-version/get-latest-worker.js
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
/**
|
||||||
|
* This file is spawned in the background and checks npm for the latest version
|
||||||
|
* of the CLI, then writes the version to the cache file.
|
||||||
|
*
|
||||||
|
* NOTE: Since this file runs asynchronously in the background, it's possible
|
||||||
|
* for multiple instances of this file to be running at the same time leading
|
||||||
|
* to a race condition where the most recent instance will overwrite the
|
||||||
|
* previous cache file resetting the `notified` flag and cause the update
|
||||||
|
* notification to appear for multiple consequetive commands. Not the end of
|
||||||
|
* the world, but something to be aware of.
|
||||||
|
*
|
||||||
|
* IMPORTANT! This file must NOT depend on any 3rd party dependencies. This
|
||||||
|
* file is NOT bundled by `ncc` and thus any 3rd party dependencies will never
|
||||||
|
* be available.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const https = require('https');
|
||||||
|
const { mkdirSync, writeFileSync } = require('fs');
|
||||||
|
const { access, mkdir, readFile, unlink, writeFile } = require('fs/promises');
|
||||||
|
const path = require('path');
|
||||||
|
const { format, inspect } = require('util');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An simple output helper which accumulates error and debug log messages in
|
||||||
|
* memory for potential persistance to disk while immediately outputting errors
|
||||||
|
* and debug messages, when the `--debug` flag is set, to `stderr`.
|
||||||
|
*/
|
||||||
|
class WorkerOutput {
|
||||||
|
debugLog = [];
|
||||||
|
logFile = null;
|
||||||
|
|
||||||
|
constructor({ debug = true }) {
|
||||||
|
this.debugOutputEnabled = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(...args) {
|
||||||
|
this.print('debug', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(...args) {
|
||||||
|
this.print('error', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(type, args) {
|
||||||
|
const str = format(
|
||||||
|
...args.map(s => (typeof s === 'string' ? s : inspect(s)))
|
||||||
|
);
|
||||||
|
this.debugLog.push(`[${new Date().toISOString()}] [${type}] ${str}`);
|
||||||
|
if (type === 'debug' && this.debugOutputEnabled) {
|
||||||
|
console.error(`> '[debug] [${new Date().toISOString()}] ${str}`);
|
||||||
|
} else if (type === 'error') {
|
||||||
|
console.error(`Error: ${str}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLogFile(file) {
|
||||||
|
// wire up the exit handler the first time the log file is set
|
||||||
|
if (this.logFile === null) {
|
||||||
|
process.on('exit', () => {
|
||||||
|
if (this.debugLog.length) {
|
||||||
|
mkdirSync(path.dirname(this.logFile), { recursive: true });
|
||||||
|
writeFileSync(this.logFile, this.debugLog.join('\n'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.logFile = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = new WorkerOutput({
|
||||||
|
// enable the debug logging if the `--debug` is set or if this worker script
|
||||||
|
// was directly executed
|
||||||
|
debug: process.argv.includes('--debug') || !process.connected,
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('unhandledRejection', err => {
|
||||||
|
output.error('Exiting worker due to unhandled rejection:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// this timer will prevent this worker process from running longer than 10s
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
output.error('Worker timed out after 10 seconds');
|
||||||
|
process.exit(1);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
// wait for the parent to give us the work payload
|
||||||
|
process.once('message', async msg => {
|
||||||
|
output.debug('Received message from parent:', msg);
|
||||||
|
|
||||||
|
output.debug('Disconnecting from parent');
|
||||||
|
process.disconnect();
|
||||||
|
|
||||||
|
const { cacheFile, distTag, name, updateCheckInterval } = msg;
|
||||||
|
const cacheFileParsed = path.parse(cacheFile);
|
||||||
|
await mkdir(cacheFileParsed.dir, { recursive: true });
|
||||||
|
|
||||||
|
output.setLogFile(
|
||||||
|
path.join(cacheFileParsed.dir, `${cacheFileParsed.name}.log`)
|
||||||
|
);
|
||||||
|
|
||||||
|
const lockFile = path.join(
|
||||||
|
cacheFileParsed.dir,
|
||||||
|
`${cacheFileParsed.name}.lock`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// check for a lock file and either bail if running or write our pid and continue
|
||||||
|
output.debug(`Checking lock file: ${lockFile}`);
|
||||||
|
if (await isRunning(lockFile)) {
|
||||||
|
output.debug('Worker already running, exiting');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
output.debug(`Initializing lock file with pid ${process.pid}`);
|
||||||
|
await writeFile(lockFile, String(process.pid), 'utf-8');
|
||||||
|
|
||||||
|
// fetch the latest version from npm
|
||||||
|
const agent = new https.Agent({
|
||||||
|
keepAlive: true,
|
||||||
|
maxSockets: 15, // See: `npm config get maxsockets`
|
||||||
|
});
|
||||||
|
const headers = {
|
||||||
|
accept:
|
||||||
|
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
|
||||||
|
};
|
||||||
|
const url = `https://registry.npmjs.org/-/package/${name}/dist-tags`;
|
||||||
|
output.debug(`Fetching ${url}`);
|
||||||
|
|
||||||
|
const tags = await new Promise((resolve, reject) => {
|
||||||
|
const req = https.get(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
agent,
|
||||||
|
headers,
|
||||||
|
},
|
||||||
|
res => {
|
||||||
|
let buf = '';
|
||||||
|
res.on('data', chunk => {
|
||||||
|
buf += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(buf));
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
req.on('error', reject);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
const version = tags[distTag];
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
output.debug(`Found dist tag "${distTag}" with version "${version}"`);
|
||||||
|
} else {
|
||||||
|
output.error(`Dist tag "${distTag}" not found`);
|
||||||
|
output.debug('Available dist tags:', Object.keys(tags));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.debug(`Writing cache file: ${cacheFile}`);
|
||||||
|
await writeFile(
|
||||||
|
cacheFile,
|
||||||
|
JSON.stringify({
|
||||||
|
expireAt: Date.now() + updateCheckInterval,
|
||||||
|
notified: false,
|
||||||
|
version,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
output.error(`Failed to get package info:`, err);
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timer);
|
||||||
|
|
||||||
|
if (await fileExists(lockFile)) {
|
||||||
|
output.debug(`Releasing lock file: ${lockFile}`);
|
||||||
|
await unlink(lockFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.debug(`Worker finished successfully!`);
|
||||||
|
|
||||||
|
// force the worker to exit
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// signal the parent process we're ready
|
||||||
|
if (process.connected) {
|
||||||
|
output.debug("Notifying parent we're ready");
|
||||||
|
process.send({ type: 'ready' });
|
||||||
|
} else {
|
||||||
|
console.error('No IPC bridge detected, exiting');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fileExists(file) {
|
||||||
|
return access(file)
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isRunning(lockFile) {
|
||||||
|
try {
|
||||||
|
const pid = parseInt(await readFile(lockFile, 'utf-8'));
|
||||||
|
output.debug(`Found lock file with pid: ${pid}`);
|
||||||
|
|
||||||
|
// checks for existence of a process; throws if not found
|
||||||
|
process.kill(pid, 0);
|
||||||
|
|
||||||
|
// process is still running
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
if (await fileExists(lockFile)) {
|
||||||
|
// lock file does not exist or process is not running and pid is stale
|
||||||
|
output.debug(`Resetting lock file: ${err.toString()}`);
|
||||||
|
await unlink(lockFile);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
151
packages/cli/src/util/get-latest-version/index.ts
Normal file
151
packages/cli/src/util/get-latest-version/index.ts
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import semver from 'semver';
|
||||||
|
import XDGAppPaths from 'xdg-app-paths';
|
||||||
|
import { dirname, parse as parsePath, resolve as resolvePath } from 'path';
|
||||||
|
import type { Output } from '../output';
|
||||||
|
import { existsSync, outputJSONSync, readJSONSync } from 'fs-extra';
|
||||||
|
import type { PackageJson } from '@vercel/build-utils';
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
|
||||||
|
interface GetLatestVersionOptions {
|
||||||
|
cacheDir?: string;
|
||||||
|
distTag?: string;
|
||||||
|
output?: Output;
|
||||||
|
pkg: PackageJson;
|
||||||
|
updateCheckInterval?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PackageInfoCache {
|
||||||
|
version: string;
|
||||||
|
expireAt: number;
|
||||||
|
notified: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetLatestWorkerPayload {
|
||||||
|
cacheFile?: string;
|
||||||
|
distTag?: string;
|
||||||
|
updateCheckInterval?: number;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if it needs to check for a newer CLI version and returns the last
|
||||||
|
* detected version. The version could be stale, but still newer than the
|
||||||
|
* current version.
|
||||||
|
*
|
||||||
|
* @returns {String|undefined} If a newer version is found, then the lastest
|
||||||
|
* version, otherwise `undefined`.
|
||||||
|
*/
|
||||||
|
export default function getLatestVersion({
|
||||||
|
cacheDir = XDGAppPaths('com.vercel.cli').cache(),
|
||||||
|
distTag = 'latest',
|
||||||
|
output,
|
||||||
|
pkg,
|
||||||
|
updateCheckInterval = 1000 * 60 * 60 * 24 * 7, // 1 week
|
||||||
|
}: GetLatestVersionOptions): string | undefined {
|
||||||
|
if (
|
||||||
|
!pkg ||
|
||||||
|
typeof pkg !== 'object' ||
|
||||||
|
!pkg.name ||
|
||||||
|
typeof pkg.name !== 'string'
|
||||||
|
) {
|
||||||
|
throw new TypeError('Expected package to be an object with a package name');
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheFile = resolvePath(
|
||||||
|
cacheDir,
|
||||||
|
'package-updates',
|
||||||
|
`${pkg.name}-${distTag}.json`
|
||||||
|
);
|
||||||
|
|
||||||
|
let cache: PackageInfoCache | undefined;
|
||||||
|
try {
|
||||||
|
cache = readJSONSync(cacheFile);
|
||||||
|
} catch (err: any) {
|
||||||
|
// cache does not exist or malformed
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
output?.debug(`Error reading latest package cache file: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cache || cache.expireAt < Date.now()) {
|
||||||
|
spawnWorker(
|
||||||
|
{
|
||||||
|
cacheFile,
|
||||||
|
distTag,
|
||||||
|
updateCheckInterval,
|
||||||
|
name: pkg.name,
|
||||||
|
},
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
cache &&
|
||||||
|
!cache.notified &&
|
||||||
|
pkg.version &&
|
||||||
|
semver.lt(pkg.version, cache.version)
|
||||||
|
) {
|
||||||
|
cache.notified = true;
|
||||||
|
outputJSONSync(cacheFile, cache);
|
||||||
|
return cache.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn the worker, wait for the worker to report it's ready, then signal the
|
||||||
|
* worker to fetch the latest version.
|
||||||
|
*/
|
||||||
|
function spawnWorker(
|
||||||
|
payload: GetLatestWorkerPayload,
|
||||||
|
output: Output | undefined
|
||||||
|
) {
|
||||||
|
// we need to find the update worker script since the location is
|
||||||
|
// different based on production vs tests
|
||||||
|
let dir = dirname(__filename);
|
||||||
|
let script = resolvePath(dir, 'dist', 'get-latest-worker.js');
|
||||||
|
const { root } = parsePath(dir);
|
||||||
|
while (!existsSync(script)) {
|
||||||
|
dir = dirname(dir);
|
||||||
|
if (dir === root) {
|
||||||
|
// didn't find it, bail
|
||||||
|
output?.debug('Failed to find the get latest worker script!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
script = resolvePath(dir, 'dist', 'get-latest-worker.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
// spawn the worker with an IPC channel
|
||||||
|
output?.debug(`Spawning ${script}`);
|
||||||
|
const args = [script];
|
||||||
|
if (output?.debugEnabled) {
|
||||||
|
args.push('--debug');
|
||||||
|
}
|
||||||
|
const worker = spawn(process.execPath, args, {
|
||||||
|
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
||||||
|
windowsHide: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// we allow the child 2 seconds to let us know it's ready before we give up
|
||||||
|
const workerReadyTimer = setTimeout(() => worker.kill(), 2000);
|
||||||
|
|
||||||
|
// listen for an early on close error, but then we remove it when unref
|
||||||
|
const onClose = (code: number) => {
|
||||||
|
output?.debug(`Get latest worker exited (code ${code})`);
|
||||||
|
};
|
||||||
|
worker.on('close', onClose);
|
||||||
|
|
||||||
|
// generally, the parent won't be around long enough to handle a non-zero
|
||||||
|
// worker process exit code
|
||||||
|
worker.on('error', err => {
|
||||||
|
output?.log(`Failed to spawn get latest worker: ${err.stack}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for the worker to start and notify us it is ready
|
||||||
|
worker.once('message', () => {
|
||||||
|
clearTimeout(workerReadyTimer);
|
||||||
|
|
||||||
|
worker.removeListener('close', onClose);
|
||||||
|
worker.send(payload);
|
||||||
|
worker.unref();
|
||||||
|
});
|
||||||
|
}
|
||||||
140
packages/cli/test/unit/util/get-latest-version.test.ts
Normal file
140
packages/cli/test/unit/util/get-latest-version.test.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import fs from 'fs-extra';
|
||||||
|
import sleep from '../../../src/util/sleep';
|
||||||
|
import tmp from 'tmp-promise';
|
||||||
|
import getLatestVersion from '../../../src/util/get-latest-version';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
tmp.setGracefulCleanup();
|
||||||
|
|
||||||
|
const cacheDir = tmp.tmpNameSync({
|
||||||
|
prefix: 'test-vercel-cli-get-latest-version-',
|
||||||
|
});
|
||||||
|
|
||||||
|
const cacheFile = join(cacheDir, 'package-updates', 'vercel-latest.json');
|
||||||
|
|
||||||
|
const pkg = {
|
||||||
|
name: 'vercel',
|
||||||
|
version: '27.3.0',
|
||||||
|
};
|
||||||
|
|
||||||
|
const versionRE = /^\d+\.\d+\.\d+$/;
|
||||||
|
|
||||||
|
describe('get latest version', () => {
|
||||||
|
afterEach(() => fs.remove(cacheDir));
|
||||||
|
|
||||||
|
it('should find newer version async', async () => {
|
||||||
|
// 1. first call, no cache file
|
||||||
|
let latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
expect(latest).toEqual(undefined);
|
||||||
|
|
||||||
|
await waitForCacheFile();
|
||||||
|
|
||||||
|
let cache = await fs.readJSON(cacheFile);
|
||||||
|
expect(typeof cache).toEqual('object');
|
||||||
|
expect(typeof cache.expireAt).toEqual('number');
|
||||||
|
expect(cache.expireAt).toBeGreaterThan(Date.now());
|
||||||
|
expect(typeof cache.version).toEqual('string');
|
||||||
|
expect(cache.version).toEqual(expect.stringMatching(versionRE));
|
||||||
|
expect(cache.notified).toEqual(false);
|
||||||
|
|
||||||
|
// 2. call again and this time it'll return the version from the cache
|
||||||
|
latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
expect(typeof latest).toBe('string');
|
||||||
|
expect(latest).toEqual(expect.stringMatching(versionRE));
|
||||||
|
|
||||||
|
cache = await fs.readJSON(cacheFile);
|
||||||
|
expect(cache.version).toEqual(expect.stringMatching(versionRE));
|
||||||
|
expect(cache.notified).toEqual(true);
|
||||||
|
|
||||||
|
// 3. notification already done, should skip
|
||||||
|
latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
expect(latest).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not find a newer version', async () => {
|
||||||
|
// 1. first call, no cache file
|
||||||
|
let latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
updateCheckInterval: 1,
|
||||||
|
pkg: {
|
||||||
|
...pkg,
|
||||||
|
version: '999.0.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(latest).toEqual(undefined);
|
||||||
|
|
||||||
|
await waitForCacheFile();
|
||||||
|
|
||||||
|
// 2. call again and should recheck and still not find a new version
|
||||||
|
latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
updateCheckInterval: 1,
|
||||||
|
pkg: {
|
||||||
|
...pkg,
|
||||||
|
version: '999.0.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(latest).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not check twice', async () => {
|
||||||
|
// 1. first call, no cache file
|
||||||
|
let latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
updateCheckInterval: 1,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
expect(latest).toEqual(undefined);
|
||||||
|
|
||||||
|
// 2. immediately call again, but should hopefully still be undefined
|
||||||
|
latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
updateCheckInterval: 1,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
expect(latest).toEqual(undefined);
|
||||||
|
|
||||||
|
await waitForCacheFile();
|
||||||
|
|
||||||
|
// 3. call again and should recheck and find a new version
|
||||||
|
latest = getLatestVersion({
|
||||||
|
cacheDir,
|
||||||
|
updateCheckInterval: 1,
|
||||||
|
pkg,
|
||||||
|
});
|
||||||
|
expect(typeof latest).toBe('string');
|
||||||
|
expect(latest).toEqual(expect.stringMatching(versionRE));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if no arguments are passed in', () => {
|
||||||
|
expect(() => getLatestVersion(undefined as any)).toThrow(TypeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error package is invalid', () => {
|
||||||
|
expect(() => getLatestVersion({} as any)).toThrow(TypeError);
|
||||||
|
expect(() => getLatestVersion({ pkg: null as any })).toThrow(TypeError);
|
||||||
|
expect(() => getLatestVersion({ pkg: {} })).toThrow(TypeError);
|
||||||
|
expect(() => getLatestVersion({ pkg: { name: null as any } })).toThrow(
|
||||||
|
TypeError
|
||||||
|
);
|
||||||
|
expect(() => getLatestVersion({ pkg: { name: '' } })).toThrow(TypeError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function waitForCacheFile() {
|
||||||
|
for (let i = 0; i < 40; i++) {
|
||||||
|
await sleep(100);
|
||||||
|
if (await fs.pathExists(cacheFile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
131
yarn.lock
131
yarn.lock
@@ -4248,20 +4248,6 @@ boxen@^3.0.0:
|
|||||||
type-fest "^0.3.0"
|
type-fest "^0.3.0"
|
||||||
widest-line "^2.0.0"
|
widest-line "^2.0.0"
|
||||||
|
|
||||||
boxen@^5.0.0:
|
|
||||||
version "5.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
|
|
||||||
integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==
|
|
||||||
dependencies:
|
|
||||||
ansi-align "^3.0.0"
|
|
||||||
camelcase "^6.2.0"
|
|
||||||
chalk "^4.1.0"
|
|
||||||
cli-boxes "^2.2.1"
|
|
||||||
string-width "^4.2.2"
|
|
||||||
type-fest "^0.20.2"
|
|
||||||
widest-line "^3.1.0"
|
|
||||||
wrap-ansi "^7.0.0"
|
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
@@ -4612,14 +4598,6 @@ chalk@^4.0.0:
|
|||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chalk@^4.1.0:
|
|
||||||
version "4.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
|
||||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
|
||||||
dependencies:
|
|
||||||
ansi-styles "^4.1.0"
|
|
||||||
supports-color "^7.1.0"
|
|
||||||
|
|
||||||
chance@1.1.7:
|
chance@1.1.7:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.7.tgz#e99dde5ac16681af787b5ba94c8277c090d6cfe8"
|
resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.7.tgz#e99dde5ac16681af787b5ba94c8277c090d6cfe8"
|
||||||
@@ -4764,11 +4742,6 @@ cli-boxes@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d"
|
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d"
|
||||||
integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
|
integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
|
||||||
|
|
||||||
cli-boxes@^2.2.1:
|
|
||||||
version "2.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
|
|
||||||
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
|
|
||||||
|
|
||||||
cli-cursor@^2.0.0, cli-cursor@^2.1.0:
|
cli-cursor@^2.0.0, cli-cursor@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
||||||
@@ -5022,18 +4995,6 @@ configstore@^4.0.0:
|
|||||||
write-file-atomic "^2.0.0"
|
write-file-atomic "^2.0.0"
|
||||||
xdg-basedir "^3.0.0"
|
xdg-basedir "^3.0.0"
|
||||||
|
|
||||||
configstore@^5.0.1:
|
|
||||||
version "5.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
|
|
||||||
integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==
|
|
||||||
dependencies:
|
|
||||||
dot-prop "^5.2.0"
|
|
||||||
graceful-fs "^4.1.2"
|
|
||||||
make-dir "^3.0.0"
|
|
||||||
unique-string "^2.0.0"
|
|
||||||
write-file-atomic "^3.0.0"
|
|
||||||
xdg-basedir "^4.0.0"
|
|
||||||
|
|
||||||
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||||
@@ -5283,11 +5244,6 @@ crypto-random-string@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
|
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
|
||||||
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
|
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
|
||||||
|
|
||||||
crypto-random-string@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
|
||||||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
|
||||||
|
|
||||||
css-select@^4.3.0:
|
css-select@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
|
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
|
||||||
@@ -5675,7 +5631,7 @@ dot-prop@^4.1.0, dot-prop@^4.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-obj "^1.0.0"
|
is-obj "^1.0.0"
|
||||||
|
|
||||||
dot-prop@^5.1.0, dot-prop@^5.2.0:
|
dot-prop@^5.1.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
|
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
|
||||||
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
|
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
|
||||||
@@ -6042,11 +5998,6 @@ escalade@^3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||||
|
|
||||||
escape-goat@^2.0.0:
|
|
||||||
version "2.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
|
|
||||||
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
|
|
||||||
|
|
||||||
escape-html@1.0.3, escape-html@~1.0.3:
|
escape-html@1.0.3, escape-html@~1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||||
@@ -7126,13 +7077,6 @@ global-dirs@^0.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ini "^1.3.4"
|
ini "^1.3.4"
|
||||||
|
|
||||||
global-dirs@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
|
|
||||||
integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==
|
|
||||||
dependencies:
|
|
||||||
ini "2.0.0"
|
|
||||||
|
|
||||||
globals@^11.1.0:
|
globals@^11.1.0:
|
||||||
version "11.12.0"
|
version "11.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||||
@@ -7616,11 +7560,6 @@ inherits@2.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||||
|
|
||||||
ini@2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
|
|
||||||
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
|
|
||||||
|
|
||||||
ini@3.0.0:
|
ini@3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.0.tgz#2f6de95006923aa75feed8894f5686165adc08f1"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.0.tgz#2f6de95006923aa75feed8894f5686165adc08f1"
|
||||||
@@ -7891,24 +7830,11 @@ is-installed-globally@^0.1.0:
|
|||||||
global-dirs "^0.1.0"
|
global-dirs "^0.1.0"
|
||||||
is-path-inside "^1.0.0"
|
is-path-inside "^1.0.0"
|
||||||
|
|
||||||
is-installed-globally@^0.4.0:
|
|
||||||
version "0.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
|
|
||||||
integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
|
|
||||||
dependencies:
|
|
||||||
global-dirs "^3.0.0"
|
|
||||||
is-path-inside "^3.0.2"
|
|
||||||
|
|
||||||
is-npm@^3.0.0:
|
is-npm@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-3.0.0.tgz#ec9147bfb629c43f494cf67936a961edec7e8053"
|
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-3.0.0.tgz#ec9147bfb629c43f494cf67936a961edec7e8053"
|
||||||
integrity sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==
|
integrity sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==
|
||||||
|
|
||||||
is-npm@^5.0.0:
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8"
|
|
||||||
integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==
|
|
||||||
|
|
||||||
is-number@^3.0.0:
|
is-number@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
||||||
@@ -7974,11 +7900,6 @@ is-path-inside@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017"
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017"
|
||||||
integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==
|
integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==
|
||||||
|
|
||||||
is-path-inside@^3.0.2:
|
|
||||||
version "3.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
|
||||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
|
||||||
|
|
||||||
is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
|
is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||||
@@ -9190,7 +9111,7 @@ kleur@^4.1.5:
|
|||||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
|
||||||
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
||||||
|
|
||||||
latest-version@^5.0.0, latest-version@^5.1.0:
|
latest-version@^5.0.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
|
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
|
||||||
integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
|
integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
|
||||||
@@ -11272,13 +11193,6 @@ punycode@^2.1.0, punycode@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
pupa@^2.1.1:
|
|
||||||
version "2.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62"
|
|
||||||
integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==
|
|
||||||
dependencies:
|
|
||||||
escape-goat "^2.0.0"
|
|
||||||
|
|
||||||
q@^1.5.1:
|
q@^1.5.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||||
@@ -11865,13 +11779,6 @@ semver-diff@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver "^5.0.3"
|
semver "^5.0.3"
|
||||||
|
|
||||||
semver-diff@^3.1.1:
|
|
||||||
version "3.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
|
|
||||||
integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==
|
|
||||||
dependencies:
|
|
||||||
semver "^6.3.0"
|
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
|
"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
|
||||||
version "5.7.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
@@ -12376,7 +12283,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
|
|||||||
is-fullwidth-code-point "^3.0.0"
|
is-fullwidth-code-point "^3.0.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
string-width@^4.2.2, string-width@^4.2.3:
|
string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@@ -13354,13 +13261,6 @@ unique-string@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
crypto-random-string "^1.0.0"
|
crypto-random-string "^1.0.0"
|
||||||
|
|
||||||
unique-string@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
|
|
||||||
integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==
|
|
||||||
dependencies:
|
|
||||||
crypto-random-string "^2.0.0"
|
|
||||||
|
|
||||||
unique-temp-dir@^1.0.0:
|
unique-temp-dir@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz#6dce95b2681ca003eebfb304a415f9cbabcc5385"
|
resolved "https://registry.yarnpkg.com/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz#6dce95b2681ca003eebfb304a415f9cbabcc5385"
|
||||||
@@ -13416,26 +13316,6 @@ unset-value@^1.0.0:
|
|||||||
has-value "^0.3.1"
|
has-value "^0.3.1"
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
|
|
||||||
update-notifier@5.1.0:
|
|
||||||
version "5.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9"
|
|
||||||
integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==
|
|
||||||
dependencies:
|
|
||||||
boxen "^5.0.0"
|
|
||||||
chalk "^4.1.0"
|
|
||||||
configstore "^5.0.1"
|
|
||||||
has-yarn "^2.1.0"
|
|
||||||
import-lazy "^2.1.0"
|
|
||||||
is-ci "^2.0.0"
|
|
||||||
is-installed-globally "^0.4.0"
|
|
||||||
is-npm "^5.0.0"
|
|
||||||
is-yarn-global "^0.3.0"
|
|
||||||
latest-version "^5.1.0"
|
|
||||||
pupa "^2.1.1"
|
|
||||||
semver "^7.3.4"
|
|
||||||
semver-diff "^3.1.1"
|
|
||||||
xdg-basedir "^4.0.0"
|
|
||||||
|
|
||||||
update-notifier@^3.0.1:
|
update-notifier@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-3.0.1.tgz#78ecb68b915e2fd1be9f767f6e298ce87b736250"
|
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-3.0.1.tgz#78ecb68b915e2fd1be9f767f6e298ce87b736250"
|
||||||
@@ -13820,11 +13700,6 @@ xdg-basedir@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||||
|
|
||||||
xdg-basedir@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
|
||||||
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
|
|
||||||
|
|
||||||
xdg-portable@^7.0.0:
|
xdg-portable@^7.0.0:
|
||||||
version "7.2.1"
|
version "7.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/xdg-portable/-/xdg-portable-7.2.1.tgz#4301ba0868b2cbc9de0c53b3699906adcc9d2560"
|
resolved "https://registry.yarnpkg.com/xdg-portable/-/xdg-portable-7.2.1.tgz#4301ba0868b2cbc9de0c53b3699906adcc9d2560"
|
||||||
|
|||||||
Reference in New Issue
Block a user