diff --git a/@types/cwordin-api.d.ts b/@types/cwordin-api.d.ts new file mode 100644 index 0000000..0ebb860 --- /dev/null +++ b/@types/cwordin-api.d.ts @@ -0,0 +1,102 @@ +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; + } + + 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; + } + + 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; + } + + export interface LanguageStatusResponse { + files: Array; + } + + type FilesList = Record; + + export default class CrowdinApi { + constructor(params: { + apiKey: string; + projectName: string; + baseUrl?: string; + }); + projectInfo(): Promise; + languageStatus(language: string): Promise; + exportFile( + file: string, + language: string, + params?: { + branch?: string; + format?: 'xliff'; + export_translated_only?: boolean; + export_approved_only?: boolean; + }, + ): Promise; // TODO: not sure about Promise return type + updateFile( + files: FilesList, + params: { + titles?: Record; + export_patterns?: Record; + new_names?: Record; + first_line_contains_header?: string; + scheme?: string; + update_option?: 'update_as_unapproved' | 'update_without_changes'; + branch?: string; + }, + ): Promise; + } +} diff --git a/@types/multi-progress.d.ts b/@types/multi-progress.d.ts new file mode 100644 index 0000000..b3ab5e5 --- /dev/null +++ b/@types/multi-progress.d.ts @@ -0,0 +1,13 @@ +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; + } +} diff --git a/@types/prompt.d.ts b/@types/prompt.d.ts new file mode 100644 index 0000000..83bd868 --- /dev/null +++ b/@types/prompt.d.ts @@ -0,0 +1,66 @@ +// Type definitions for Prompt.js 1.0.0 +// Project: https://github.com/flatiron/prompt + +declare module 'prompt' { + type PropertiesType = + | Array + | prompt.PromptSchema + | Array; + + 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( + properties: T, + callback: ( + err: Error, + result: T extends Array + ? Record + : T extends PromptSchema + ? Record + : T extends Array + ? 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; +} diff --git a/package.json b/package.json index 10e15d9..e6960cc 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ "ts:check": "tsc", "ci:check": "yarn lint:check && yarn ts:check && yarn test", "analyze": "yarn run clean && yarn run build:webpack --analyze", - "i18n:collect": "babel-node ./packages/scripts/i18n-collect.js", - "i18n:push": "babel-node ./packages/scripts/i18n-crowdin.js push", - "i18n:pull": "babel-node ./packages/scripts/i18n-crowdin.js pull", + "i18n:collect": "babel-node --extensions \".ts\" ./packages/scripts/i18n-collect.ts", + "i18n:push": "babel-node --extensions \".ts\" ./packages/scripts/i18n-crowdin.ts push", + "i18n:pull": "babel-node --extensions \".ts\" ./packages/scripts/i18n-crowdin.ts pull", "build": "yarn run clean && yarn run build:webpack", "build:install": "yarn install", "build:webpack": "NODE_ENV=production webpack --colors -p --bail", diff --git a/packages/app/package.json b/packages/app/package.json index b1d408f..6a2e9c4 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -35,6 +35,9 @@ "whatwg-fetch": "^3.0.0" }, "devDependencies": { + "@types/debounce": "^1.2.0", + "@types/intl": "^1.2.0", + "@types/raf": "^3.4.0", "@types/react-helmet": "^5.0.15", "@types/webpack-env": "^1.15.0", "enzyme": "^3.8.0", diff --git a/packages/app/services/captcha.ts b/packages/app/services/captcha.ts index f160be3..b9cc450 100644 --- a/packages/app/services/captcha.ts +++ b/packages/app/services/captcha.ts @@ -3,7 +3,7 @@ import options from 'app/services/api/options'; let readyPromise: Promise; let lang = 'en'; -let sitekey; +let sitekey: string; export type CaptchaID = string; diff --git a/packages/app/services/localStorage.js b/packages/app/services/localStorage.ts similarity index 79% rename from packages/app/services/localStorage.js rename to packages/app/services/localStorage.ts index dc60659..faa8a89 100644 --- a/packages/app/services/localStorage.js +++ b/packages/app/services/localStorage.ts @@ -17,16 +17,17 @@ export function hasStorage() { return _hasStorage; } -class DummyStorage { - getItem(key) { +// TODO: work on +class DummyStorage implements Storage { + getItem(key: string): string | null { return this[key] || null; } - setItem(key, value) { + setItem(key: string, value: string): void { this[key] = value; } - removeItem(key) { + removeItem(key: string): void { Reflect.deleteProperty(this, key); } } diff --git a/packages/scripts/i18n-collect.ts b/packages/scripts/i18n-collect.ts index 50579b8..7cf6815 100644 --- a/packages/scripts/i18n-collect.ts +++ b/packages/scripts/i18n-collect.ts @@ -12,19 +12,27 @@ const LANG_DIR = `${__dirname}/../app/i18n`; const DEFAULT_LOCALE = 'en'; const SUPPORTED_LANGS = [DEFAULT_LOCALE, ...Object.keys(localesMap)]; +interface MessageDescriptor { + id: string | number; + defaultMessage: string; +} + /** * Aggregates the default messages that were extracted from the app's * React components via the React Intl Babel plugin. An error will be thrown if * there are messages in different components that use the same `id`. The result * is a flat collection of `id: message` pairs for the app's default locale. */ -let idToFileMap: { [key: string]: string[] } = {}; -let duplicateIds: string[] = []; +let idToFileMap: Record> = {}; +let duplicateIds: Array = []; const collectedMessages = globSync(MESSAGES_PATTERN) - .map(filename => [filename, JSON.parse(fs.readFileSync(filename, 'utf8'))]) - .reduce((collection, [file, descriptors]) => { + .map<[string, Array]>(filename => [ + filename, + JSON.parse(fs.readFileSync(filename, 'utf8')), + ]) + .reduce>((collection, [file, descriptors]) => { descriptors.forEach(({ id, defaultMessage }) => { - if (collection.hasOwnProperty(id)) { + if (collection[id]) { duplicateIds.push(id); } @@ -52,13 +60,9 @@ idToFileMap = {}; * Making a diff with the previous DEFAULT_LOCALE version */ const defaultMessagesPath = `${LANG_DIR}/${DEFAULT_LOCALE}.json`; -let keysToUpdate: string[] = []; -let keysToAdd: string[] = []; -let keysToRemove: string[] = []; -const keysToRename: Array<[string, string]> = []; const isNotMarked = (value: string) => value.slice(0, 2) !== '--'; -const prevMessages: { [key: string]: string } = readJSON(defaultMessagesPath); +const prevMessages = readJSON>(defaultMessagesPath); const prevMessagesMap = Object.entries(prevMessages).reduce( (acc, [key, value]) => { if (acc[value]) { @@ -69,21 +73,24 @@ const prevMessagesMap = Object.entries(prevMessages).reduce( return acc; }, - {} as { [key: string]: string[] }, + {} as Record>, ); -keysToAdd = Object.keys(collectedMessages).filter(key => !prevMessages[key]); -keysToRemove = Object.keys(prevMessages) +const keysToAdd = Object.keys(collectedMessages).filter( + key => !prevMessages[key], +); +const keysToRemove: Array = Object.keys(prevMessages) .filter(key => !collectedMessages[key]) .filter(isNotMarked); -keysToUpdate = Object.entries(prevMessages).reduce( +const keysToUpdate: Array = Object.entries(prevMessages).reduce( (acc, [key, message]) => acc.concat( collectedMessages[key] && collectedMessages[key] !== message ? key : [], ), - [] as string[], + [] as Array, ); +const keysToRename: Array<[string, string]> = []; // detect keys to rename, mutating keysToAdd and keysToRemove [...keysToAdd].forEach(toKey => { const keys = prevMessagesMap[collectedMessages[toKey]] || []; @@ -91,7 +98,6 @@ keysToUpdate = Object.entries(prevMessages).reduce( if (fromKey) { keysToRename.push([fromKey, toKey]); - keysToRemove.splice(keysToRemove.indexOf(fromKey), 1); keysToAdd.splice(keysToAdd.indexOf(toKey), 1); } @@ -178,7 +184,7 @@ function buildLocales() { SUPPORTED_LANGS.map(lang => { const destPath = `${LANG_DIR}/${lang}.json`; - const newMessages = readJSON(destPath); + const newMessages = readJSON>(destPath); keysToRename.forEach(([fromKey, toKey]) => { newMessages[toKey] = newMessages[fromKey]; @@ -195,18 +201,23 @@ function buildLocales() { newMessages[key] = collectedMessages[key]; }); - const sortedKeys = Object.keys(newMessages).sort((key1, key2) => { - key1 = key1.replace(/^-+/, ''); - key2 = key2.replace(/^-+/, ''); + const sortedKeys: Array = Object.keys(newMessages).sort( + (key1, key2) => { + key1 = key1.replace(/^-+/, ''); + key2 = key2.replace(/^-+/, ''); - return key1 < key2 || !isNotMarked(key1) ? -1 : 1; - }); + return key1 < key2 || !isNotMarked(key1) ? -1 : 1; + }, + ); - const sortedNewMessages = sortedKeys.reduce((acc, key) => { - acc[key] = newMessages[key]; + const sortedNewMessages = sortedKeys.reduce( + (acc, key) => { + acc[key] = newMessages[key]; - return acc; - }, {}); + return acc; + }, + {}, + ); fs.writeFileSync( destPath, @@ -215,15 +226,15 @@ function buildLocales() { }); } -function readJSON(destPath) { +function readJSON(destPath: string): T { try { return JSON.parse(fs.readFileSync(destPath, 'utf8')); } catch (err) { console.log( - chalk.yellow(`Can not read ${destPath}. The new file will be created.`), + chalk.yellow(`Can't read ${destPath}. The new file will be created.`), `(${err.message})`, ); } - return {}; + return {} as T; } diff --git a/packages/scripts/i18n-crowdin.ts b/packages/scripts/i18n-crowdin.ts index 928f842..a8c8f9b 100644 --- a/packages/scripts/i18n-crowdin.ts +++ b/packages/scripts/i18n-crowdin.ts @@ -1,14 +1,16 @@ /* eslint-env node */ -/* eslint-disable no-console */ +/* eslint-disable */ import fs from 'fs'; import path from 'path'; -import CrowdinApi from 'crowdin-api'; +import CrowdinApi, { LanguageStatusNode, LanguageStatusResponse, ProjectInfoResponse } from 'crowdin-api'; import MultiProgress from 'multi-progress'; import ch from 'chalk'; import iso639 from 'iso-639-1'; import prompt from 'prompt'; +import { ValuesType } from 'utility-types'; + import config from '../../config'; if (!config.crowdinApiKey) { @@ -24,18 +26,21 @@ const LANG_DIR = path.resolve(`${__dirname}/../app/i18n`); const INDEX_FILE_NAME = 'index.js'; const MIN_RELEASE_PROGRESS = 80; // Minimal ready percent before translation can be published -const crowdin = new CrowdinApi({ apiKey: PROJECT_KEY }); +const crowdin = new CrowdinApi({ + apiKey: PROJECT_KEY, + projectName: PROJECT_ID, +}); const progressBar = new MultiProgress(); /** * Locales that has been verified by core team members */ -const RELEASED_LOCALES = ['be', 'fr', 'id', 'pt', 'ru', 'uk', 'vi', 'zh']; +const RELEASED_LOCALES: Array = ['be', 'fr', 'id', 'pt', 'ru', 'uk', 'vi', 'zh']; /** * Array of Crowdin locales to our internal locales representation */ -const LOCALES_MAP = { +const LOCALES_MAP: Record = { 'pt-BR': 'pt', 'zh-CN': 'zh', }; @@ -43,7 +48,7 @@ const LOCALES_MAP = { /** * This array allows us to customise native languages names, because ISO-639-1 sometimes is strange */ -const NATIVE_NAMES_MAP = { +const NATIVE_NAMES_MAP: Record = { be: 'Беларуская', id: 'Bahasa Indonesia', lt: 'Lietuvių', @@ -57,7 +62,7 @@ const NATIVE_NAMES_MAP = { /** * This arrays allows us to override Crowdin English languages names */ -const ENGLISH_NAMES_MAP = { +const ENGLISH_NAMES_MAP: Record = { pt: 'Portuguese, Brazilian', sr: 'Serbian', zh: 'Simplified Chinese', @@ -65,9 +70,6 @@ const ENGLISH_NAMES_MAP = { /** * Converts Crowdin's language code to our internal value - * - * @param {string} code - * @returns {string} */ function toInternalLocale(code: string): string { return LOCALES_MAP[code] || code; @@ -76,108 +78,33 @@ function toInternalLocale(code: string): string { /** * Форматирует входящий объект с переводами в итоговую строку в том формате, в каком они * хранятся в самом приложении - * - * @param {object} translates - * @returns {string} */ -function serializeToFormattedJson( - translates: { [key: string]: any }, - { asModule = false }: { asModule?: boolean } = {}, -): string { +function serializeToModule(translates: Record): string { const src = JSON.stringify(sortByKeys(translates), null, 2); - return asModule ? `module.exports = ${src};\n` : `${src}\n`; + return `module.exports = ${src};\n`; } -/** - * http://stackoverflow.com/a/29622653/5184751 - * - * @param {object} object - * @returns {object} - */ -function sortByKeys(object: { [key: string]: any }): { [key: string]: any } { +// http://stackoverflow.com/a/29622653/5184751 +function sortByKeys>(object: T): T { return Object.keys(object) .sort() .reduce((result, key) => { + // @ts-ignore result[key] = object[key]; return result; - }, {}); + }, {} as T); } -interface ProjectInfoFile { - node_type: 'file'; - id: number; - name: string; - created: string; - last_updated: string; - last_accessed: string; - last_revision: string; -} - -interface ProjectInfoDirectory { - node_type: 'directory'; - id: number; - name: string; - files: Array; -} - -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; -} - -async function pullLocales() { - const { languages }: ProjectInfoResponse = await crowdin.projectInfo( - PROJECT_ID, - ); +async function pullLocales(): Promise { + const { languages } = await crowdin.projectInfo(); return languages; } -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; -} - function findFile( - root: Array, + root: LanguageStatusResponse['files'], path: string, ): LanguageStatusNode | null { const [nodeToSearch, ...rest] = path.split('/'); @@ -229,14 +156,18 @@ async function pull() { ); let downloadingTotal = 0; let downloadingReady = 0; + + interface Result { + locale: ValuesType, + progress: number, + translatesFilePath: string, + } + const results = await Promise.all( - locales.map(async locale => { - const { - files, - }: { files: Array } = await crowdin.languageStatus( - PROJECT_ID, - locale.code, - ); + // TODO: there is should be some way to reimplement this + // with reduce to avoid null values + locales.map(async (locale): Promise => { + const { files } = await crowdin.languageStatus(locale.code); checkingProgressBar.tick(); const fileInfo = findFile(files, CROWDIN_FILE_PATH); @@ -261,7 +192,6 @@ async function pull() { }); const translatesFilePath = await crowdin.exportFile( - PROJECT_ID, CROWDIN_FILE_PATH, locale.code, ); @@ -291,47 +221,40 @@ async function pull() { }, }; await Promise.all( - results.map( - result => - new Promise((resolve, reject) => { - if (result === null) { - resolve(); + 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); - return; - } + indexFileEntries[ourCode] = { + code: ourCode, + name: NATIVE_NAMES_MAP[ourCode] || iso639.getNativeName(ourCode), + englishName: ENGLISH_NAMES_MAP[ourCode] || name, + progress: parseFloat(progress.toFixed(1)), + isReleased: RELEASED_LOCALES.includes(ourCode), + }; - 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: RELEASED_LOCALES.includes(ourCode), - }; - - fs.copyFile( - translatesFilePath, - path.join(LANG_DIR, `${ourCode}.json`), - 0, - err => { - err ? reject(err) : resolve(); - }, - ); - }), - ), + 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), - serializeToFormattedJson(indexFileEntries, { asModule: true }), + serializeToModule(indexFileEntries), ); console.log(ch.green('The index file was successfully written')); @@ -362,12 +285,10 @@ function push() { console.log(`Publishing ${ch.bold(SOURCE_LANG)} translates file...`); await crowdin.updateFile( - PROJECT_ID, { [CROWDIN_FILE_PATH]: path.join(LANG_DIR, `${SOURCE_LANG}.json`), }, { - // eslint-disable-next-line @typescript-eslint/camelcase update_option: disapprove ? 'update_as_unapproved' : 'update_without_changes', diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 6d49004..3cd128a 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -11,12 +11,15 @@ "license": "ISC", "dependencies": { "@babel/node": "^7.8.3", + "@types/mkdirp": "^0.5.2", + "@types/progress": "^2.0.3", "chalk": "^3.0.0", - "crowdin-api": "erickskrauch/crowdin-api#add_missed_methods_and_fix_files_uploading", + "crowdin-api": "^4.0.0", "glob": "^7.1.6", "iso-639-1": "^2.1.0", "mkdirp": "^0.5.1", "multi-progress": "^2.0.0", - "prompt": "^1.0.0" + "prompt": "^1.0.0", + "utility-types": "^3.10.0" } } diff --git a/yarn.lock b/yarn.lock index 41509ec..df18eb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2787,6 +2787,11 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/debounce@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/debounce/-/debounce-1.2.0.tgz#9ee99259f41018c640b3929e1bb32c3dcecdb192" + integrity sha512-bWG5wapaWgbss9E238T0R6bfo5Fh3OkeoSt245CM7JJwVwpw6MEBCbIxLq5z8KzsE3uJhzcIuQkyiZmzV3M/Dw== + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -2819,6 +2824,11 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/intl@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/intl/-/intl-1.2.0.tgz#1245511f13064402087979f498764611a3c758fc" + integrity sha512-BP+KwmOvD9AR5aoxnbyyPr3fAtpjEI/bVImHsotmpuC43+z0NAmjJ9cQbX7vPCq8XcvCeAVc8E3KSQPYNaPsUQ== + "@types/invariant@^2.2.31": version "2.2.31" resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.31.tgz#4444c03004f215289dbca3856538434317dd28b2" @@ -2866,6 +2876,13 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/mkdirp@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" + integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@^12.0.0": version "12.12.24" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.24.tgz#d4606afd8cf6c609036b854360367d1b2c78931f" @@ -2876,6 +2893,13 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/progress@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/progress/-/progress-2.0.3.tgz#7ccbd9c6d4d601319126c469e73b5bb90dfc8ccc" + integrity sha512-bPOsfCZ4tsTlKiBjBhKnM8jpY5nmIll166IPD58D92hR7G7kZDfx5iB9wGF4NfZrdKolebjeAr3GouYkSGoJ/A== + dependencies: + "@types/node" "*" + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -2886,6 +2910,11 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== +"@types/raf@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2" + integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw== + "@types/reach__router@^1.2.3": version "1.2.6" resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.2.6.tgz#b14cf1adbd1a365d204bbf6605cd9dd7b8816c87" @@ -4156,7 +4185,7 @@ bluebird@3.7.1: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== -bluebird@^3.3.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5: +bluebird@^3.3.5, bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -5252,14 +5281,15 @@ cross-spawn@^7.0.0: shebang-command "^2.0.0" which "^2.0.1" -crowdin-api@erickskrauch/crowdin-api#add_missed_methods_and_fix_files_uploading: - version "2.0.3" - resolved "https://codeload.github.com/erickskrauch/crowdin-api/tar.gz/acf088542aff16e903290ebf43866494fa07e58d" +crowdin-api@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/crowdin-api/-/crowdin-api-4.0.0.tgz#faa1d0af62e97fcbdf94d7a0830db5f5ddbdb0e6" + integrity sha512-NEUMrtEvxhNjiBp68EEm0t4PGQaBxxUlKSQHy3GlgjepQffd2bxnBTx2+8LkB9wzClu2+euVV/MStQG7++tkVw== dependencies: - bluebird "^3.5.1" + bluebird "^3.5.3" request "^2.88.0" - request-promise "^4.2.2" - temp "^0.8.3" + request-promise "^4.2.4" + temp "^0.9.0" crypto-browserify@^3.11.0: version "3.12.0" @@ -12661,7 +12691,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request-promise@^4.2.2: +request-promise@^4.2.4: version "4.2.5" resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.5.tgz#186222c59ae512f3497dfe4d75a9c8461bd0053c" integrity sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg== @@ -14083,10 +14113,10 @@ telejson@^3.2.0: lodash "^4.17.15" memoizerific "^1.11.3" -temp@^0.8.3: - version "0.8.4" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" - integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== +temp@^0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.1.tgz#2d666114fafa26966cd4065996d7ceedd4dd4697" + integrity sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA== dependencies: rimraf "~2.6.2" @@ -14721,6 +14751,11 @@ utile@0.3.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"