Cleanup the crowdin integration script, completely migrate it to API v2

This commit is contained in:
ErickSkrauch 2020-05-27 13:07:25 +03:00
parent e47eaf720f
commit 5e62c930b1
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
8 changed files with 161 additions and 489 deletions

View File

@ -1,98 +0,0 @@
declare module 'crowdin-api' {
export interface ProjectInfoFile {
node_type: 'file';
id: number;
name: string;
created: string;
last_updated: string;
last_accessed: string;
last_revision: string;
}
export interface ProjectInfoDirectory {
node_type: 'directory';
id: number;
name: string;
files: Array<ProjectInfoFile | ProjectInfoDirectory>;
}
export interface ProjectInfoResponse {
details: {
source_language: {
name: string;
code: string;
};
name: string;
identifier: string;
created: string;
description: string;
join_policy: string;
last_build: string | null;
last_activity: string;
participants_count: string; // it's number, but string in the response
logo_url: string | null;
total_strings_count: string; // it's number, but string in the response
total_words_count: string; // it's number, but string in the response
duplicate_strings_count: number;
duplicate_words_count: number;
invite_url: {
translator: string;
proofreader: string;
};
};
languages: Array<{
name: string; // English language name
code: string;
can_translate: 0 | 1;
can_approve: 0 | 1;
}>;
files: Array<ProjectInfoFile | ProjectInfoDirectory>;
}
export interface LanguageStatusNode {
node_type: 'directory' | 'file';
id: number;
name: string;
phrases: number;
translated: number;
approved: number;
words: number;
words_translated: number;
words_approved: number;
files: Array<LanguageStatusNode>;
}
export interface LanguageStatusResponse {
files: Array<LanguageStatusNode>;
}
type FilesList = Record<string, string | ReadableStream>;
export default class CrowdinApi {
constructor(params: { apiKey: string; projectName: string; baseUrl?: string });
projectInfo(): Promise<ProjectInfoResponse>;
languageStatus(language: string): Promise<LanguageStatusResponse>;
exportFile(
file: string,
language: string,
params?: {
branch?: string;
format?: 'xliff';
export_translated_only?: boolean;
export_approved_only?: boolean;
},
): Promise<string>; // TODO: not sure about Promise return type
updateFile(
files: FilesList,
params: {
titles?: Record<string, string>;
export_patterns?: Record<string, string>;
new_names?: Record<string, string>;
first_line_contains_header?: string;
scheme?: string;
update_option?: 'update_as_unapproved' | 'update_without_changes';
branch?: string;
},
): Promise<void>;
}
}

View File

@ -1,10 +0,0 @@
declare module 'multi-progress' {
export default class MultiProgress {
constructor(stream?: string);
newBar(schema: string, options: ProgressBar.ProgressBarOptions): ProgressBar;
terminate(): void;
move(index: number): void;
tick(index: number, value?: number, options?: any): void;
update(index: number, value?: number, options?: any): void;
}
}

56
@types/prompt.d.ts vendored
View File

@ -1,56 +0,0 @@
// Type definitions for Prompt.js 1.0.0
// Project: https://github.com/flatiron/prompt
declare module 'prompt' {
type PropertiesType = Array<string> | prompt.PromptSchema | Array<prompt.PromptPropertyOptions>;
namespace prompt {
interface PromptSchema {
properties: PromptProperties;
}
interface PromptProperties {
[propName: string]: PromptPropertyOptions;
}
interface PromptPropertyOptions {
name?: string;
pattern?: RegExp;
message?: string;
required?: boolean;
hidden?: boolean;
description?: string;
type?: string;
default?: string;
before?: (value: any) => any;
conform?: (result: any) => boolean;
}
export function start(): void;
export function get<T extends PropertiesType>(
properties: T,
callback: (
err: Error,
result: T extends Array<string>
? Record<T[number], string>
: T extends PromptSchema
? Record<keyof T['properties'], string>
: T extends Array<PromptPropertyOptions>
? Record<T[number]['name'] extends string ? T[number]['name'] : number, string>
: never,
) => void,
): void;
export function addProperties(obj: any, properties: PropertiesType, callback: (err: Error) => void): void;
export function history(propertyName: string): any;
export let override: any;
export let colors: boolean;
export let message: string;
export let delimiter: string;
}
export = prompt;
}

View File

@ -8,7 +8,16 @@ module.exports = {
},
],
'@babel/preset-react',
'@babel/preset-env',
[
'@babel/preset-env',
{
ignoreBrowserslistConfig: true,
targets: {
node: true,
},
modules: 'commonjs',
},
],
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
@ -29,6 +38,7 @@ module.exports = {
[
'@babel/preset-env',
{
ignoreBrowserslistConfig: false,
modules: false,
useBuiltIns: 'usage', // or "entry"
corejs: 3,

View File

@ -1,4 +1,7 @@
/* eslint-env node */
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
require('dotenv').config();
@ -9,5 +12,12 @@ module.exports = {
apiHost: env.API_HOST || 'https://dev.account.ely.by',
ga: env.GA_ID && { id: env.GA_ID },
sentryDSN: env.SENTRY_DSN,
crowdinApiKey: env.CROWDIN_API_KEY,
crowdin: {
apiKey: env.CROWDIN_API_KEY,
projectId: 350687,
filePath: 'accounts/site.json',
sourceLang: 'en',
basePath: path.resolve(`${__dirname}/packages/app/i18n`),
minApproved: 80, // Minimal ready percent before translation can be published
},
};

View File

@ -3,53 +3,40 @@
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import JSON5 from 'json5';
import CrowdinApi, { LanguageStatusNode, LanguageStatusResponse, ProjectInfoResponse } from 'crowdin-api';
import { TranslationStatus, Translations, Credentials } from '@crowdin/crowdin-api-client';
import MultiProgress from 'multi-progress';
import Crowdin, { SourceFilesModel } from '@crowdin/crowdin-api-client';
import ProgressBar from 'progress';
import ch from 'chalk';
import iso639 from 'iso-639-1';
import prompt from 'prompt';
import { prompt } from 'inquirer';
import { ValuesType } from 'utility-types';
import config from './../../config';
import config from '../../config';
if (!config.crowdinApiKey) {
if (!config.crowdin.apiKey) {
console.error(ch.red`crowdinApiKey is required`);
process.exit(126);
}
const ORGANIZATION_ID = 'elyby';
const PROJECT_ID = 350687;
const FILE_ID = 6;
const PROJECT_KEY = config.crowdinApiKey;
const CROWDIN_FILE_PATH = 'accounts/site.json';
const SOURCE_LANG = 'en';
const LANG_DIR = path.resolve(`${__dirname}/../app/i18n`);
const PROJECT_ID = config.crowdin.projectId;
const CROWDIN_FILE_PATH = config.crowdin.filePath;
const SOURCE_LANG = config.crowdin.sourceLang;
const LANG_DIR = config.crowdin.basePath;
const INDEX_FILE_NAME = 'index.js';
const MIN_RELEASE_PROGRESS = 80; // Minimal ready percent before translation can be published
const MIN_RELEASE_PROGRESS = config.crowdin.minApproved;
const credentials: Credentials = {
token: config.crowdinApiKey,
};
const translationStatusApi = new TranslationStatus(credentials);
const translationsApi = new Translations(credentials);
const crowdin = new CrowdinApi({
apiKey: PROJECT_KEY,
projectName: ORGANIZATION_ID,
const crowdin = new Crowdin({
token: config.crowdin.apiKey,
});
const progressBar = new MultiProgress();
/**
* Locales that has been verified by core team members
*/
const releasedLocales: Array<string> = ['be', 'fr', 'id', 'pt', 'ru', 'uk', 'vi', 'zh'];
const releasedLocales: ReadonlyArray<string> = ['be', 'fr', 'id', 'pt', 'ru', 'uk', 'vi', 'zh'];
/**
* Array of Crowdin locales to our internal locales representation
* Map Crowdin locales into our internal locales representation
*/
const LOCALES_MAP: Record<string, string> = {
'pt-BR': 'pt',
@ -57,7 +44,8 @@ const LOCALES_MAP: Record<string, string> = {
};
/**
* This array allows us to customise native languages names, because ISO-639-1 sometimes is strange
* This array allows us to customise native languages names,
* because ISO-639-1 sometimes is strange
*/
const NATIVE_NAMES_MAP: Record<string, string> = {
be: 'Беларуская',
@ -108,30 +96,6 @@ function sortByKeys<T extends Record<string, any>>(object: T): T {
}, {} as T);
}
async function pullLocales(): Promise<ProjectInfoResponse['languages']> {
const { languages } = await crowdin.projectInfo();
return languages;
}
function findFile(root: LanguageStatusResponse['files'], path: string): LanguageStatusNode | null {
const [nodeToSearch, ...rest] = path.split('/');
for (const node of root) {
if (node.name !== nodeToSearch) {
continue;
}
if (rest.length === 0) {
return node;
}
return findFile(node.files, rest.join('/'));
}
return null;
}
interface IndexFileEntry {
code: string;
name: string;
@ -140,9 +104,48 @@ interface IndexFileEntry {
isReleased: boolean;
}
async function pullNew(): Promise<void> {
function getLocaleFilePath(languageId: string): string {
return path.join(LANG_DIR, `${toInternalLocale(languageId)}.json`);
}
let directoriesList: Array<SourceFilesModel.Directory>;
let filesList: Array<SourceFilesModel.File>;
async function findFileId(path: string, parentDir: number|null = null): Promise<number> {
const [nodeToSearch, ...rest] = path.split('/');
if (rest.length === 0) {
if (!filesList) {
const { data: filesResponse } = await crowdin.sourceFilesApi.listProjectFiles(PROJECT_ID);
filesList = filesResponse.map((fileData) => fileData.data);
}
const file = filesList.find((file) => file.directoryId === parentDir && file.name === nodeToSearch);
if (file === undefined) {
throw new Error('Cannot find file by provided path');
}
return file.id;
}
if (!directoriesList) {
const { data: dirsResponse } = await crowdin.sourceFilesApi.listProjectDirectories(PROJECT_ID);
directoriesList = dirsResponse.map((dirData) => dirData.data);
}
const dir = directoriesList.find((dir) => dir.directoryId === parentDir && dir.name === nodeToSearch);
if (dir === undefined) {
throw new Error('Cannot find directory by provided path');
}
return findFileId(rest.join('/'), dir.id);
}
async function pull(): Promise<void> {
console.log('Loading file info...');
const fileId = await findFileId(CROWDIN_FILE_PATH);
console.log('Pulling translation progress...');
const { data: translationProgress } = await translationStatusApi.getFileProgress(PROJECT_ID, FILE_ID, 100);
const { data: translationProgress } = await crowdin.translationStatusApi.getFileProgress(PROJECT_ID, fileId, 100);
const localesToPull: Array<string> = [];
const indexFileEntries: Record<string, IndexFileEntry> = {
@ -170,7 +173,7 @@ async function pullNew(): Promise<void> {
});
// Add prefix 'c' to current and total to prevent filling thees placeholders with real values
const downloadingProgressBar = progressBar.newBar('Downloading translates :bar :percent | :cCurrent/:total', {
const downloadingProgressBar = new ProgressBar('Downloading translates :bar :percent | :cCurrent/:total', {
total: localesToPull.length,
incomplete: '\u2591',
complete: '\u2588',
@ -179,18 +182,18 @@ async function pullNew(): Promise<void> {
let downloadingReady = 0;
const promises = localesToPull.map(async (languageId): Promise<void> => {
const { data: { url } } = await translationsApi.buildProjectFileTranslation(PROJECT_ID, FILE_ID, {
const { data: { url } } = await crowdin.translationsApi.buildProjectFileTranslation(PROJECT_ID, fileId, {
targetLanguageId: languageId,
exportApprovedOnly: true,
});
const fileResponse = await axios.get(url, {
const { data: fileContents } = await axios.get(url, {
// Disable response parsing
transformResponse: [],
});
fs.writeFileSync(path.join(LANG_DIR, `${toInternalLocale(languageId)}.json`), fileResponse.data);
fs.writeFileSync(getLocaleFilePath(languageId), fileContents);
downloadingProgressBar.update(++downloadingReady / localesToPull, {
downloadingProgressBar.update(++downloadingReady / localesToPull.length, {
cCurrent: downloadingReady,
});
});
@ -204,156 +207,32 @@ async function pullNew(): Promise<void> {
console.log(ch.green('The index file was successfully written'));
}
async function pull() {
console.log('Pulling locales list...');
const locales = await pullLocales();
const checkingProgressBar = progressBar.newBar('| Pulling locales info :bar :percent | :current/:total', {
total: locales.length,
incomplete: '\u2591',
complete: '\u2588',
width: locales.length,
});
// Add prefix 'c' to current and total to prevent filling thees placeholders with real values
const downloadingProgressBar = progressBar.newBar('| Downloading translates :bar :percent | :cCurrent/:cTotal', {
total: 100,
incomplete: '\u2591',
complete: '\u2588',
width: locales.length,
});
let downloadingTotal = 0;
let downloadingReady = 0;
async function push(): Promise<void> {
const { disapproveTranslates } = await prompt([{
name: 'disapproveTranslates',
type: 'confirm',
default: true,
message: 'Disapprove changed lines?',
}]);
interface Result {
locale: ValuesType<typeof locales>;
progress: number;
translatesFilePath: string;
}
console.log('Loading file info...');
const fileId = await findFileId(CROWDIN_FILE_PATH);
const results = await Promise.all(
// TODO: there is should be some way to reimplement this
// with reduce to avoid null values
locales.map(
async (locale): Promise<Result | null> => {
const { files } = await crowdin.languageStatus(locale.code);
checkingProgressBar.tick();
const fileInfo = findFile(files, CROWDIN_FILE_PATH);
if (fileInfo === null) {
throw new Error('Unable to find translation file. Please check the CROWDIN_FILE_PATH param.');
}
const progress = (fileInfo.words_approved / fileInfo.words) * 100;
if (!releasedLocales.includes(toInternalLocale(locale.code)) && progress < MIN_RELEASE_PROGRESS) {
return null;
}
downloadingProgressBar.update(downloadingReady / ++downloadingTotal, {
cCurrent: downloadingReady,
cTotal: downloadingTotal,
});
const translatesFilePath = await crowdin.exportFile(CROWDIN_FILE_PATH, locale.code);
downloadingProgressBar.update(++downloadingReady / downloadingTotal, {
cCurrent: downloadingReady,
cTotal: downloadingTotal,
});
return {
locale,
progress,
translatesFilePath,
};
},
),
console.log('Uploading the source file to the storage...')
const { data: { id: storageId } } = await crowdin.uploadStorageApi.addStorage(
path.basename(CROWDIN_FILE_PATH),
fs.readFileSync(getLocaleFilePath(SOURCE_LANG)),
);
console.log('Locales are downloaded. Writing them to file system.');
const indexFileEntries: Record<string, IndexFileEntry> = {
en: {
code: 'en',
name: 'English',
englishName: 'English',
progress: 100,
isReleased: true,
},
};
await Promise.all(
results
.filter((result): result is Result => result !== null)
.map(
(result) =>
new Promise((resolve, reject) => {
const {
locale: { code, name },
progress,
translatesFilePath,
} = result;
const ourCode = toInternalLocale(code);
indexFileEntries[ourCode] = {
code: ourCode,
name: NATIVE_NAMES_MAP[ourCode] || iso639.getNativeName(ourCode),
englishName: ENGLISH_NAMES_MAP[ourCode] || name,
progress: parseFloat(progress.toFixed(1)),
isReleased: releasedLocales.includes(ourCode),
};
fs.copyFile(translatesFilePath, path.join(LANG_DIR, `${ourCode}.json`), 0, (err) => {
err ? reject(err) : resolve();
});
}),
),
);
console.log('Writing an index file.');
fs.writeFileSync(path.join(LANG_DIR, INDEX_FILE_NAME), serializeToModule(indexFileEntries));
console.log(ch.green('The index file was successfully written'));
}
function push() {
return new Promise((resolve, reject) => {
prompt.start();
prompt.get(
{
properties: {
disapprove: {
description: 'Disapprove changed lines? [Y/n]',
pattern: /^y|n$/i,
message: 'Please enter "y" or "n"',
default: 'y',
before: (value) => value.toLowerCase() === 'y',
},
},
},
async (err, { disapprove }) => {
if (err) {
reject(err);
return;
}
console.log(`Publishing ${ch.bold(SOURCE_LANG)} translates file...`);
await crowdin.updateFile(
{
[CROWDIN_FILE_PATH]: path.join(LANG_DIR, `${SOURCE_LANG}.json`),
},
{
update_option: disapprove ? 'update_as_unapproved' : 'update_without_changes',
},
);
console.log(ch.green('Success'));
resolve();
},
);
console.log(`Applying the new revision...`);
await crowdin.sourceFilesApi.updateOrRestoreFile(PROJECT_ID, fileId, {
storageId,
updateOption: disapproveTranslates
? SourceFilesModel.UpdateOption.CLEAR_TRANSLATIONS_AND_APPROVALS
: SourceFilesModel.UpdateOption.KEEP_TRANSLATIONS_AND_APPROVALS,
});
console.log(ch.green('Success'));
}
try {
@ -361,7 +240,7 @@ try {
switch (action) {
case 'pull':
pullNew();
pull();
break;
case 'push':
push();

View File

@ -12,20 +12,21 @@
"dependencies": {
"@babel/node": "^7.8.3",
"@crowdin/crowdin-api-client": "^1.8.0",
"@types/mkdirp": "^1.0.0",
"@types/progress": "^2.0.3",
"axios": "^0.19.2",
"chalk": "^4.0.0",
"crowdin-api": "^4.0.0",
"glob": "^7.1.6",
"inquirer": "^7.1.0",
"iso-639-1": "^2.1.3",
"json5": "^2.1.3",
"mkdirp": "^1.0.4",
"multi-progress": "^2.0.0",
"prompt": "https://github.com/flatiron/prompt.git#master",
"utility-types": "^3.10.0"
"progress": "^2.0.3"
},
"devDependencies": {
"@types/inquirer": "^6.5.0",
"@types/json5": "^0.0.30",
"@types/mkdirp": "^1.0.0",
"@types/progress": "^2.0.3",
"@types/webpack": "^4.41.13"
}
}

160
yarn.lock
View File

@ -3040,6 +3040,14 @@
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880"
integrity sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==
"@types/inquirer@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-6.5.0.tgz#b83b0bf30b88b8be7246d40e51d32fe9d10e09be"
integrity sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==
dependencies:
"@types/through" "*"
rxjs "^6.4.0"
"@types/intl@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@types/intl/-/intl-1.2.0.tgz#1245511f13064402087979f498764611a3c758fc"
@ -3093,6 +3101,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
"@types/json5@^0.0.30":
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818"
integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==
"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@ -3332,6 +3345,13 @@
"@types/testing-library__dom" "*"
pretty-format "^25.1.0"
"@types/through@*":
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==
dependencies:
"@types/node" "*"
"@types/uglify-js@*":
version "3.9.0"
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.0.tgz#4490a140ca82aa855ad68093829e7fd6ae94ea87"
@ -4244,16 +4264,6 @@ async@~0.2.6:
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
async@~0.9.0:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=
async@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -5576,11 +5586,6 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.2"
colors@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
colors@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
@ -6201,11 +6206,6 @@ currently-unhandled@^0.4.1:
dependencies:
array-find-index "^1.0.1"
cycle@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI=
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@ -6349,11 +6349,6 @@ deep-equal@^1.0.1, deep-equal@^1.1.1:
object-keys "^1.1.1"
regexp.prototype.flags "^1.2.0"
deep-equal@~0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d"
integrity sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=
deep-is@^0.1.3, deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@ -7402,11 +7397,6 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
eyes@0.1.x:
version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=
fake-tag@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fake-tag/-/fake-tag-1.0.1.tgz#1d59da482240a02bd83500ca98976530ed154b0d"
@ -8613,11 +8603,6 @@ human-signals@^1.1.1:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
i@0.3.x:
version "0.3.6"
resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d"
integrity sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -8821,6 +8806,25 @@ inquirer@^7.0.0:
strip-ansi "^5.1.0"
through "^2.3.6"
inquirer@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==
dependencies:
ansi-escapes "^4.2.1"
chalk "^3.0.0"
cli-cursor "^3.1.0"
cli-width "^2.0.0"
external-editor "^3.0.3"
figures "^3.0.0"
lodash "^4.17.15"
mute-stream "0.0.8"
run-async "^2.4.0"
rxjs "^6.5.3"
string-width "^4.1.0"
strip-ansi "^6.0.0"
through "^2.3.6"
internal-ip@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
@ -9387,7 +9391,7 @@ isomorphic-fetch@^2.1.1:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isstream@0.1.x, isstream@~0.1.2:
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
@ -10924,7 +10928,7 @@ mixin-object@^2.0.1:
for-in "^0.1.3"
is-extendable "^0.1.1"
mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@~0.5.1:
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
@ -10975,13 +10979,6 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
multi-progress@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/multi-progress/-/multi-progress-2.0.0.tgz#29ccb42cf24874b1c6384f03127ce5dff7b22f2c"
integrity sha1-Kcy0LPJIdLHGOE8DEnzl3/eyLyw=
dependencies:
progress "^1.1.8"
multicast-dns-service-types@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
@ -11000,7 +10997,7 @@ mute-stream@0.0.7:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
mute-stream@0.0.8, mute-stream@~0.0.4:
mute-stream@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
@ -11037,11 +11034,6 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
ncp@1.0.x:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246"
integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=
neatequal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/neatequal/-/neatequal-1.0.0.tgz#2ee1211bc9fa6e4c55715fd210bb05602eb1ae3b"
@ -12563,11 +12555,6 @@ progress-stream@^2.0.0:
speedometer "~1.0.0"
through2 "~2.0.3"
progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
progress@^2.0.0, progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
@ -12605,16 +12592,6 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
"prompt@https://github.com/flatiron/prompt.git#master":
version "1.0.0"
resolved "https://github.com/flatiron/prompt.git#8d5495c84c3f433b8f26ea2798f8ba68c3656459"
dependencies:
colors "^1.1.2"
read "1.0.x"
revalidator "0.1.x"
utile "0.3.x"
winston "2.x"
prompts@^2.0.1:
version "2.3.0"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.0.tgz#a444e968fa4cc7e86689a74050685ac8006c4cc4"
@ -13203,13 +13180,6 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
read@1.0.x:
version "1.0.7"
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=
dependencies:
mute-stream "~0.0.4"
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
@ -13694,11 +13664,6 @@ retry@0.12.0, retry@^0.12.0:
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
revalidator@0.1.x:
version "0.1.8"
resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b"
integrity sha1-/s5hv6DBtSoga9axgZgYS91SOjs=
rgb-regex@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
@ -13709,7 +13674,7 @@ rgba-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1:
rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@ -13750,6 +13715,11 @@ run-async@^2.2.0:
dependencies:
is-promise "^2.1.0"
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
run-queue@^1.0.0, run-queue@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
@ -14501,11 +14471,6 @@ stable@^0.1.8:
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
stack-utils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593"
@ -15687,23 +15652,6 @@ utila@^0.4.0, utila@~0.4:
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
utile@0.3.x:
version "0.3.0"
resolved "https://registry.yarnpkg.com/utile/-/utile-0.3.0.tgz#1352c340eb820e4d8ddba039a4fbfaa32ed4ef3a"
integrity sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=
dependencies:
async "~0.9.0"
deep-equal "~0.2.1"
i "0.3.x"
mkdirp "0.x.x"
ncp "1.0.x"
rimraf "2.x.x"
utility-types@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
@ -16203,18 +16151,6 @@ widest-line@^3.1.0:
dependencies:
string-width "^4.0.0"
winston@2.x:
version "2.4.4"
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.4.tgz#a01e4d1d0a103cf4eada6fc1f886b3110d71c34b"
integrity sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==
dependencies:
async "~1.0.0"
colors "1.0.x"
cycle "1.0.x"
eyes "0.1.x"
isstream "0.1.x"
stack-trace "0.0.x"
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"