diff --git a/_locales/en/messages.json b/_locales/en/messages.json index fda9a9410..b0a57a3dd 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -166,6 +166,11 @@ "description": "Only available on development modes, menu option to open up the standalone device setup sequence" }, + "contextMenuNoSuggestions": { + "message": "No Suggestions", + "description": + "Shown in the context menu for a misspelled word to indicate that there are no suggestions to replace the misspelled word" + }, "connectingLoad": { "message": "Connecting To Server", "description": @@ -1414,6 +1419,11 @@ "message": "Enable spell check of text entered in message composition box", "description": "Description of the media permission description" }, + "spellCheckDirty": { + "message": "You must restart Session to apply your new settings", + "description": + "Shown when the user changes their spellcheck setting to indicate that they must restart Signal." + }, "clearDataHeader": { "message": "Clear All Local Data", "description": diff --git a/app/spell_check.js b/app/spell_check.js new file mode 100644 index 000000000..695c171a6 --- /dev/null +++ b/app/spell_check.js @@ -0,0 +1,88 @@ +/* global exports, require */ +/* eslint-disable strict */ + +const { Menu } = require('electron'); +const osLocale = require('os-locale'); + +exports.setup = (browserWindow, messages) => { + const { session } = browserWindow.webContents; + const userLocale = osLocale.sync().replace(/_/g, '-'); + const userLocales = [userLocale, userLocale.split('-')[0]]; + + const available = session.availableSpellCheckerLanguages; + const languages = userLocales.filter(l => available.includes(l)); + console.log(`spellcheck: user locale: ${userLocale}`); + console.log('spellcheck: available spellchecker languages: ', available); + console.log('spellcheck: setting languages to: ', languages); + session.setSpellCheckerLanguages(languages); + + browserWindow.webContents.on('context-menu', (_event, params) => { + const { editFlags } = params; + const isMisspelled = Boolean(params.misspelledWord); + const showMenu = params.isEditable || editFlags.canCopy; + + // Popup editor menu + if (showMenu) { + const template = []; + + if (isMisspelled) { + if (params.dictionarySuggestions.length > 0) { + template.push( + ...params.dictionarySuggestions.map(label => ({ + label, + click: () => { + browserWindow.webContents.replaceMisspelling(label); + }, + })) + ); + } else { + template.push({ + label: messages.contextMenuNoSuggestions.message, + enabled: false, + }); + } + template.push({ type: 'separator' }); + } + + if (params.isEditable) { + if (editFlags.canUndo) { + template.push({ label: messages.editMenuUndo.message, role: 'undo' }); + } + // This is only ever `true` if undo was triggered via the context menu + // (not ctrl/cmd+z) + if (editFlags.canRedo) { + template.push({ label: messages.editMenuRedo.message, role: 'redo' }); + } + if (editFlags.canUndo || editFlags.canRedo) { + template.push({ type: 'separator' }); + } + if (editFlags.canCut) { + template.push({ label: messages.editMenuCut.message, role: 'cut' }); + } + } + + if (editFlags.canPaste) { + template.push({ label: messages.editMenuPaste.message, role: 'paste' }); + } + + if (editFlags.canPaste) { + template.push({ + label: messages.editMenuPasteAndMatchStyle.message, + role: 'pasteAndMatchStyle', + }); + } + + // Only enable select all in editors because select all in non-editors + // results in all the UI being selected + if (editFlags.canSelectAll && params.isEditable) { + template.push({ + label: messages.editMenuSelectAll.message, + role: 'selectall', + }); + } + + const menu = Menu.buildFromTemplate(template); + menu.popup(browserWindow); + } + }); +}; diff --git a/integration_test/common.js b/integration_test/common.js index 0a2daca5b..cda7f29e8 100644 --- a/integration_test/common.js +++ b/integration_test/common.js @@ -67,7 +67,6 @@ module.exports = { ELECTRON_ENABLE_STACK_DUMPING: true, ELECTRON_DISABLE_SANDBOX: 1, }, - startTimeout: 10000, requireName: 'electronRequire', // chromeDriverLogPath: '../chromedriverlog.txt', chromeDriverArgs: [ diff --git a/js/background.js b/js/background.js index 60b74b6ca..1b77e422a 100644 --- a/js/background.js +++ b/js/background.js @@ -320,7 +320,6 @@ getSpellCheck: () => storage.get('spell-check', true), setSpellCheck: value => { storage.put('spell-check', value); - startSpellCheck(); }, addDarkOverlay: () => { @@ -419,19 +418,6 @@ } }); - const startSpellCheck = () => { - if (!window.enableSpellCheck || !window.disableSpellCheck) { - return; - } - - if (window.Events.getSpellCheck()) { - window.enableSpellCheck(); - } else { - window.disableSpellCheck(); - } - }; - startSpellCheck(); - const themeSetting = window.Events.getThemeSetting(); const newThemeSetting = mapOldThemeToNew(themeSetting); window.Events.setThemeSetting(newThemeSetting); @@ -1037,8 +1023,16 @@ }; window.toggleSpellCheck = () => { - const newValue = !window.getSettingValue('spell-check'); + const currentValue = window.getSettingValue('spell-check'); + // if undefined, it means 'default' so true. but we have to toggle it, so false + // if not undefined, we take the opposite + const newValue = currentValue !== undefined ? !currentValue : false; window.Events.setSpellCheck(newValue); + window.pushToast({ + description: window.i18n('spellCheckDirty'), + type: 'info', + id: 'spellCheckDirty', + }); }; window.toggleLinkPreview = () => { diff --git a/js/spell_check.js b/js/spell_check.js deleted file mode 100644 index 1c01da51b..000000000 --- a/js/spell_check.js +++ /dev/null @@ -1,185 +0,0 @@ -/* global require, process, _ */ - -/* eslint-disable strict */ - -const electron = require('electron'); - -const Typo = require('typo-js'); -const fs = require('fs'); -const osLocale = require('os-locale'); -const path = require('path'); - -const { remote, webFrame } = electron; - -// `remote.require` since `Menu` is a main-process module. -const buildEditorContextMenu = remote.require('electron-editor-context-menu'); - -const EN_VARIANT = /^en/; - -// Prevent the spellchecker from showing contractions as errors. -const ENGLISH_SKIP_WORDS = [ - 'ain', - 'couldn', - 'didn', - 'doesn', - 'hadn', - 'hasn', - 'mightn', - 'mustn', - 'needn', - 'oughtn', - 'shan', - 'shouldn', - 'wasn', - 'weren', - 'wouldn', -]; - -function setupLinux(locale) { - if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { - // apt-get install hunspell- can be run for easy access - // to other dictionaries - const location = process.env.HUNSPELL_DICTIONARIES || '/usr/share/hunspell'; - const affDataPath = path.join(location, `${locale}.aff`); - const dicDataPath = path.join(location, `${locale}.dic`); - - window.log.info( - 'Detected Linux. Setting up spell check with locale', - locale, - 'and dictionary location', - location - ); - - if (fs.existsSync(affDataPath) && fs.existsSync(dicDataPath)) { - const affData = fs.readFileSync(affDataPath, 'utf-8'); - const dicData = fs.readFileSync(dicDataPath, 'utf-8'); - - return new Typo(locale, affData, dicData); - } - - window.log.error( - `Could not find one of ${affDataPath} or ${dicDataPath} on filesystem` - ); - } - - window.log.info('Detected Linux. Using default en_US spell check dictionary'); - - return new Typo('en_US'); -} - -// We load locale this way and not via app.getLocale() because this call returns -// 'es_ES' and not just 'es.' And hunspell requires the fully-qualified locale. -const locale = osLocale.sync().replace('-', '_'); - -// The LANG environment variable is how node spellchecker finds its default language: -// https://github.com/atom/node-spellchecker/blob/59d2d5eee5785c4b34e9669cd5d987181d17c098/lib/spellchecker.js#L29 -if (!process.env.LANG) { - process.env.LANG = locale; -} - -let spellchecker = null; - -if (process.platform === 'linux') { - spellchecker = setupLinux(locale); -} else { - spellchecker = new Typo(locale); - // OSX and Windows 8+ have OS-level spellcheck APIs - window.log.info( - 'Using OS-level spell check API with locale', - process.env.LANG - ); -} - -const simpleChecker = { - spellCheck(words, callback) { - let mispelled; - if (Array.isArray(words)) { - mispelled = words.filter(word => this.isMisspelled(word)); - } else { - mispelled = this.isMisspelled(words); - } - - callback(mispelled); - }, - isMisspelled(word) { - const misspelled = !spellchecker.check(word); - - // The idea is to make this as fast as possible. For the many, many calls which - // don't result in the red squiggly, we minimize the number of checks. - if (!misspelled) { - return false; - } - - // Only if we think we've found an error do we check the locale and skip list. - if (locale.match(EN_VARIANT) && _.contains(ENGLISH_SKIP_WORDS, word)) { - return false; - } - - return true; - }, - getSuggestions(text) { - return spellchecker.suggest(text); - }, - add() {}, -}; - -const dummyChecker = { - spellCheck(words, callback) { - callback([]); - }, - isMisspelled() { - return false; - }, - getSuggestions() { - return []; - }, - add() { - // nothing - }, -}; - -window.spellChecker = simpleChecker; -window.disableSpellCheck = () => { - window.removeEventListener('contextmenu', spellCheckHandler); - window.addEventListener('contextmenu', defaultContextMenuHandler); - webFrame.setSpellCheckProvider('en-US', dummyChecker); -}; - -window.enableSpellCheck = () => { - webFrame.setSpellCheckProvider('en-US', simpleChecker); - window.addEventListener('contextmenu', spellCheckHandler); - window.removeEventListener('contextmenu', defaultContextMenuHandler); -}; - -const defaultContextMenuHandler = () => { - const menu = buildEditorContextMenu({}); - - // @see js/spell_check.js:183 - setTimeout(() => { - menu.popup(remote.getCurrentWindow()); - }, 30); -}; - -const spellCheckHandler = e => { - // Only show the context menu in text editors. - if (!e.target.closest('textarea, input, [contenteditable="true"]')) { - return; - } - - const selectedText = window.getSelection().toString(); - const isMisspelled = selectedText && simpleChecker.isMisspelled(selectedText); - const spellingSuggestions = - isMisspelled && simpleChecker.getSuggestions(selectedText).slice(0, 5); - const menu = buildEditorContextMenu({ - isMisspelled, - spellingSuggestions, - }); - - // The 'contextmenu' event is emitted after 'selectionchange' has fired - // but possibly before the visible selection has changed. Try to wait - // to show the menu until after that, otherwise the visible selection - // will update after the menu dismisses and look weird. - setTimeout(() => { - menu.popup(remote.getCurrentWindow()); - }, 30); -}; diff --git a/main.js b/main.js index 092fd52a4..ca2a1558a 100644 --- a/main.js +++ b/main.js @@ -9,7 +9,7 @@ const crypto = require('crypto'); const _ = require('lodash'); const pify = require('pify'); const electron = require('electron'); - +const { setup: setupSpellChecker } = require('./app/spell_check'); const packageJson = require('./package.json'); const GlobalErrors = require('./app/global_errors'); @@ -82,6 +82,18 @@ const { } = require('./app/protocol_filter'); const { installPermissionsHandler } = require('./app/permissions'); +let appStartInitialSpellcheckSetting = true; + +async function getSpellCheckSetting() { + const json = await sql.getItemById('spell-check'); + // Default to `true` if setting doesn't exist yet + if (!json) { + return true; + } + + return json.value; +} + function showWindow() { if (!mainWindow) { return; @@ -166,6 +178,7 @@ function prepareURL(pathSegments, moreKeys) { contentProxyUrl: config.contentProxyUrl, importMode: importMode ? true : undefined, // for stringify() serverTrustRoot: config.get('serverTrustRoot'), + appStartInitialSpellcheckSetting, defaultFileServer: config.get('defaultFileServer'), ...moreKeys, }, @@ -216,7 +229,7 @@ function isVisible(window, bounds) { ); } -function createWindow() { +async function createWindow() { const { screen } = electron; const windowOptions = Object.assign( { @@ -233,7 +246,7 @@ function createWindow() { contextIsolation: false, preload: path.join(__dirname, 'preload.js'), nativeWindowOpen: true, - spellcheck: false, + spellcheck: await getSpellCheckSetting(), }, icon: path.join(__dirname, 'images', 'session', 'icon_64.png'), }, @@ -284,6 +297,8 @@ function createWindow() { // Create the browser window. mainWindow = new BrowserWindow(windowOptions); + setupSpellChecker(mainWindow, locale.messages); + // Disable system main menu mainWindow.setMenu(null); @@ -775,6 +790,7 @@ async function showMainWindow(sqlKey, passwordAttempt = false) { messages: locale.messages, passwordAttempt, }); + appStartInitialSpellcheckSetting = await getSpellCheckSetting(); await sqlChannels.initialize(); try { diff --git a/package.json b/package.json index c0c80871c..ee6822cbc 100644 --- a/package.json +++ b/package.json @@ -70,8 +70,6 @@ "config": "1.28.1", "cross-env": "^6.0.3", "dompurify": "^2.0.7", - "electron-context-menu": "^0.15.0", - "electron-editor-context-menu": "1.1.1", "electron-is-dev": "^1.1.0", "electron-localshortcut": "^3.2.1", "electron-updater": "^4.2.2", @@ -123,7 +121,6 @@ "testcheck": "1.0.0-rc.2", "tmp": "0.0.33", "to-arraybuffer": "1.0.1", - "typo-js": "1.1.0", "underscore": "1.9.0", "uuid": "3.3.2", "websocket": "1.0.28" @@ -264,7 +261,6 @@ "config/local-${env.SIGNAL_ENV}.json", "background.html", "about.html", - "settings.html", "password.html", "permissions_popup.html", "debug_log.html", diff --git a/preload.js b/preload.js index 9e3144c16..08c73ed67 100644 --- a/preload.js +++ b/preload.js @@ -376,34 +376,17 @@ window.Signal.Backup = require('./js/modules/backup'); window.Signal.Debug = require('./js/modules/debug'); window.Signal.Logs = require('./js/modules/logs'); -// Add right-click listener for selected text and urls -const contextMenu = require('electron-context-menu'); - -const isQR = params => - params.mediaType === 'image' && params.titleText === 'Scan me!'; - -// QR saving doesn't work so we just disable it -contextMenu({ - showInspectElement: false, - shouldShowMenu: (event, params) => { - const isRegular = - params.mediaType === 'none' && (params.linkURL || params.selectionText); - return Boolean(!params.isEditable && (isQR(params) || isRegular)); - }, - menu: (actions, params) => { - // If it's not a QR then show the default options - if (!isQR(params)) { - return actions; - } - - return [actions.copyImage()]; - }, +window.addEventListener('contextmenu', e => { + const editable = e.target.closest( + 'textarea, input, [contenteditable="true"]' + ); + const link = e.target.closest('a'); + const selection = Boolean(window.getSelection().toString()); + if (!editable && !selection && !link) { + e.preventDefault(); + } }); -// We pull this in last, because the native module involved appears to be sensitive to -// /tmp mounted as noexec on Linux. -require('./js/spell_check'); - window.shortenPubkey = pubkey => `(...${pubkey.substring(pubkey.length - 6)})`; window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g; diff --git a/settings.html b/settings.html deleted file mode 100644 index bd10df867..000000000 --- a/settings.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/index.html b/test/index.html index 54d1dd46f..13477e485 100644 --- a/test/index.html +++ b/test/index.html @@ -586,7 +586,6 @@ - diff --git a/test/spellcheck_test.js b/test/spellcheck_test.js deleted file mode 100644 index b51daa4e9..000000000 --- a/test/spellcheck_test.js +++ /dev/null @@ -1,15 +0,0 @@ -describe('spellChecker', () => { - it('should work', () => { - let result = null; - - window.spellChecker.spellCheck(['correct'], answer => { - result = answer; - }); - assert.deepEqual(result, []); - - window.spellChecker.spellCheck(['fhqwgads'], answer => { - result = answer; - }); - assert.deepEqual(result, ['fhqwgads']); - }); -}); diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index b83188def..9660fc0a4 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -116,9 +116,14 @@ export class SettingsView extends React.Component { const description = setting.description || ''; const comparisonValue = setting.comparisonValue || null; + const storedSetting = window.getSettingValue( + setting.id, + comparisonValue + ); const value = - window.getSettingValue(setting.id, comparisonValue) || - (setting.content && setting.content.defaultValue); + storedSetting !== undefined + ? storedSetting + : setting.content && setting.content.defaultValue; const sliderFn = setting.type === SessionSettingType.Slider @@ -356,7 +361,7 @@ export class SettingsView extends React.Component { type: SessionSettingType.Toggle, category: SessionSettingCategory.Appearance, setFn: window.toggleSpellCheck, - content: undefined, + content: { defaultValue: true }, comparisonValue: undefined, onClick: undefined, confirmationDialogParams: undefined, diff --git a/yarn.lock b/yarn.lock index 39f1c76b1..ff06052c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -938,11 +938,6 @@ ast-types@^0.7.2: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" integrity sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk= -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -1438,7 +1433,7 @@ buffer-alloc@^1.2.0: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" -buffer-crc32@0.2.13, buffer-crc32@^0.2.1: +buffer-crc32@0.2.13, buffer-crc32@^0.2.1, buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= @@ -1887,14 +1882,6 @@ cli-spinners@^1.1.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== -cli-truncate@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -2169,7 +2156,7 @@ concat-stream@1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0: +concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -3015,24 +3002,6 @@ electron-chromedriver@^8.0.0: electron-download "^4.1.1" extract-zip "^1.6.7" -electron-context-menu@^0.15.0: - version "0.15.2" - resolved "https://registry.yarnpkg.com/electron-context-menu/-/electron-context-menu-0.15.2.tgz#66f544a06d2099dafdecade2e9214a66e26b5764" - integrity sha512-r4Zv/NWKe6smezz66yIZSt0geRPx3wdfVlMYXNSGsPcN7LiyZXGlxWvCX9gsqN8Rg8nWC3Q2EcXzt8d9NQFUyg== - dependencies: - cli-truncate "^2.0.0" - electron-dl "^1.2.0" - electron-is-dev "^1.0.1" - -electron-dl@^1.2.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/electron-dl/-/electron-dl-1.14.0.tgz#1466f1b945664ca3d784268307c2b935728177bf" - integrity sha512-4okyei42a1mLsvLK7hLrIfd20EQzB18nIlLTwBV992aMSmTGLUEFRTmO1MfSslGNrzD8nuPuy1l/VxO8so4lig== - dependencies: - ext-name "^5.0.0" - pupa "^1.0.0" - unused-filename "^1.0.0" - electron-download@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8" @@ -3048,17 +3017,6 @@ electron-download@^4.1.1: semver "^5.4.1" sumchecker "^2.0.2" -electron-editor-context-menu@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/electron-editor-context-menu/-/electron-editor-context-menu-1.1.1.tgz#dc30098e0dfb37f62628e43303124c7f3379572d" - integrity sha1-3DAJjg37N/YmKOQzAxJMfzN5Vy0= - dependencies: - lodash.clonedeep "^4.3.0" - lodash.defaults "^4.0.1" - lodash.isarray "^4.0.0" - lodash.isempty "^4.1.2" - lodash.isfunction "^3.0.8" - electron-icon-maker@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/electron-icon-maker/-/electron-icon-maker-0.0.3.tgz#bcd2e91896d7200f84fcc6652aed924fdaaa8307" @@ -3073,7 +3031,7 @@ electron-is-accelerator@^0.1.0: resolved "https://registry.yarnpkg.com/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz#509e510c26a56b55e17f863a4b04e111846ab27b" integrity sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns= -electron-is-dev@*, electron-is-dev@^1.0.1, electron-is-dev@^1.1.0: +electron-is-dev@*, electron-is-dev@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.1.0.tgz#b15a2a600bdc48a51a857d460e05f15b19a2522c" integrity sha512-Z1qA/1oHNowGtSBIcWk0pcLEqYT/j+13xUw/MYOrBUOL4X7VN0i0KCTf5SqyvMPmW5pSPKbo28wkxMxzZ20YnQ== @@ -3618,21 +3576,6 @@ express@^4.16.2: utils-merge "1.0.1" vary "~1.1.2" -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3693,7 +3636,7 @@ extract-zip@1.6.6: mkdirp "0.5.0" yauzl "2.4.1" -extract-zip@^1.0.3, extract-zip@^1.6.5, extract-zip@^1.6.7: +extract-zip@^1.0.3, extract-zip@^1.6.5: version "1.6.7" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= @@ -3703,6 +3646,16 @@ extract-zip@^1.0.3, extract-zip@^1.6.5, extract-zip@^1.6.7: mkdirp "0.5.1" yauzl "2.4.1" +extract-zip@^1.6.7: + version "1.7.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -3759,6 +3712,13 @@ fd-slicer@~1.0.1: dependencies: pend "~1.2.0" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -6115,7 +6075,7 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.3.2: +lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= @@ -6125,36 +6085,16 @@ lodash.cond@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" integrity sha1-9HGh2khr5g9quVXRcRVSPdHSVdU= -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.isarray@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403" - integrity sha1-KspJayjEym1yZxUxNZDALm6jRAM= - -lodash.isempty@^4.1.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" - integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= - lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isfunction@^3.0.8: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" - integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -6482,7 +6422,7 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.43.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: +mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": version "1.43.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== @@ -6618,13 +6558,20 @@ mkdirp@0.5.0: dependencies: minimist "0.0.8" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" +mkdirp@^0.5.4, mkdirp@~0.5.1: + version "0.5.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" + integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== + dependencies: + minimist "^1.2.5" + mkpath@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-0.1.0.tgz#7554a6f8d871834cc97b5462b122c4c124d6de91" @@ -6662,11 +6609,6 @@ mocha@4.1.0: mkdirp "0.5.1" supports-color "4.4.0" -modify-filename@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" - integrity sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE= - moment@2.21.0: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" @@ -8159,9 +8101,9 @@ pseudomap@^1.0.2: integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= psl@^1.1.28: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== public-encrypt@^4.0.0: version "4.0.3" @@ -8215,11 +8157,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-1.0.0.tgz#9a9568a5af7e657b8462a6e9d5328743560ceff6" - integrity sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y= - pupa@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" @@ -9527,7 +9464,12 @@ shell-quote@1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" -signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +signal-exit@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= @@ -9571,15 +9513,6 @@ slice-ansi@1.0.0: dependencies: is-fullwidth-code-point "^2.0.0" -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -9656,13 +9589,6 @@ socks@~2.3.2: ip "1.1.5" smart-buffer "^4.1.0" -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= - dependencies: - sort-keys "^1.0.0" - sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -10632,11 +10558,6 @@ typescript@3.3.3333: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6" integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw== -typo-js@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.1.0.tgz#a5a9f592bcb453666bf70c9694da58705d025ed8" - integrity sha512-W3kLbx+ML9PBl5Bzso/lTvVxk4BCveSNAtQeht59FEtxCdGThmn6wSHA4Xq3eQYAK24NHdisMM4JmsK0GFy/pg== - uc.micro@^1.0.1: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -10844,14 +10765,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -unused-filename@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unused-filename/-/unused-filename-1.0.0.tgz#d340880f71ae2115ebaa1325bef05cc6684469c6" - integrity sha1-00CID3GuIRXrqhMlvvBcxmhEacY= - dependencies: - modify-filename "^1.1.0" - path-exists "^3.0.0" - upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -11508,6 +11421,14 @@ yauzl@2.4.1: dependencies: fd-slicer "~1.0.1" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + zip-stream@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"