diff --git a/.gitignore b/.gitignore index be68f684e6e2..b2887952d29c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # build output dist app/renderer +bin/cli.* # dependencies node_modules diff --git a/app/config.js b/app/config.js index b8d2dc3c10eb..cc059cbbdd1c 100644 --- a/app/config.js +++ b/app/config.js @@ -15,10 +15,13 @@ const _watch = function() { } const onChange = () => { - cfg = _import(); - notify('Configuration updated', 'Hyper configuration reloaded!'); - watchers.forEach(fn => fn()); - checkDeprecatedConfig(); + // Need to wait 100ms to ensure that write is complete + setTimeout(() => { + cfg = _import(); + notify('Configuration updated', 'Hyper configuration reloaded!'); + watchers.forEach(fn => fn()); + checkDeprecatedConfig(); + }, 100); }; if (process.platform === 'win32') { diff --git a/app/config/paths.js b/app/config/paths.js index 7987b4ab28f0..d302791aae97 100644 --- a/app/config/paths.js +++ b/app/config/paths.js @@ -35,6 +35,7 @@ const plugs = { cache: resolve(plugins, 'cache') }; const yarn = resolve(__dirname, '../../bin/yarn-standalone.js'); +const cliScriptPath = resolve(__dirname, '../../bin/hyper'); const icon = resolve(__dirname, '../static/icon96x96.png'); @@ -64,5 +65,6 @@ module.exports = { icon, defaultPlatformKeyPath, plugs, - yarn + yarn, + cliScriptPath }; diff --git a/app/index.js b/app/index.js index 9213a3c7f346..68f23a2ca02f 100644 --- a/app/index.js +++ b/app/index.js @@ -62,6 +62,7 @@ const config = require('./config'); config.setup(); const plugins = require('./plugins'); +const {addSymlink, addBinToUserPath} = require('./utils/cli-install'); const AppMenu = require('./menus/menu'); @@ -98,6 +99,13 @@ if (isDev) { } else { //eslint-disable-next-line no-console console.log('running in prod mode'); + if (process.platform === 'win32') { + //eslint-disable-next-line no-console + addBinToUserPath().catch(err => console.error('Failed to add Hyper CLI path to user PATH', err)); + } else { + //eslint-disable-next-line no-console + addSymlink().catch(err => console.error('Failed to symlink Hyper CLI', err)); + } } const url = 'file://' + resolve(isDev ? __dirname : app.getAppPath(), 'index.html'); diff --git a/app/utils/cli-install.js b/app/utils/cli-install.js new file mode 100644 index 000000000000..342099c6dc52 --- /dev/null +++ b/app/utils/cli-install.js @@ -0,0 +1,86 @@ +const pify = require('pify'); +const fs = require('fs'); +const path = require('path'); +const Registry = require('winreg'); + +const {cliScriptPath} = require('../config/paths'); + +const lstat = pify(fs.lstat); +const readlink = pify(fs.readlink); +const unlink = pify(fs.unlink); +const symlink = pify(fs.symlink); + +const target = '/usr/local/bin/hyper'; +const source = cliScriptPath; + +const checkInstall = () => { + return lstat(target) + .then(stat => stat.isSymbolicLink()) + .then(() => readlink(target)) + .then(link => link === source) + .catch(err => { + if (err.code === 'ENOENT') { + return false; + } + throw err; + }); +}; + +const createSymlink = () => { + return unlink(target) + .catch(err => { + if (err.code === 'ENOENT') { + return; + } + throw err; + }) + .then(() => symlink(source, target)); +}; + +exports.addSymlink = () => { + return checkInstall().then(isInstalled => { + if (isInstalled) { + return Promise.resolve(); + } + return createSymlink(); + }); +}; + +exports.addBinToUserPath = () => { + // Can't use pify because of param order of Registry.values callback + return new Promise((resolve, reject) => { + const envKey = new Registry({hive: 'HKCU', key: '\\Environment'}); + envKey.values((err, items) => { + if (err) { + reject(err); + return; + } + // C:\Users\\AppData\Local\hyper\app-\resources\bin + const binPath = path.dirname(cliScriptPath); + // C:\Users\\AppData\Local\hyper + const basePath = path.resolve(binPath, '../../..'); + + const pathItem = items.find(item => item.name === 'Path'); + const pathParts = pathItem.value.split(';'); + const existingPath = pathParts.find(pathPart => pathPart === binPath); + if (existingPath) { + resolve(); + return; + } + + // Because version is in path we need to remove old path if present and add current path + const newPathValue = pathParts + .filter(pathPart => !pathPart.startsWith(basePath)) + .concat([binPath]) + .join(';'); + + envKey.set(pathItem.name, Registry.REG_SZ, newPathValue, error => { + if (error) { + reject(error); + return; + } + resolve(); + }); + }); + }); +}; diff --git a/build/linux/after-install.tpl b/build/linux/after-install.tpl new file mode 100644 index 000000000000..7a39c2b8987e --- /dev/null +++ b/build/linux/after-install.tpl @@ -0,0 +1,4 @@ +#!/bin/bash + +# Link to the CLI bootstrap +ln -sf '/opt/${productFilename}/resources/bin/${executable}' '/usr/local/bin/${executable}' \ No newline at end of file diff --git a/build/linux/hyper b/build/linux/hyper new file mode 100755 index 000000000000..18824de3a3b1 --- /dev/null +++ b/build/linux/hyper @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Deeply inspired by https://github.com/Microsoft/vscode/blob/1.17.0/resources/linux/bin/code.sh + +# If root, ensure that --user-data-dir is specified +if [ "$(id -u)" = "0" ]; then + for i in $@ + do + if [[ $i == --user-data-dir=* ]]; then + DATA_DIR_SET=1 + fi + done + if [ -z $DATA_DIR_SET ]; then + echo "It is recommended to start hyper as a normal user. To run as root, you must specify an alternate user data directory with the --user-data-dir argument." 1>&2 + exit 1 + fi +fi + +if [ ! -L $0 ]; then + # if path is not a symlink, find relatively + HYPER_PATH="$(dirname $0)/../.." +else + if which readlink >/dev/null; then + # if readlink exists, follow the symlink and find relatively + HYPER_PATH="$(dirname $(readlink -f $0))/../.." + else + # else use the standard install location + HYPER_PATH="/opt/Hyper" + fi +fi + +ELECTRON="$HYPER_PATH/hyper" +CLI="$HYPER_PATH/resources/bin/cli.js" +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +exit $? \ No newline at end of file diff --git a/build/mac/hyper b/build/mac/hyper new file mode 100755 index 000000000000..d5be61bf1b19 --- /dev/null +++ b/build/mac/hyper @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Deeply inspired by https://github.com/Microsoft/vscode/blob/1.17.0/resources/darwin/bin/code.sh + +function realpath() { /usr/bin/python -c "import os,sys; print os.path.realpath(sys.argv[1])" "$0"; } +CONTENTS="$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")" +ELECTRON="$CONTENTS/MacOS/Hyper" +CLI="$CONTENTS/Resources/bin/cli.js" +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +exit $? \ No newline at end of file diff --git a/build/win/hyper b/build/win/hyper new file mode 100755 index 000000000000..8d7ebd7010ef --- /dev/null +++ b/build/win/hyper @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Deeply inspired by https://github.com/Microsoft/vscode/blob/1.17.0/resources/win/bin/code.sh + +NAME="Hyper" +HYPER_PATH="$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")" +ELECTRON="$HYPER_PATH/$NAME.exe" +if grep -q Microsoft /proc/version; then + echo "Warning! Due to WSL limitations, you can use CLI commands here. Please use Hyper CLI on cmd, PowerShell or GitBash/CygWin." + echo "Please see: https://github.com/Microsoft/WSL/issues/1494" + echo "" + # If running under WSL don't pass cli.js to Electron as environment vars + # cannot be transferred from WSL to Windows + # See: https://github.com/Microsoft/BashOnWindows/issues/1363 + # https://github.com/Microsoft/BashOnWindows/issues/1494 + "$ELECTRON" "$@" + exit $? +fi +if [ "$(expr substr $(uname -s) 1 9)" == "CYGWIN_NT" ]; then + CLI=$(cygpath -m "$HYPER_PATH/resources/bin/cli.js") +else + CLI="$HYPER_PATH/resources/bin/cli.js" +fi +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +exit $? + diff --git a/build/win/hyper.cmd b/build/win/hyper.cmd new file mode 100755 index 000000000000..ce733d990ca3 --- /dev/null +++ b/build/win/hyper.cmd @@ -0,0 +1,5 @@ +@echo off +setlocal +set ELECTRON_RUN_AS_NODE=1 +call "%~dp0..\..\Hyper.exe" "%~dp0..\..\resources\bin\cli.js" %* +endlocal \ No newline at end of file diff --git a/cli/api.js b/cli/api.js new file mode 100644 index 000000000000..239aaf918d62 --- /dev/null +++ b/cli/api.js @@ -0,0 +1,112 @@ +const fs = require('fs'); +const os = require('os'); +const npmName = require('npm-name'); +const pify = require('pify'); +const recast = require('recast'); + +const fileName = `${os.homedir()}/.hyper.js`; + +let fileContents; +let parsedFile; +let plugins; +let localPlugins; + +try { + fileContents = fs.readFileSync(fileName, 'utf8'); + + parsedFile = recast.parse(fileContents); + + const properties = parsedFile.program.body[0].expression.right.properties; + plugins = properties.find(property => { + return property.key.name === 'plugins'; + }).value.elements; + + localPlugins = properties.find(property => { + return property.key.name === 'localPlugins'; + }).value.elements; +} catch (err) { + if (err.code !== 'ENOENT') { + // ENOENT === !exists() + throw err; + } +} + +function exists() { + return fileContents !== undefined; +} + +function isInstalled(plugin, locally) { + const array = locally ? localPlugins : plugins; + if (array && Array.isArray(array)) { + return array.find(entry => entry.value === plugin) !== undefined; + } + return false; +} + +function save() { + return pify(fs.writeFile)(fileName, recast.print(parsedFile).code, 'utf8'); +} + +function existsOnNpm(plugin) { + plugin = plugin.split('#')[0].split('@')[0]; + return npmName(plugin).then(unavailable => { + if (unavailable) { + const err = new Error(`${plugin} not found on npm`); + err.code = 'NOT_FOUND_ON_NPM'; + throw err; + } + }); +} + +function install(plugin, locally) { + const array = locally ? localPlugins : plugins; + return new Promise((resolve, reject) => { + existsOnNpm(plugin) + .then(() => { + if (isInstalled(plugin, locally)) { + return reject(`${plugin} is already installed`); + } + + array.push(recast.types.builders.literal(plugin)); + save() + .then(resolve) + .catch(err => reject(err)); + }) + .catch(err => { + if (err.code === 'NOT_FOUND_ON_NPM') { + reject(err.message); + } else { + reject(err); + } + }); + }); +} + +function uninstall(plugin) { + return new Promise((resolve, reject) => { + if (!isInstalled(plugin)) { + return reject(`${plugin} is not installed`); + } + + const index = plugins.findIndex(entry => entry.value === plugin); + plugins.splice(index, 1); + save() + .then(resolve) + .catch(err => reject(err)); + }); +} + +function list() { + if (Array.isArray(plugins)) { + return plugins.map(plugin => plugin.value).join('\n'); + } + return false; +} + +module.exports.configPath = fileName; +module.exports.exists = exists; +module.exports.existsOnNpm = existsOnNpm; +module.exports.isInstalled = isInstalled; +module.exports.install = install; +module.exports.uninstall = uninstall; +module.exports.list = list; diff --git a/cli/index.js b/cli/index.js new file mode 100644 index 000000000000..4f43da698910 --- /dev/null +++ b/cli/index.js @@ -0,0 +1,210 @@ +// This is a CLI tool, using console is OK +/* eslint no-console: 0 */ +const {spawn, exec} = require('child_process'); +const {isAbsolute, resolve} = require('path'); +const {existsSync} = require('fs'); +const pify = require('pify'); +const args = require('args'); +const chalk = require('chalk'); +const opn = require('opn'); +const columnify = require('columnify'); +const got = require('got'); +const ora = require('ora'); +const api = require('./api'); + +const PLUGIN_PREFIX = 'hyper-'; + +let commandPromise; + +const getPluginName = arg => { + if (arg.indexOf(PLUGIN_PREFIX) !== 0) { + return `${PLUGIN_PREFIX}${arg}`; + } + return arg; +}; + +const checkConfig = () => { + if (api.exists()) { + return true; + } + let msg = chalk.red(`Error! Config file not found: ${api.configPath}\n`); + msg += 'Please launch Hyper and retry.'; + console.error(msg); + process.exit(1); +}; + +args.command(['i', 'install'], 'Install a plugin', (name, args_) => { + checkConfig(); + const plugin = getPluginName(args_[0]); + commandPromise = api + .install(plugin) + .then(() => console.log(chalk.green(`${plugin} installed successfully!`))) + .catch(err => console.error(chalk.red(err))); +}); + +args.command(['u', 'uninstall', 'rm', 'remove'], 'Uninstall a plugin', (name, args_) => { + checkConfig(); + const plugin = getPluginName(args_[0]); + commandPromise = api + .uninstall(plugin) + .then(() => console.log(chalk.green(`${plugin} uninstalled successfully!`))) + .catch(err => console.log(chalk.red(err))); +}); + +args.command(['ls', 'list'], 'List installed plugins', () => { + checkConfig(); + let plugins = api.list(); + + if (plugins) { + console.log(plugins); + } else { + console.log(chalk.red(`No plugins installed yet.`)); + } + process.exit(0); +}); + +const lsRemote = pattern => { + // note that no errors are catched by this function + const URL = `https://api.npms.io/v2/search?q=${(pattern && `${pattern}+`) || ''}keywords:hyper-plugin,hyper-theme`; + return got(URL) + .then(response => JSON.parse(response.body).results) + .then(entries => entries.map(entry => entry.package)) + .then(entries => entries.filter(entry => entry.name.indexOf(PLUGIN_PREFIX) === 0)) + .then(entries => + entries.map(({name, description}) => { + return {name, description}; + }) + ) + .then(entries => + entries.map(entry => { + entry.name = chalk.green(entry.name); + return entry; + }) + ); +}; + +args.command(['s', 'search'], 'Search for plugins on npm', (name, args_) => { + const spinner = ora('Searching').start(); + const query = args_[0] ? args_[0].toLowerCase() : ''; + + commandPromise = lsRemote(query) + .then(entries => { + if (entries.length === 0) { + spinner.fail(); + console.error(chalk.red(`Your search '${query}' did not match any plugins`)); + console.error(`${chalk.red('Try')} ${chalk.green('hyper ls-remote')}`); + process.exit(1); + } else { + let msg = columnify(entries); + spinner.succeed(); + msg = msg.substring(msg.indexOf('\n') + 1); // remove header + console.log(msg); + } + }) + .catch(err => { + spinner.fail(); + console.error(chalk.red(err)); // TODO + }); +}); + +args.command(['lsr', 'list-remote', 'ls-remote'], 'List plugins available on npm', () => { + const spinner = ora('Searching').start(); + + commandPromise = lsRemote() + .then(entries => { + let msg = columnify(entries); + + spinner.succeed(); + msg = msg.substring(msg.indexOf('\n') + 1); // remove header + console.log(msg); + }) + .catch(err => { + spinner.fail(); + console.error(chalk.red(err)); // TODO + }); +}); + +args.command(['d', 'docs', 'h', 'home'], 'Open the npm page of a plugin', (name, args_) => { + const plugin = getPluginName(args_[0]); + opn(`http://ghub.io/${plugin}`, {wait: false}); + process.exit(0); +}); + +args.command([''], 'Launch Hyper'); + +args.option(['v', 'verbose'], 'Verbose mode', false); + +const main = argv => { + const flags = args.parse(argv, { + name: 'hyper', + version: false, + mri: { + boolean: ['v', 'verbose'] + } + }); + + if (commandPromise) { + return commandPromise; + } + + const env = Object.assign({}, process.env, { + // this will signal Hyper that it was spawned from this module + HYPER_CLI: '1', + ELECTRON_NO_ATTACH_CONSOLE: '1' + }); + + delete env['ELECTRON_RUN_AS_NODE']; + + if (flags.verbose) { + env['ELECTRON_ENABLE_LOGGING'] = '1'; + } + + const options = { + detached: true, + env + }; + + const args_ = args.sub.map(arg => { + const cwd = isAbsolute(arg) ? arg : resolve(process.cwd(), arg); + if (!existsSync(cwd)) { + console.error(chalk.red(`Error! Directory or file does not exist: ${cwd}`)); + process.exit(1); + } + return cwd; + }); + + if (!flags.verbose) { + options['stdio'] = 'ignore'; + if (process.platform === 'darwin') { + //Use `open` to prevent multiple Hyper process + const cmd = `open -b co.zeit.hyper ${args_}`; + const opts = { + env + }; + return pify(exec)(cmd, opts); + } + } + + const child = spawn(process.execPath, args_, options); + + if (flags.verbose) { + child.stdout.on('data', data => console.log(data.toString('utf8'))); + child.stderr.on('data', data => console.error(data.toString('utf8'))); + } + if (flags.verbose) { + return new Promise(c => child.once('exit', () => c(null))); + } + child.unref(); + return Promise.resolve(); +}; + +function eventuallyExit(code) { + setTimeout(() => process.exit(code), 100); +} + +main(process.argv) + .then(() => eventuallyExit(0)) + .catch(err => { + console.error(err.stack ? err.stack : err); + eventuallyExit(1); + }); diff --git a/package.json b/package.json index f108e253ed39..f6e8d60ef5ab 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,15 @@ }, "build": { "appId": "co.zeit.hyper", - "extraResources": "./bin/yarn-standalone.js", + "extraResources": [ + "./bin/yarn-standalone.js", + "./bin/cli.js", + { + "from": "./build/${os}/", + "to": "./bin/", + "filter": ["hyper*"] + } + ], "linux": { "category": "TerminalEmulator", "target": [ @@ -155,6 +163,12 @@ "mac": { "category": "public.app-category.developer-tools", "extendInfo": "build/Info.plist" + }, + "deb": { + "afterInstall": "./build/linux/after-install.tpl" + }, + "rpm": { + "afterInstall": "./build/linux/after-install.tpl" } }, "license": "MIT", @@ -164,21 +178,31 @@ }, "dependencies": { "aphrodite-simple": "0.4.1", + "args": "3.0.6", + "chalk": "2.1.0", "color": "2.0.1", + "columnify": "1.5.4", "css-loader": "0.28.7", + "got": "7.1.0", "json-loader": "0.5.7", "mousetrap": "chabou/mousetrap#useCapture", "ms": "2.1.1", + "npm-name": "3.1.0", + "opn": "5.1.0", + "ora": "1.3.0", "php-escape-shell": "1.0.0", + "pify": "3.0.0", "react": "16.2.0", "react-deep-force-update": "2.0.1", "react-dom": "16.2.0", "react-redux": "5.0.6", + "recast": "0.12.6", "redux": "3.7.2", "redux-thunk": "2.2.0", "reselect": "3.0.1", "seamless-immutable": "7.1.2", "semver": "5.4.1", + "shebang-loader": "false0.0.1", "uuid": "3.1.0", "xterm": "2.9.2" }, @@ -207,5 +231,8 @@ "spectron": "3.7.2", "style-loader": "0.19.0", "webpack": "3.10.0" + }, + "resolutions": { + "rc": "1.2.3" } } diff --git a/webpack.config.js b/webpack.config.js index b9e7b50d30d6..c0d88ffefa52 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,48 +6,85 @@ const Copy = require('copy-webpack-plugin'); const nodeEnv = process.env.NODE_ENV || 'development'; const isProd = nodeEnv === 'production'; -module.exports = { - resolve: { - extensions: ['.js', '.jsx'] - }, - devtool: isProd ? 'hidden-source-map' : 'cheap-module-source-map', - entry: './lib/index.js', - output: { - path: path.join(__dirname, 'app', 'renderer'), - filename: 'bundle.js' - }, - module: { - rules: [ - { - test: /\.(js|jsx)$/, - exclude: /node_modules/, - loader: 'babel-loader' - }, - { - test: /\.json/, - loader: 'json-loader' - }, - // for xterm.js - { - test: /\.css$/, - loader: 'style-loader!css-loader' - } - ] - }, - plugins: [ - new webpack.IgnorePlugin(/.*\.js.map$/i), +module.exports = [ + { + name: 'hyper', + resolve: { + extensions: ['.js', '.jsx'] + }, + devtool: isProd ? 'hidden-source-map' : 'cheap-module-source-map', + entry: './lib/index.js', + output: { + path: path.join(__dirname, 'app', 'renderer'), + filename: 'bundle.js' + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + loader: 'babel-loader' + }, + { + test: /\.json/, + loader: 'json-loader' + }, + // for xterm.js + { + test: /\.css$/, + loader: 'style-loader!css-loader' + } + ] + }, + plugins: [ + new webpack.IgnorePlugin(/.*\.js.map$/i), - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(nodeEnv) - } - }), - new Copy([ - { - from: './assets', - to: './assets' - } - ]) - ], - target: 'electron' -}; + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(nodeEnv) + } + }), + new Copy([ + { + from: './assets', + to: './assets' + } + ]) + ], + target: 'electron' + }, + { + name: 'hyper-cli', + resolve: { + extensions: ['.js', '.jsx', '.json'] + }, + devtool: isProd ? 'none' : 'cheap-module-source-map', + entry: './cli/index.js', + output: { + path: path.join(__dirname, 'bin'), + filename: 'cli.js' + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + loader: 'babel-loader' + }, + { + test: /index.js/, + loader: 'shebang-loader', + include: [/node_modules\/rc/] + } + ] + }, + plugins: [ + // spawn-sync is required by execa if node <= 0.10 + new webpack.IgnorePlugin(/(.*\.js.map|spawn-sync)$/i), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(nodeEnv) + }) + ], + target: 'node' + } +]; diff --git a/yarn.lock b/yarn.lock index 58261f4169f2..e0355b662c1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -250,6 +250,16 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +args@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/args/-/args-3.0.6.tgz#2c323409dd84ffeac97979bb70c7836bb8aafd4a" + dependencies: + camelcase "4.1.0" + chalk "2.1.0" + mri "1.1.0" + pkginfo "0.4.1" + string-similarity "1.2.0" + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -334,6 +344,10 @@ assert@^1.1.1: dependencies: util "0.10.3" +ast-types@0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.11.tgz#371177bb59232ff5ceaa1d09ee5cad705b1a5aa9" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -1314,6 +1328,10 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" +camelcase@4.1.0, camelcase@^4.0.0, camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -1326,10 +1344,6 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.0.0, camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" @@ -1358,6 +1372,14 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +chalk@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + chalk@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" @@ -1565,6 +1587,13 @@ colors@^1.1.2, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" +columnify@1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -1679,7 +1708,7 @@ core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" -core-js@^2.0.0, core-js@^2.4.0, core-js@^2.5.0: +core-js@^2.0.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" @@ -1928,6 +1957,12 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +decompress-response@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + dependencies: + mimic-response "^1.0.0" + deep-equal@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -1944,6 +1979,12 @@ deepmerge@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312" +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + define-properties@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" @@ -2471,7 +2512,7 @@ esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" -esprima@^4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" @@ -2920,6 +2961,25 @@ globule@^1.0.0: lodash "~4.17.4" minimatch "~3.0.2" +got@7.1.0, got@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -2980,6 +3040,16 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has-symbol-support-x@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz#66ec2e377e0c7d7ccedb07a3a84d77510ff1bc4c" + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + dependencies: + has-symbol-support-x "^1.4.1" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -3375,6 +3445,10 @@ is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + is-observable@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2" @@ -3403,7 +3477,7 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.0.0: +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -3469,6 +3543,10 @@ is-windows@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -3502,6 +3580,13 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + jest-docblock@^21.0.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" @@ -3779,7 +3864,11 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.4: +lodash.zip@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -3935,6 +4024,10 @@ mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" +mimic-response@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" + minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" @@ -3977,6 +4070,10 @@ mousetrap@chabou/mousetrap#useCapture: version "1.6.1" resolved "https://codeload.github.com/chabou/mousetrap/tar.gz/c95eeeaafba1131dd8d35bc130d4a79b2ff9261a" +mri@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -4140,6 +4237,14 @@ npm-install-package@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/npm-install-package/-/npm-install-package-2.1.0.tgz#d7efe3cfcd7ab00614b896ea53119dc9ab259125" +npm-name@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/npm-name/-/npm-name-3.1.0.tgz#79789afac52ae335dc3a5aa7f41f59d1b77c756f" + dependencies: + got "^7.0.0" + lodash.zip "^4.0.0" + registry-url "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -4217,6 +4322,12 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opn@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519" + dependencies: + is-wsl "^1.1.0" + optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -4239,7 +4350,7 @@ optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" -ora@^1.2.0: +ora@1.3.0, ora@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ora/-/ora-1.3.0.tgz#80078dd2b92a934af66a3ad72a5b910694ede51a" dependencies: @@ -4289,6 +4400,10 @@ output-file-sync@^1.1.2: mkdirp "^0.5.1" object-assign "^4.1.0" +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -4303,6 +4418,12 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-timeout@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.0.tgz#9820f99434c5817868b4f34809ee5291660d5b6c" + dependencies: + p-finally "^1.0.0" + package-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44" @@ -4436,14 +4557,14 @@ php-escape-shell@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/php-escape-shell/-/php-escape-shell-1.0.0.tgz#7d589611f91c2ff6f3fe6591c91478c05c610879" +pify@3.0.0, pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - pinkie-promise@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-1.0.0.tgz#d1da67f5482563bb7cf57f286ae2822ecfbf3670" @@ -4477,6 +4598,10 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkginfo@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" + plist@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025" @@ -4800,6 +4925,10 @@ private@^0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" +private@~0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -4921,9 +5050,9 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" +rc@1.2.3, rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.3.tgz#51575a900f8dd68381c710b4712c2154c3e2035b" dependencies: deep-extend "~0.4.0" ini "~1.3.0" @@ -5037,6 +5166,16 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +recast@0.12.6: + version "0.12.6" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.12.6.tgz#4b0fb82feb1d10b3bd62d34943426d9b3ed30d4c" + dependencies: + ast-types "0.9.11" + core-js "^2.4.1" + esprima "~4.0.0" + private "~0.1.5" + source-map "~0.5.0" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -5112,7 +5251,7 @@ registry-auth-token@^3.0.1: rc "^1.1.6" safe-buffer "^5.0.1" -registry-url@^3.0.3: +registry-url@^3.0.0, registry-url@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" dependencies: @@ -5362,6 +5501,10 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-loader@false0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shebang-loader/-/shebang-loader-0.0.1.tgz#a4000495d44cceefbec63435e7b1698569fa52ec" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" @@ -5463,7 +5606,7 @@ source-map@^0.1.38: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -5560,6 +5703,12 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" +string-similarity@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-1.2.0.tgz#d75153cb383846318b7a39a8d9292bb4db4e9c30" + dependencies: + lodash "^4.13.1" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -5982,6 +6131,10 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -6054,6 +6207,12 @@ watchpack@^1.4.0: chokidar "^1.7.0" graceful-fs "^4.1.2" +wcwidth@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + dependencies: + defaults "^1.0.3" + wdio-dot-reporter@~0.0.8: version "0.0.9" resolved "https://registry.yarnpkg.com/wdio-dot-reporter/-/wdio-dot-reporter-0.0.9.tgz#929b2adafd49d6b0534fda068e87319b47e38fe5"