From f5f6271238c29f57f135957088c91feb2220ffa9 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 13:56:16 +0000 Subject: [PATCH 01/47] Initial commit --- packages/cli/package.json | 1 + packages/cli/src/cliEntry.js | 75 +++++----------------- packages/cli/src/commands/config/config.js | 46 +++++++++++++ packages/cli/src/commands/index.js | 2 + packages/cli/src/tools/getLegacyConfig.js | 62 ------------------ packages/cli/src/tools/loadConfig.js | 55 ++++++++++++++++ 6 files changed, 119 insertions(+), 122 deletions(-) create mode 100644 packages/cli/src/commands/config/config.js delete mode 100644 packages/cli/src/tools/getLegacyConfig.js create mode 100644 packages/cli/src/tools/loadConfig.js diff --git a/packages/cli/package.json b/packages/cli/package.json index a545eb4e8..9dfda307a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -23,6 +23,7 @@ "commander": "^2.19.0", "compression": "^1.7.1", "connect": "^3.6.5", + "cosmiconfig": "^5.1.0", "denodeify": "^1.2.1", "envinfo": "^5.7.0", "errorhandler": "^1.5.0", diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index e7779fca4..9f5d203aa 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -10,10 +10,8 @@ import chalk from 'chalk'; import childProcess from 'child_process'; import commander from 'commander'; -import minimist from 'minimist'; import path from 'path'; import type {CommandT, ContextT} from './tools/types.flow'; -import getLegacyConfig from './tools/getLegacyConfig'; import {getCommands} from './commands'; import init from './commands/init/init'; import assertRequiredOptions from './tools/assertRequiredOptions'; @@ -24,8 +22,6 @@ import pkgJson from '../package.json'; commander .option('--version', 'Print CLI version') - .option('--projectRoot [string]', 'Path to the root of the project') - .option('--reactNativePath [string]', 'Path to React Native') .option('--verbose', 'Increase logging verbosity'); commander.on('command:*', () => { @@ -84,22 +80,22 @@ function printUnknownCommand(cmdName) { } } -const addCommand = (command: CommandT, ctx: ContextT) => { +const addCommand = (command: CommandT, root: string) => { const options = command.options || []; const cmd = commander .command(command.name) .description(command.description) - .action(function handleAction(...args) { + .action(async function handleAction(...args) { const passedOptions = this.opts(); const argv: Array = Array.from(args).slice(0, -1); - Promise.resolve() - .then(() => { - assertRequiredOptions(options, passedOptions); - return command.func(argv, ctx, passedOptions); - }) - .catch(handleError); + try { + assertRequiredOptions(options, passedOptions); + return command.func(argv, passedOptions); + } catch (e) { + handleError(e); + } }); cmd.helpInformation = printHelpInformation.bind( @@ -117,15 +113,6 @@ const addCommand = (command: CommandT, ctx: ContextT) => { opt.default, ), ); - - /** - * We want every command (like "start", "link") to accept below options. - * To achieve that we append them to regular options of each command here. - * This way they'll be displayed in the commands --help menus. - */ - cmd - .option('--projectRoot [string]', 'Path to the root of the project') - .option('--reactNativePath [string]', 'Path to React Native'); }; async function run() { @@ -156,45 +143,13 @@ async function setupAndRun() { } } - /** - * At this point, commander arguments are not parsed yet because we need to - * add all the commands and their options. That's why we resort to using - * minimist for parsing some global options. - */ - const options = minimist(process.argv.slice(2)); - - const root = options.projectRoot - ? path.resolve(options.projectRoot) - : process.cwd(); - - const reactNativePath = options.reactNativePath - ? path.resolve(options.reactNativePath) - : (() => { - try { - return path.dirname( - // $FlowIssue: Wrong `require.resolve` type definition - require.resolve('react-native/package.json', { - paths: [root], - }), - ); - } catch (_ignored) { - throw new Error( - 'Unable to find React Native files. Make sure "react-native" module is installed in your project dependencies.', - ); - } - })(); - - const ctx = { - ...getLegacyConfig(root), - reactNativePath, - root, - }; - - setProjectDir(ctx.root); - - const commands = getCommands(ctx.root); - - commands.forEach(command => addCommand(command, ctx)); + // @todo: Let's use process.cwd directly where possible + const root = process.cwd(); + + // @todo this shouldn't be called here + setProjectDir(root); + + getCommands(root).forEach(command => addCommand(command, root)); commander.parse(process.argv); diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js new file mode 100644 index 000000000..4f20b3cfe --- /dev/null +++ b/packages/cli/src/commands/config/config.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ +import loadConfig from '../../tools/loadConfig'; + +import getPlatforms from '../../tools/getPlatforms'; +import getProjectConfig from '../link/getProjectConfig'; +import getDependencyConfig from '../link/getDependencyConfig'; +import getProjectDependencies from '../link/getProjectDependencies'; + +export default { + name: 'config', + description: 'Print CLI configuration', + func: async () => { + const config = await loadConfig(); + + const platforms = getPlatforms(config.root); + const project = getProjectConfig(config, platforms); + + const depenendencies = getProjectDependencies(config.root).reduce( + (acc, dependency) => { + acc[dependency] = getDependencyConfig(config, platforms, dependency); + return acc; + }, + {}, + ); + + console.log( + JSON.stringify( + { + ...config, + platforms: Object.keys(platforms), + project, + depenendencies, + }, + null, + 2, + ), + ); + }, +}; diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index fded7f655..55a745d7e 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -28,6 +28,7 @@ import upgrade from './upgrade/upgrade'; import logAndroid from './logAndroid/logAndroid'; import logIOS from './logIOS/logIOS'; import info from './info/info'; +import config from './config/config'; /** * List of built-in commands @@ -49,6 +50,7 @@ const loadLocalCommands: Array = [ logAndroid, logIOS, info, + config, ]; /** diff --git a/packages/cli/src/tools/getLegacyConfig.js b/packages/cli/src/tools/getLegacyConfig.js deleted file mode 100644 index 2b5386f8e..000000000 --- a/packages/cli/src/tools/getLegacyConfig.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @flow - */ -import path from 'path'; -import util from 'util'; - -import getPlatforms from './getPlatforms'; -import getPackageConfiguration from './getPackageConfiguration'; -import getHooks from './getHooks'; -import getAssets from './getAssets'; -import getParams from './getParams'; - -const generateDeprecationMessage = api => - `${api} is deprecated and will be removed soon. Please check release notes on how to upgrade`; - -/** - * Gets legacy configuration to support existing plugins while they migrate - * to the new API - * - * This file will be removed from the next version. - */ -export default (root: string) => ({ - getPlatformConfig: util.deprecate( - () => getPlatforms(root), - generateDeprecationMessage('getPlatformConfig()'), - ), - getProjectConfig: util.deprecate(() => { - const platforms = getPlatforms(root); - - const rnpm = getPackageConfiguration(root); - - const config = { - ...rnpm, - assets: getAssets(root), - }; - - Object.keys(platforms).forEach(key => { - config[key] = platforms[key].projectConfig(root, rnpm[key] || {}); - }); - - return config; - }, generateDeprecationMessage('getProjectConfig()')), - getDependencyConfig: util.deprecate((packageName: string) => { - const platforms = getPlatforms(root); - const folder = path.join(process.cwd(), 'node_modules', packageName); - - const rnpm = getPackageConfiguration(folder); - - const config = { - ...rnpm, - assets: getAssets(folder), - commands: getHooks(folder), - params: getParams(folder), - }; - - Object.keys(platforms).forEach(key => { - config[key] = platforms[key].dependencyConfig(folder, rnpm[key] || {}); - }); - - return config; - }, generateDeprecationMessage('getDependencyConfig()')), -}); diff --git a/packages/cli/src/tools/loadConfig.js b/packages/cli/src/tools/loadConfig.js new file mode 100644 index 000000000..1bfeeb817 --- /dev/null +++ b/packages/cli/src/tools/loadConfig.js @@ -0,0 +1,55 @@ +/** + * @flow + */ +import comsmiconfig from 'cosmiconfig'; +import path from 'path'; + +import getPlatforms from './getPlatforms'; + +// @todo(mike): move this out to `tools` +import getProjectDependencies from '../commands/link/getProjectDependencies'; + +const explorer = comsmiconfig('react-native'); + +type Config = { + root: string, + reactNativePath: string, +}; + +type Options = { + root: ?string, + dependencies: boolean, +}; + +const defaultOptions = { + root: process.cwd(), + dependencies: false, +}; + +// todo make overrides with options +async function loadConfig(opts: Options = defaultOptions): Config { + const {config} = (await explorer.search(opts.root)) || {config: {}}; + + return { + ...config, + root: opts.root, + reactNativePath: config.reactNativePath + ? path.resolve(config.reactNativePath) + : (() => { + try { + return path.dirname( + // $FlowIssue: Wrong `require.resolve` type definition + require.resolve('react-native/package.json', { + paths: [opts.root], + }), + ); + } catch (_ignored) { + throw new Error( + 'Unable to find React Native files. Make sure "react-native" module is installed in your project dependencies.', + ); + } + })(), + }; +} + +export default loadConfig; From 9734a8f4aa4d858b2eaf2a659bdd67493525d1ec Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 14:00:24 +0000 Subject: [PATCH 02/47] Clean up main entry point --- packages/cli/src/cliEntry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 9f5d203aa..a327c8bc1 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -80,7 +80,7 @@ function printUnknownCommand(cmdName) { } } -const addCommand = (command: CommandT, root: string) => { +const addCommand = (command: CommandT) => { const options = command.options || []; const cmd = commander @@ -149,7 +149,7 @@ async function setupAndRun() { // @todo this shouldn't be called here setProjectDir(root); - getCommands(root).forEach(command => addCommand(command, root)); + getCommands(root).forEach(command => addCommand(command)); commander.parse(process.argv); From 3ab1c46447dfb91b4604646892de9ff377e2f12d Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Wed, 20 Mar 2019 14:08:23 +0000 Subject: [PATCH 03/47] Fixes some typos --- packages/cli/src/commands/config/config.js | 4 ++-- packages/cli/src/commands/link/linkAll.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js index 4f20b3cfe..63fb02710 100644 --- a/packages/cli/src/commands/config/config.js +++ b/packages/cli/src/commands/config/config.js @@ -22,7 +22,7 @@ export default { const platforms = getPlatforms(config.root); const project = getProjectConfig(config, platforms); - const depenendencies = getProjectDependencies(config.root).reduce( + const dependencies = getProjectDependencies(config.root).reduce( (acc, dependency) => { acc[dependency] = getDependencyConfig(config, platforms, dependency); return acc; @@ -36,7 +36,7 @@ export default { ...config, platforms: Object.keys(platforms), project, - depenendencies, + dependencies, }, null, 2, diff --git a/packages/cli/src/commands/link/linkAll.js b/packages/cli/src/commands/link/linkAll.js index 333185dac..9ba413763 100644 --- a/packages/cli/src/commands/link/linkAll.js +++ b/packages/cli/src/commands/link/linkAll.js @@ -33,19 +33,19 @@ function linkAll( const projectAssets = getAssets(context.root); const dependencies = getProjectDependencies(context.root); - const depenendenciesConfig = dependencies.map(dependnecy => - getDependencyConfig(context, platforms, dependnecy), + const dependenciesConfig = dependencies.map(dependency => + getDependencyConfig(context, platforms, dependency), ); const assets = dedupeAssets( - depenendenciesConfig.reduce( + dependenciesConfig.reduce( (acc, dependency) => acc.concat(dependency.assets), projectAssets, ), ); const tasks = flatten( - depenendenciesConfig.map(config => [ + dependenciesConfig.map(config => [ () => promisify(config.commands.prelink || commandStub), () => linkDependency(platforms, project, config), () => promisify(config.commands.postlink || commandStub), From 703098f7c38115b5d6599d34645272d7f88627e7 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 15:13:58 +0000 Subject: [PATCH 04/47] Update configuration --- packages/cli/package.json | 1 + packages/cli/src/commands/config/config.js | 29 +------- packages/cli/src/tools/loadConfig.js | 82 ++++++++++++++++++++-- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 9dfda307a..ccf260300 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,6 +24,7 @@ "compression": "^1.7.1", "connect": "^3.6.5", "cosmiconfig": "^5.1.0", + "deepmerge": "^3.2.0", "denodeify": "^1.2.1", "envinfo": "^5.7.0", "errorhandler": "^1.5.0", diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js index 4f20b3cfe..7e32800a2 100644 --- a/packages/cli/src/commands/config/config.js +++ b/packages/cli/src/commands/config/config.js @@ -8,39 +8,12 @@ */ import loadConfig from '../../tools/loadConfig'; -import getPlatforms from '../../tools/getPlatforms'; -import getProjectConfig from '../link/getProjectConfig'; -import getDependencyConfig from '../link/getDependencyConfig'; -import getProjectDependencies from '../link/getProjectDependencies'; - export default { name: 'config', description: 'Print CLI configuration', func: async () => { const config = await loadConfig(); - const platforms = getPlatforms(config.root); - const project = getProjectConfig(config, platforms); - - const depenendencies = getProjectDependencies(config.root).reduce( - (acc, dependency) => { - acc[dependency] = getDependencyConfig(config, platforms, dependency); - return acc; - }, - {}, - ); - - console.log( - JSON.stringify( - { - ...config, - platforms: Object.keys(platforms), - project, - depenendencies, - }, - null, - 2, - ), - ); + console.log(JSON.stringify(config, null, 2)); }, }; diff --git a/packages/cli/src/tools/loadConfig.js b/packages/cli/src/tools/loadConfig.js index 1bfeeb817..4ae913c05 100644 --- a/packages/cli/src/tools/loadConfig.js +++ b/packages/cli/src/tools/loadConfig.js @@ -3,35 +3,103 @@ */ import comsmiconfig from 'cosmiconfig'; import path from 'path'; +import merge from 'deepmerge'; +import {get} from 'lodash'; import getPlatforms from './getPlatforms'; - -// @todo(mike): move this out to `tools` import getProjectDependencies from '../commands/link/getProjectDependencies'; const explorer = comsmiconfig('react-native'); +type DependencyConfig = { + ios: {}, + android: {}, +}; + +type ProjectConfig = { + ios: {}, + android: {}, +}; + type Config = { root: string, reactNativePath: string, + project: ProjectConfig, + dependencies: { + [key: string]: DependencyConfig, + }, }; type Options = { root: ?string, - dependencies: boolean, }; const defaultOptions = { root: process.cwd(), - dependencies: false, }; -// todo make overrides with options +function readConfigFromDisk(root: string) { + const {config} = explorer.searchSync(root) || {config: {}}; + return config; +} + +/** + * Loads default CLI configuration + */ +function getDefaultConfig(config: Config, root: string) { + const platforms = getPlatforms(root); + + const dependencies = getProjectDependencies(root).reduce( + (deps, dependency) => { + const folder = path.join(root, 'node_modules', dependency); + const dependencyConfig = readConfigFromDisk(folder); + + deps[dependency] = Object.keys(platforms).reduce( + (acc, platform) => { + const dependencyPlatformConfig = get( + dependencyConfig, + `dependency.${platform}`, + {}, + ); + if (dependencyPlatformConfig === null) { + return acc; + } + const detectedConfig = platforms[platform].dependencyConfig( + folder, + dependencyPlatformConfig, + ); + if (detectedConfig === null) { + return acc; + } + acc[platform] = { + ...dependencyPlatformConfig, + ...detectedConfig, + }; + return acc; + }, + { + ios: null, + android: null, + }, + ); + return deps; + }, + {}, + ); + + return merge( + { + dependencies, + }, + config, + ); +} + async function loadConfig(opts: Options = defaultOptions): Config { - const {config} = (await explorer.search(opts.root)) || {config: {}}; + const config = readConfigFromDisk(opts.root); return { - ...config, + ...getDefaultConfig(config, opts.root), root: opts.root, reactNativePath: config.reactNativePath ? path.resolve(config.reactNativePath) From 32da7cfd6966927fd3efa4d88787475b3358b8f8 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 15:37:39 +0000 Subject: [PATCH 05/47] update --- packages/cli/src/commands/config/config.js | 2 +- packages/cli/src/tools/android/index.js | 38 +++++++++++++++++++ .../tools/config/getProjectDependencies.js | 0 .../tools/{loadConfig.js => config/index.js} | 20 ++++++---- packages/cli/src/tools/ios/index.js | 9 +++++ 5 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 packages/cli/src/tools/config/getProjectDependencies.js rename packages/cli/src/tools/{loadConfig.js => config/index.js} (86%) diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js index 7e32800a2..0bb711a24 100644 --- a/packages/cli/src/commands/config/config.js +++ b/packages/cli/src/commands/config/config.js @@ -6,7 +6,7 @@ * * @format */ -import loadConfig from '../../tools/loadConfig'; +import loadConfig from '../../tools/config'; export default { name: 'config', diff --git a/packages/cli/src/tools/android/index.js b/packages/cli/src/tools/android/index.js index 4fa788a18..41945e671 100644 --- a/packages/cli/src/tools/android/index.js +++ b/packages/cli/src/tools/android/index.js @@ -130,3 +130,41 @@ export function dependencyConfig(folder, userConfig = {}) { return {sourceDir, folder, manifest, packageImportPath, packageInstance}; } + +/** + * New version of getDependencyConfig + */ +export function getDependencyConfig(folder, userConfig) { + const src = userConfig.sourceDir || findAndroidAppFolder(folder); + + if (!src) { + return null; + } + + const sourceDir = path.join(folder, src); + const manifestPath = userConfig.manifestPath + ? path.join(sourceDir, userConfig.manifestPath) + : findManifest(sourceDir); + + if (!manifestPath) { + return null; + } + + const manifest = readManifest(manifestPath); + const packageName = userConfig.packageName || getPackageName(manifest); + const packageClassName = + userConfig.packageClassName || findPackageClassName(sourceDir); + + /** + * This module has no package to export + */ + if (!packageClassName) { + return null; + } + + const packageImportPath = `import ${packageName}.${packageClassName};`; + + const packageInstance = `new ${packageClassName}()`; + + return {packageImportPath, packageInstance}; +} diff --git a/packages/cli/src/tools/config/getProjectDependencies.js b/packages/cli/src/tools/config/getProjectDependencies.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/cli/src/tools/loadConfig.js b/packages/cli/src/tools/config/index.js similarity index 86% rename from packages/cli/src/tools/loadConfig.js rename to packages/cli/src/tools/config/index.js index 4ae913c05..5b9aea4af 100644 --- a/packages/cli/src/tools/loadConfig.js +++ b/packages/cli/src/tools/config/index.js @@ -6,8 +6,7 @@ import path from 'path'; import merge from 'deepmerge'; import {get} from 'lodash'; -import getPlatforms from './getPlatforms'; -import getProjectDependencies from '../commands/link/getProjectDependencies'; +import getProjectDependencies from '../../commands/link/getProjectDependencies'; const explorer = comsmiconfig('react-native'); @@ -43,11 +42,16 @@ function readConfigFromDisk(root: string) { return config; } -/** - * Loads default CLI configuration - */ function getDefaultConfig(config: Config, root: string) { - const platforms = getPlatforms(root); + const platforms = { + ios: { + getDependencyConfig: require('../ios').getDependencyConfig, + }, + android: { + getDependencyConfig: require('../android').getDependencyConfig, + }, + ...config.platforms, + }; const dependencies = getProjectDependencies(root).reduce( (deps, dependency) => { @@ -64,7 +68,7 @@ function getDefaultConfig(config: Config, root: string) { if (dependencyPlatformConfig === null) { return acc; } - const detectedConfig = platforms[platform].dependencyConfig( + const detectedConfig = platforms[platform].getDependencyConfig( folder, dependencyPlatformConfig, ); @@ -72,8 +76,8 @@ function getDefaultConfig(config: Config, root: string) { return acc; } acc[platform] = { - ...dependencyPlatformConfig, ...detectedConfig, + ...dependencyPlatformConfig, }; return acc; }, diff --git a/packages/cli/src/tools/ios/index.js b/packages/cli/src/tools/ios/index.js index 6e0c537c9..255e559d6 100644 --- a/packages/cli/src/tools/ios/index.js +++ b/packages/cli/src/tools/ios/index.js @@ -58,3 +58,12 @@ export function projectConfig(folder, userConfig) { } export const dependencyConfig = projectConfig; + +/** + * New version of `ios` configuration + */ +export function getDependencyConfig(folder) { + return { + podspec: findPodspecName(folder), + }; +} From 942db96dd99fb8902609460e7cfc707c950364ec Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 15:57:27 +0000 Subject: [PATCH 06/47] Tweaks to types --- packages/cli/src/tools/config/index.js | 44 ++++++++++++++++++++------ packages/cli/src/tools/ios/index.js | 8 ++++- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 5b9aea4af..8a3441dc0 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -11,19 +11,43 @@ import getProjectDependencies from '../../commands/link/getProjectDependencies'; const explorer = comsmiconfig('react-native'); type DependencyConfig = { - ios: {}, - android: {}, + ios: ?DependencyConfigIOS, + android: ?DependencyConfigAndroid, }; -type ProjectConfig = { - ios: {}, - android: {}, +type DependencyConfigIOS = DetectedDependencyConfigIOS & { + project: string, +}; + +type DetectedDependencyConfigIOS = { + podspec: string, +}; + +type DependencyConfigAndroid = DetectedDependencyConfigAndroid & { + sourceDir: string, + manifestPath: string, + packageName: string, + packageClassName: string, +}; + +type DetectedDependencyConfigAndroid = { + packageImportPath: string, + packageInstance: string, +}; + +type PlatformConfig = { + getDependencyConfig: (string, T) => ?K, }; -type Config = { +type Platforms = { + [key: string]: PlatformConfig<*>, + ios: PlatformConfig, + android: PlatformConfig, +}; + +type ProjectConfig = { root: string, reactNativePath: string, - project: ProjectConfig, dependencies: { [key: string]: DependencyConfig, }, @@ -42,8 +66,8 @@ function readConfigFromDisk(root: string) { return config; } -function getDefaultConfig(config: Config, root: string) { - const platforms = { +function getDefaultConfig(config: ProjectConfig, root: string) { + const platforms: Platforms = { ios: { getDependencyConfig: require('../ios').getDependencyConfig, }, @@ -99,7 +123,7 @@ function getDefaultConfig(config: Config, root: string) { ); } -async function loadConfig(opts: Options = defaultOptions): Config { +async function loadConfig(opts: Options = defaultOptions): ProjectConfig { const config = readConfigFromDisk(opts.root); return { diff --git a/packages/cli/src/tools/ios/index.js b/packages/cli/src/tools/ios/index.js index 255e559d6..9ba49115e 100644 --- a/packages/cli/src/tools/ios/index.js +++ b/packages/cli/src/tools/ios/index.js @@ -62,7 +62,13 @@ export const dependencyConfig = projectConfig; /** * New version of `ios` configuration */ -export function getDependencyConfig(folder) { +export function getDependencyConfig(folder, userConfig) { + const project = userConfig.project || findProject(folder); + + if (!project) { + return null; + } + return { podspec: findPodspecName(folder), }; From dcb0362a94e2a6244f21c4f096768c1524395b3c Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 15:59:54 +0000 Subject: [PATCH 07/47] types and tweaks --- packages/cli/src/tools/config/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 8a3441dc0..f6e41b30e 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -16,7 +16,7 @@ type DependencyConfig = { }; type DependencyConfigIOS = DetectedDependencyConfigIOS & { - project: string, + project?: string, }; type DetectedDependencyConfigIOS = { @@ -24,10 +24,10 @@ type DetectedDependencyConfigIOS = { }; type DependencyConfigAndroid = DetectedDependencyConfigAndroid & { - sourceDir: string, - manifestPath: string, - packageName: string, - packageClassName: string, + sourceDir?: string, + manifestPath?: string, + packageName?: string, + packageClassName?: string, }; type DetectedDependencyConfigAndroid = { From 93090f8573297188c078da8ebe873b6bc7e0b3a3 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 20 Mar 2019 19:41:10 +0000 Subject: [PATCH 08/47] Save another chunk of work --- packages/cli/src/tools/android/index.js | 38 ------- packages/cli/src/tools/config/dependency.js | 80 +++++++++++++++ .../tools/config/getProjectDependencies.js | 0 packages/cli/src/tools/config/index.js | 98 ++++++++++--------- packages/cli/src/tools/config/types.flow.js | 50 ++++++++++ packages/cli/src/tools/ios/index.js | 15 --- 6 files changed, 180 insertions(+), 101 deletions(-) create mode 100644 packages/cli/src/tools/config/dependency.js delete mode 100644 packages/cli/src/tools/config/getProjectDependencies.js create mode 100644 packages/cli/src/tools/config/types.flow.js diff --git a/packages/cli/src/tools/android/index.js b/packages/cli/src/tools/android/index.js index 41945e671..4fa788a18 100644 --- a/packages/cli/src/tools/android/index.js +++ b/packages/cli/src/tools/android/index.js @@ -130,41 +130,3 @@ export function dependencyConfig(folder, userConfig = {}) { return {sourceDir, folder, manifest, packageImportPath, packageInstance}; } - -/** - * New version of getDependencyConfig - */ -export function getDependencyConfig(folder, userConfig) { - const src = userConfig.sourceDir || findAndroidAppFolder(folder); - - if (!src) { - return null; - } - - const sourceDir = path.join(folder, src); - const manifestPath = userConfig.manifestPath - ? path.join(sourceDir, userConfig.manifestPath) - : findManifest(sourceDir); - - if (!manifestPath) { - return null; - } - - const manifest = readManifest(manifestPath); - const packageName = userConfig.packageName || getPackageName(manifest); - const packageClassName = - userConfig.packageClassName || findPackageClassName(sourceDir); - - /** - * This module has no package to export - */ - if (!packageClassName) { - return null; - } - - const packageImportPath = `import ${packageName}.${packageClassName};`; - - const packageInstance = `new ${packageClassName}()`; - - return {packageImportPath, packageInstance}; -} diff --git a/packages/cli/src/tools/config/dependency.js b/packages/cli/src/tools/config/dependency.js new file mode 100644 index 000000000..bf79fe048 --- /dev/null +++ b/packages/cli/src/tools/config/dependency.js @@ -0,0 +1,80 @@ +/** + * @flow + */ +import path from 'path'; + +import findAndroidAppFolder from '../android/findAndroidAppFolder'; +import findAndroidManifest from '../android/findManifest'; +import findAndroidPackageClassName from '../android/findPackageClassName'; +import readAndroidManifest from '../android/readManifest'; + +import findIOSPodspecName from '../ios/findPodspecName'; + +import type { + InputDependencyConfigIOS, + DependencyConfigIOS, + InputDependencyConfigAndroid, + DependencyConfigAndroid, +} from './types.flow'; + +const getAndroidSourceDir = (folder: string) => { + const androidFolder = findAndroidAppFolder(folder); + if (!androidFolder) { + return null; + } + return path.join(folder, androidFolder); +}; + +export function android( + folder: string, + userConfig: InputDependencyConfigAndroid, +): ?DependencyConfigAndroid { + const packageInstance = userConfig.packageInstance + ? userConfig.packageInstance + : (() => { + const sourceDir = getAndroidSourceDir(folder); + if (!sourceDir) { + return null; + } + const packageClassName = findAndroidPackageClassName(sourceDir); + return `new ${packageClassName}()`; + })(); + + const packageImportPath = userConfig.packageImportPath + ? userConfig.packageImportPath + : (() => { + const sourceDir = getAndroidSourceDir(folder); + if (!sourceDir) { + return null; + } + const manifestPath = findAndroidManifest(sourceDir); + if (!manifestPath) { + return null; + } + const manifest = readAndroidManifest(manifestPath); + const packageClassName = findAndroidPackageClassName(sourceDir); + const packageName = manifest.attr.package; + return `import ${packageName}.${packageClassName};`; + })(); + + if (packageInstance === null || packageImportPath === null) { + return null; + } + + return {packageImportPath, packageInstance}; +} + +export function ios( + folder: string, + userConfig: InputDependencyConfigIOS, +): ?DependencyConfigIOS { + const podspec = userConfig.podspec + ? userConfig.podspec + : findIOSPodspecName(folder); + + if (!podspec) { + return null; + } + + return {podspec}; +} diff --git a/packages/cli/src/tools/config/getProjectDependencies.js b/packages/cli/src/tools/config/getProjectDependencies.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index f6e41b30e..0f4bcd7bc 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -7,6 +7,7 @@ import merge from 'deepmerge'; import {get} from 'lodash'; import getProjectDependencies from '../../commands/link/getProjectDependencies'; +import * as dependency from './dependency'; const explorer = comsmiconfig('react-native'); @@ -15,19 +16,17 @@ type DependencyConfig = { android: ?DependencyConfigAndroid, }; -type DependencyConfigIOS = DetectedDependencyConfigIOS & { - project?: string, +type DependencyConfigIOS = { + podspec?: string, }; type DetectedDependencyConfigIOS = { podspec: string, }; -type DependencyConfigAndroid = DetectedDependencyConfigAndroid & { - sourceDir?: string, - manifestPath?: string, - packageName?: string, - packageClassName?: string, +type DependencyConfigAndroid = { + packageImportPath?: string, + packageInstance?: string, }; type DetectedDependencyConfigAndroid = { @@ -42,7 +41,10 @@ type PlatformConfig = { type Platforms = { [key: string]: PlatformConfig<*>, ios: PlatformConfig, - android: PlatformConfig, + android: PlatformConfig< + DependencyConfigAndroid, + DetectedDependencyConfigAndroid, + >, }; type ProjectConfig = { @@ -57,7 +59,10 @@ type Options = { root: ?string, }; -const defaultOptions = { +/** + * Default options + */ +const DEFAULT_OPTIONS: Options = { root: process.cwd(), }; @@ -69,51 +74,48 @@ function readConfigFromDisk(root: string) { function getDefaultConfig(config: ProjectConfig, root: string) { const platforms: Platforms = { ios: { - getDependencyConfig: require('../ios').getDependencyConfig, + getDependencyConfig: dependency.ios, }, android: { - getDependencyConfig: require('../android').getDependencyConfig, + getDependencyConfig: dependency.android, }, ...config.platforms, }; - const dependencies = getProjectDependencies(root).reduce( - (deps, dependency) => { - const folder = path.join(root, 'node_modules', dependency); - const dependencyConfig = readConfigFromDisk(folder); - - deps[dependency] = Object.keys(platforms).reduce( - (acc, platform) => { - const dependencyPlatformConfig = get( - dependencyConfig, - `dependency.${platform}`, - {}, - ); - if (dependencyPlatformConfig === null) { - return acc; - } - const detectedConfig = platforms[platform].getDependencyConfig( - folder, - dependencyPlatformConfig, - ); - if (detectedConfig === null) { - return acc; - } - acc[platform] = { - ...detectedConfig, - ...dependencyPlatformConfig, - }; + const dependencies = getProjectDependencies(root).reduce((deps, dep) => { + const folder = path.join(root, 'node_modules', dep); + const dependencyConfig = readConfigFromDisk(folder); + + deps[dep] = Object.keys(platforms).reduce( + (acc, platform) => { + const dependencyPlatformConfig = get( + dependencyConfig, + `dependency.${platform}`, + {}, + ); + if (dependencyPlatformConfig === null) { return acc; - }, - { - ios: null, - android: null, - }, - ); - return deps; - }, - {}, - ); + } + const detectedConfig = platforms[platform].getDependencyConfig( + folder, + dependencyPlatformConfig, + ); + if (detectedConfig === null) { + return acc; + } + acc[platform] = { + ...detectedConfig, + ...dependencyPlatformConfig, + }; + return acc; + }, + { + ios: null, + android: null, + }, + ); + return deps; + }, {}); return merge( { @@ -123,7 +125,7 @@ function getDefaultConfig(config: ProjectConfig, root: string) { ); } -async function loadConfig(opts: Options = defaultOptions): ProjectConfig { +async function loadConfig(opts: Options = DEFAULT_OPTIONS): ProjectConfig { const config = readConfigFromDisk(opts.root); return { diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js new file mode 100644 index 000000000..076ca1eeb --- /dev/null +++ b/packages/cli/src/tools/config/types.flow.js @@ -0,0 +1,50 @@ +/** + * @flow + */ +export type DependencyConfig = { + ios: ?DependencyConfigIOS, + android: ?DependencyConfigAndroid, +}; + +export type InputDependencyConfigIOS = { + podspec?: string, +}; + +export type DependencyConfigIOS = { + podspec: string, +}; + +export type InputDependencyConfigAndroid = { + packageImportPath?: string, + packageInstance?: string, +}; + +export type DependencyConfigAndroid = { + packageImportPath: string, + packageInstance: string, +}; + +export type PlatformConfig = { + getDependencyConfig: (string, T) => ?K, +}; + +export type Platforms = { + [key: string]: PlatformConfig<*>, + ios: PlatformConfig, + android: PlatformConfig< + InputDependencyConfigAndroid, + DependencyConfigAndroid, + >, +}; + +export type ProjectConfig = { + root: string, + reactNativePath: string, + dependencies: { + [key: string]: DependencyConfig, + }, +}; + +export type Options = { + root: ?string, +}; diff --git a/packages/cli/src/tools/ios/index.js b/packages/cli/src/tools/ios/index.js index 9ba49115e..6e0c537c9 100644 --- a/packages/cli/src/tools/ios/index.js +++ b/packages/cli/src/tools/ios/index.js @@ -58,18 +58,3 @@ export function projectConfig(folder, userConfig) { } export const dependencyConfig = projectConfig; - -/** - * New version of `ios` configuration - */ -export function getDependencyConfig(folder, userConfig) { - const project = userConfig.project || findProject(folder); - - if (!project) { - return null; - } - - return { - podspec: findPodspecName(folder), - }; -} From 4afecc220644d5adcfb6911022b104987de303ca Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 21 Mar 2019 21:54:41 +0000 Subject: [PATCH 09/47] add initial native modules gradle script --- packages/cli/native_modules.gradle | 193 +++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 packages/cli/native_modules.gradle diff --git a/packages/cli/native_modules.gradle b/packages/cli/native_modules.gradle new file mode 100644 index 000000000..8bcf7fb23 --- /dev/null +++ b/packages/cli/native_modules.gradle @@ -0,0 +1,193 @@ +import groovy.json.JsonSlurper +import org.gradle.initialization.DefaultSettings + +def generatedFileName = "PackageList.java" +def generatedFileContentsTemplate = """ +package com.facebook.react; + +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainReactPackage; +import java.util.Arrays; +import java.util.List; + +{{ packageImports }} + +public class PackageList { + public static List getPackages () { + return Arrays.asList( + new MainReactPackage(){{ packageClassInstances }} + ); + } +} +""" + +class ReactNativeModules { + private Project project + private DefaultSettings defaultSettings + private ExtraPropertiesExtension extension + private ArrayList> reactNativeModules + private static String REACT_NATIVE_CONFIG_CMD = "react-native config" + + void applySettingsGradle(DefaultSettings defaultSettings, ExtraPropertiesExtension extraPropertiesExtension) { + this.defaultSettings = defaultSettings + this.extension = extraPropertiesExtension + this.reactNativeModules = this.getReactNativeConfig() + + addReactNativeModuleProjects() + } + + void applyBuildGradle(Project project, ExtraPropertiesExtension extraPropertiesExtension) { + this.project = project + this.extension = extraPropertiesExtension + this.reactNativeModules = this.getReactNativeConfig() + + addReactNativeModuleDependencies() + } + + /** + * Include the react native modules android projects and specify their project directory + */ + void addReactNativeModuleProjects() { + reactNativeModules.forEach { reactNativeModule -> + String name = reactNativeModule["name"] + String androidSourceDir = reactNativeModule["androidSourceDir"] + defaultSettings.include(":${name}") + defaultSettings.project(":${name}").projectDir = new File("${androidSourceDir}") + } + } + + /** + * Adds the react native modules as dependencies to the users `app` project + */ + void addReactNativeModuleDependencies() { + reactNativeModules.forEach { reactNativeModule -> + def name = reactNativeModule["name"] + project.dependencies { + // TODO(salakar): other dependency scope methods such as `api` + implementation project(path: ":${name}") + } + } + } + + /** + * This returns the users project root (e.g. where the node_modules dir is located). + * + * This defaults to up one directory from the root android directory unless the user has defined + * a `ext.reactNativeProjectRoot` extension property + * + * @return + */ + File getReactNativeProjectRoot() { + if (this.extension.has("reactNativeProjectRoot")) { + // allow custom React Native project roots for non-standard directory structures + return new File(this.extension.get("reactNativeProjectRoot")) + } + + File androidRoot + + if (this.project) { + androidRoot = this.project.rootProject.projectDir + } else { + androidRoot = this.defaultSettings.rootProject.projectDir + } + + return androidRoot.parentFile + } + + /** + * Code-gen a java file with all the detected ReactNativePackage instances automatically added + * + * @param outputDir + * @param generatedFileName + * @param generatedFileContentsTemplate + */ + void generatePackagesFile(File outputDir, String generatedFileName, String generatedFileContentsTemplate) { + ArrayList>[] packages = this.reactNativeModules + + String packageImports = "" + String packageClassInstances = "" + + if (packages.size() > 0) { + packageImports = packages.collect { "// ${it.name}\n${it.packageImportPath}" }.join(';\n') + packageClassInstances = ",\n " + packages.collect { it.packageInstance }.join(',') + } + + String generatedFileContents = generatedFileContentsTemplate + .replace("{{ packageImports }}", packageImports) + .replace("{{ packageClassInstances }}", packageClassInstances) + + outputDir.mkdirs() + final FileTreeBuilder treeBuilder = new FileTreeBuilder(outputDir) + treeBuilder.file(generatedFileName).newWriter().withWriter { w -> + w << generatedFileContents + } + } + + /** + * Runs a process to call the React Native CLI Config command and parses the output + * + * @return ArrayList < HashMap < String , String > > + */ + ArrayList> getReactNativeConfig() { + if (this.reactNativeModules != null) return this.reactNativeModules + ArrayList> reactNativeModules = new ArrayList>() + + def cmdProcess = Runtime.getRuntime().exec(REACT_NATIVE_CONFIG_CMD, null, getReactNativeProjectRoot()) + def reactNativeConfigOutput = cmdProcess.in.text + + // TODO(salakar): validate output to provide better user warnings/errors, e.g. detect if `react-native` exists, or cli version contains the config command + def json = new JsonSlurper().parseText(reactNativeConfigOutput) + + json.forEach { value -> + String name = value["name"] + String path = value["path"] + def config = value["config"] + def androidConfig = config["android"] + + if (androidConfig != null) { + String androidSourceDir = androidConfig["sourceDir"] + HashMap reactNativeModuleConfig = new HashMap() + reactNativeModuleConfig.put("name", name) + reactNativeModuleConfig.put("path", path) + reactNativeModuleConfig.put("androidSourceDir", androidSourceDir) + reactNativeModuleConfig.put("packageInstance", androidConfig["packageInstance"]) + reactNativeModuleConfig.put("packageImportPath", androidConfig["packageImportPath"]) + reactNativeModules.add(reactNativeModuleConfig) + } + } + + return reactNativeModules + } +} + +/** ----------------------- + * Exported Extensions + * ------------------------ */ + +def autoModules = new ReactNativeAutoModules() + +ext.applyNativeModulesSettingsGradle = { DefaultSettings defaultSettings -> + autoModules.applySettingsGradle(defaultSettings, ext) +} + +ext.applyNativeModulesAppBuildGradle = { Project project -> + autoModules.applyBuildGradle(project, ext) + + def generatedSrcDir = new File(buildDir, "generated/rncli/src/main/java/com/facebook/react") + + task generatePackageList << { + autoModules.generatePackagesFile(generatedSrcDir, generatedFileName, generatedFileContentsTemplate) + } + + preBuild.dependsOn generatePackageList + + android { + sourceSets { + main { + java { + srcDirs += generatedSrcDir + } + } + } + } +} From 8f947cee560e8cf946133a00b8121a687ea48fe9 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 21 Mar 2019 22:10:49 +0000 Subject: [PATCH 10/47] [config] expose android sourceDir for react native module packages --- packages/cli/src/tools/config/dependency.js | 6 +++++- packages/cli/src/tools/config/types.flow.js | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/tools/config/dependency.js b/packages/cli/src/tools/config/dependency.js index bf79fe048..e8c5b0c3d 100644 --- a/packages/cli/src/tools/config/dependency.js +++ b/packages/cli/src/tools/config/dependency.js @@ -61,7 +61,11 @@ export function android( return null; } - return {packageImportPath, packageInstance}; + return { + packageImportPath, + packageInstance, + sourceDir: getAndroidSourceDir(folder), + }; } export function ios( diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 076ca1eeb..fc7392438 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -22,6 +22,7 @@ export type InputDependencyConfigAndroid = { export type DependencyConfigAndroid = { packageImportPath: string, packageInstance: string, + sourceDir: string, }; export type PlatformConfig = { From 4af1cfec671f9c501ed82681df2610ffa3c3ffb8 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 21 Mar 2019 23:07:04 +0000 Subject: [PATCH 11/47] rework config reader to new schema + add logging --- packages/cli/native_modules.gradle | 48 ++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/cli/native_modules.gradle b/packages/cli/native_modules.gradle index 8bcf7fb23..46e2fe381 100644 --- a/packages/cli/native_modules.gradle +++ b/packages/cli/native_modules.gradle @@ -22,12 +22,19 @@ public class PackageList { """ class ReactNativeModules { + private Logger logger private Project project private DefaultSettings defaultSettings private ExtraPropertiesExtension extension private ArrayList> reactNativeModules + + private static String LOG_PREFIX = ":ReactNative:" private static String REACT_NATIVE_CONFIG_CMD = "react-native config" + ReactNativeModules(Logger logger) { + this.logger = logger + } + void applySettingsGradle(DefaultSettings defaultSettings, ExtraPropertiesExtension extraPropertiesExtension) { this.defaultSettings = defaultSettings this.extension = extraPropertiesExtension @@ -79,8 +86,10 @@ class ReactNativeModules { */ File getReactNativeProjectRoot() { if (this.extension.has("reactNativeProjectRoot")) { + File rnRoot = File(this.extension.get("reactNativeProjectRoot")) // allow custom React Native project roots for non-standard directory structures - return new File(this.extension.get("reactNativeProjectRoot")) + this.logger.debug("${LOG_PREFIX}Using custom React Native project root path '${rnRoot.toString()}'") + return rnRoot } File androidRoot @@ -91,6 +100,7 @@ class ReactNativeModules { androidRoot = this.defaultSettings.rootProject.projectDir } + this.logger.debug("${LOG_PREFIX}Using default React Native project root path '${androidRoot.parentFile.toString()}'") return androidRoot.parentFile } @@ -132,27 +142,39 @@ class ReactNativeModules { if (this.reactNativeModules != null) return this.reactNativeModules ArrayList> reactNativeModules = new ArrayList>() - def cmdProcess = Runtime.getRuntime().exec(REACT_NATIVE_CONFIG_CMD, null, getReactNativeProjectRoot()) - def reactNativeConfigOutput = cmdProcess.in.text + def cmdProcess + + try { + cmdProcess = Runtime.getRuntime().exec(REACT_NATIVE_CONFIG_CMD, null, getReactNativeProjectRoot()) + } catch(Exception exception) { + if (exception.message.contains("No such file or directory")) { + this.logger.warn("${LOG_PREFIX}Skipping automatic imports of native modules. (NO_GLOBAL_CLI)") + return reactNativeModules + } - // TODO(salakar): validate output to provide better user warnings/errors, e.g. detect if `react-native` exists, or cli version contains the config command + throw exception + } + + def reactNativeConfigOutput = cmdProcess.in.text def json = new JsonSlurper().parseText(reactNativeConfigOutput) + def dependencies = json["dependencies"] - json.forEach { value -> - String name = value["name"] - String path = value["path"] - def config = value["config"] - def androidConfig = config["android"] + dependencies.each { name, value -> + def androidConfig = value["android"] if (androidConfig != null) { - String androidSourceDir = androidConfig["sourceDir"] + this.logger.info("${LOG_PREFIX}Automatically adding native module '${name}'") + HashMap reactNativeModuleConfig = new HashMap() reactNativeModuleConfig.put("name", name) - reactNativeModuleConfig.put("path", path) - reactNativeModuleConfig.put("androidSourceDir", androidSourceDir) + reactNativeModuleConfig.put("androidSourceDir", androidConfig["sourceDir"]) reactNativeModuleConfig.put("packageInstance", androidConfig["packageInstance"]) reactNativeModuleConfig.put("packageImportPath", androidConfig["packageImportPath"]) + this.logger.trace("${LOG_PREFIX}'${name}': ${reactNativeModuleConfig.toMapString()}") + reactNativeModules.add(reactNativeModuleConfig) + } else { + this.logger.info("${LOG_PREFIX}Skipping native module '${name}'") } } @@ -164,7 +186,7 @@ class ReactNativeModules { * Exported Extensions * ------------------------ */ -def autoModules = new ReactNativeAutoModules() +def autoModules = new ReactNativeModules(logger) ext.applyNativeModulesSettingsGradle = { DefaultSettings defaultSettings -> autoModules.applySettingsGradle(defaultSettings, ext) From 830e92979658616c9d961f5f8b3152e65e7ae866 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 21 Mar 2019 23:46:32 +0000 Subject: [PATCH 12/47] handle null android sourceDir --- packages/cli/native_modules.gradle | 2 +- packages/cli/src/tools/config/types.flow.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/native_modules.gradle b/packages/cli/native_modules.gradle index 46e2fe381..db48fa15f 100644 --- a/packages/cli/native_modules.gradle +++ b/packages/cli/native_modules.gradle @@ -162,7 +162,7 @@ class ReactNativeModules { dependencies.each { name, value -> def androidConfig = value["android"] - if (androidConfig != null) { + if (androidConfig != null && androidConfig["sourceDir"] != null) { this.logger.info("${LOG_PREFIX}Automatically adding native module '${name}'") HashMap reactNativeModuleConfig = new HashMap() diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index fc7392438..5b1f5c413 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -22,7 +22,7 @@ export type InputDependencyConfigAndroid = { export type DependencyConfigAndroid = { packageImportPath: string, packageInstance: string, - sourceDir: string, + sourceDir: string | null, }; export type PlatformConfig = { From 505e4d01a721fff404d37c838500bcf759cc44bf Mon Sep 17 00:00:00 2001 From: Salakar Date: Sat, 23 Mar 2019 20:52:02 +0000 Subject: [PATCH 13/47] add support for Packages that accept various app instance arguments --- packages/cli/native_modules.gradle | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/cli/native_modules.gradle b/packages/cli/native_modules.gradle index db48fa15f..969725f8a 100644 --- a/packages/cli/native_modules.gradle +++ b/packages/cli/native_modules.gradle @@ -5,6 +5,10 @@ def generatedFileName = "PackageList.java" def generatedFileContentsTemplate = """ package com.facebook.react; +import android.app.Application; +import android.content.Context; +import android.content.res.Resources; + import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import java.util.Arrays; @@ -13,7 +17,28 @@ import java.util.List; {{ packageImports }} public class PackageList { - public static List getPackages () { + private ReactNativeHost reactNativeHost; + public PackageList(ReactNativeHost reactNativeHost) { + this.reactNativeHost = reactNativeHost; + } + + private ReactNativeHost getReactNativeHost() { + return this.reactNativeHost; + } + + private Resources getResources() { + return this.getApplication().getResources(); + } + + private Application getApplication() { + return this.reactNativeHost.getApplication(); + } + + private Context getApplicationContext() { + return this.getApplication().getApplicationContext(); + } + + public List getPackages() { return Arrays.asList( new MainReactPackage(){{ packageClassInstances }} ); From 64f3f5a3db137c5cee4bbe26b18b26a7c55a205e Mon Sep 17 00:00:00 2001 From: Salakar Date: Sat, 23 Mar 2019 21:51:14 +0000 Subject: [PATCH 14/47] add build config support (for packages that use it in args) --- packages/cli/native_modules.gradle | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/cli/native_modules.gradle b/packages/cli/native_modules.gradle index 969725f8a..17d87e2a4 100644 --- a/packages/cli/native_modules.gradle +++ b/packages/cli/native_modules.gradle @@ -135,15 +135,17 @@ class ReactNativeModules { * @param outputDir * @param generatedFileName * @param generatedFileContentsTemplate + * @param applicationId */ - void generatePackagesFile(File outputDir, String generatedFileName, String generatedFileContentsTemplate) { + void generatePackagesFile(File outputDir, String generatedFileName, String generatedFileContentsTemplate, String applicationId) { ArrayList>[] packages = this.reactNativeModules String packageImports = "" String packageClassInstances = "" if (packages.size() > 0) { - packageImports = packages.collect { "// ${it.name}\n${it.packageImportPath}" }.join(';\n') + packageImports = "import ${applicationId}.BuildConfig;\n\n" + packageImports = packageImports + packages.collect { "// ${it.name}\n${it.packageImportPath}" }.join(';\n') packageClassInstances = ",\n " + packages.collect { it.packageInstance }.join(',') } @@ -220,10 +222,16 @@ ext.applyNativeModulesSettingsGradle = { DefaultSettings defaultSettings -> ext.applyNativeModulesAppBuildGradle = { Project project -> autoModules.applyBuildGradle(project, ext) + def applicationId def generatedSrcDir = new File(buildDir, "generated/rncli/src/main/java/com/facebook/react") + // TODO(salakar): not sure if this is the best way of getting the package name (used to import BuildConfig) + project.android.applicationVariants.all { variant -> + applicationId = [variant.mergedFlavor.applicationId, variant.buildType.applicationIdSuffix].findAll().join() + } + task generatePackageList << { - autoModules.generatePackagesFile(generatedSrcDir, generatedFileName, generatedFileContentsTemplate) + autoModules.generatePackagesFile(generatedSrcDir, generatedFileName, generatedFileContentsTemplate, applicationId) } preBuild.dependsOn generatePackageList From 933512a60d3d4bf7d11b1fe2e30b87ff648a9a06 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 16:58:52 +0100 Subject: [PATCH 15/47] WIP part 2' --- packages/cli/package.json | 1 + packages/cli/src/cliEntry.js | 20 +- packages/cli/src/commands/config/config.js | 8 +- packages/cli/src/commands/index.js | 22 +- packages/cli/src/tools/config/dependency.js | 80 ------- .../cli/src/tools/config/findDependencies.js | 33 +++ packages/cli/src/tools/config/index.js | 209 ++++++------------ .../src/tools/config/readConfigFromDisk.js | 77 +++++++ .../tools/config/resolveReactNativePath.js | 21 ++ packages/cli/src/tools/config/types.flow.js | 88 ++++---- packages/cli/src/tools/loadMetroConfig.js | 7 +- 11 files changed, 284 insertions(+), 282 deletions(-) delete mode 100644 packages/cli/src/tools/config/dependency.js create mode 100644 packages/cli/src/tools/config/findDependencies.js create mode 100644 packages/cli/src/tools/config/readConfigFromDisk.js create mode 100644 packages/cli/src/tools/config/resolveReactNativePath.js diff --git a/packages/cli/package.json b/packages/cli/package.json index ccf260300..6be959b38 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -34,6 +34,7 @@ "glob": "^7.1.1", "graceful-fs": "^4.1.3", "inquirer": "^3.0.6", + "jest-validate": "^24.5.0", "lodash": "^4.17.5", "metro": "^0.53.1", "metro-config": "^0.53.1", diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index a327c8bc1..e1dcf3481 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -11,7 +11,9 @@ import chalk from 'chalk'; import childProcess from 'child_process'; import commander from 'commander'; import path from 'path'; + import type {CommandT, ContextT} from './tools/types.flow'; + import {getCommands} from './commands'; import init from './commands/init/init'; import assertRequiredOptions from './tools/assertRequiredOptions'; @@ -19,6 +21,7 @@ import logger from './tools/logger'; import findPlugins from './tools/findPlugins'; import {setProjectDir} from './tools/PackageManager'; import pkgJson from '../package.json'; +import loadConfig from './tools/config'; commander .option('--version', 'Print CLI version') @@ -80,7 +83,7 @@ function printUnknownCommand(cmdName) { } } -const addCommand = (command: CommandT) => { +const addCommand = (command: CommandT, ctx: ContextT) => { const options = command.options || []; const cmd = commander @@ -92,7 +95,7 @@ const addCommand = (command: CommandT) => { try { assertRequiredOptions(options, passedOptions); - return command.func(argv, passedOptions); + return command.func(argv, ctx, passedOptions); } catch (e) { handleError(e); } @@ -143,13 +146,18 @@ async function setupAndRun() { } } - // @todo: Let's use process.cwd directly where possible - const root = process.cwd(); + const ctx = { + ...loadConfig(), + // @todo: Let's use process.cwd directly where possible + root: process.cwd(), + }; // @todo this shouldn't be called here - setProjectDir(root); + setProjectDir(ctx.root); + + const commands = getCommands(ctx.commands); - getCommands(root).forEach(command => addCommand(command)); + commands.forEach(command => addCommand(command, ctx)); commander.parse(process.argv); diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js index 0bb711a24..c80463319 100644 --- a/packages/cli/src/commands/config/config.js +++ b/packages/cli/src/commands/config/config.js @@ -6,14 +6,10 @@ * * @format */ -import loadConfig from '../../tools/config'; - export default { name: 'config', description: 'Print CLI configuration', - func: async () => { - const config = await loadConfig(); - - console.log(JSON.stringify(config, null, 2)); + func: async (_, ctx) => { + console.log(JSON.stringify(ctx, null, 2)); }, }; diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index 55a745d7e..737b38fbd 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -4,7 +4,6 @@ import path from 'path'; -import findPlugins from '../tools/findPlugins'; import logger from '../tools/logger'; import type { @@ -59,10 +58,10 @@ const loadLocalCommands: Array = [ * This checks all CLI plugins for presence of 3rd party packages that define commands * and loads them */ -const loadProjectCommands = (root: string): Array => { - const plugins = findPlugins(root); - - return plugins.commands.reduce((acc: Array, pathToCommands) => { +const loadProjectCommands = ( + commands: Array, +): Array => { + return commands.reduce((acc: Array, pathToCommands: string) => { /** * `pathToCommand` is a path to a file where commands are defined, relative to `node_modules` * folder. @@ -78,12 +77,17 @@ const loadProjectCommands = (root: string): Array => { .join(path.sep) : pathToCommands.split(path.sep)[0]; - const pkg = require(path.join(root, 'node_modules', name, 'package.json')); + const pkg = require(path.join( + process.cwd(), + 'node_modules', + name, + 'package.json', + )); const requiredCommands: | ProjectCommandT | Array = require(path.join( - root, + process.cwd(), 'node_modules', pathToCommands, )); @@ -101,7 +105,7 @@ const loadProjectCommands = (root: string): Array => { /** * Loads all the commands inside a given `root` folder */ -export function getCommands(root: string): Array { +export function getCommands(commands: Array): Array { return [ ...loadLocalCommands, { @@ -115,6 +119,6 @@ export function getCommands(root: string): Array { ); }, }, - ...loadProjectCommands(root), + ...loadProjectCommands(commands), ]; } diff --git a/packages/cli/src/tools/config/dependency.js b/packages/cli/src/tools/config/dependency.js deleted file mode 100644 index bf79fe048..000000000 --- a/packages/cli/src/tools/config/dependency.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @flow - */ -import path from 'path'; - -import findAndroidAppFolder from '../android/findAndroidAppFolder'; -import findAndroidManifest from '../android/findManifest'; -import findAndroidPackageClassName from '../android/findPackageClassName'; -import readAndroidManifest from '../android/readManifest'; - -import findIOSPodspecName from '../ios/findPodspecName'; - -import type { - InputDependencyConfigIOS, - DependencyConfigIOS, - InputDependencyConfigAndroid, - DependencyConfigAndroid, -} from './types.flow'; - -const getAndroidSourceDir = (folder: string) => { - const androidFolder = findAndroidAppFolder(folder); - if (!androidFolder) { - return null; - } - return path.join(folder, androidFolder); -}; - -export function android( - folder: string, - userConfig: InputDependencyConfigAndroid, -): ?DependencyConfigAndroid { - const packageInstance = userConfig.packageInstance - ? userConfig.packageInstance - : (() => { - const sourceDir = getAndroidSourceDir(folder); - if (!sourceDir) { - return null; - } - const packageClassName = findAndroidPackageClassName(sourceDir); - return `new ${packageClassName}()`; - })(); - - const packageImportPath = userConfig.packageImportPath - ? userConfig.packageImportPath - : (() => { - const sourceDir = getAndroidSourceDir(folder); - if (!sourceDir) { - return null; - } - const manifestPath = findAndroidManifest(sourceDir); - if (!manifestPath) { - return null; - } - const manifest = readAndroidManifest(manifestPath); - const packageClassName = findAndroidPackageClassName(sourceDir); - const packageName = manifest.attr.package; - return `import ${packageName}.${packageClassName};`; - })(); - - if (packageInstance === null || packageImportPath === null) { - return null; - } - - return {packageImportPath, packageInstance}; -} - -export function ios( - folder: string, - userConfig: InputDependencyConfigIOS, -): ?DependencyConfigIOS { - const podspec = userConfig.podspec - ? userConfig.podspec - : findIOSPodspecName(folder); - - if (!podspec) { - return null; - } - - return {podspec}; -} diff --git a/packages/cli/src/tools/config/findDependencies.js b/packages/cli/src/tools/config/findDependencies.js new file mode 100644 index 000000000..0b5c50db0 --- /dev/null +++ b/packages/cli/src/tools/config/findDependencies.js @@ -0,0 +1,33 @@ +/** + * @flow + */ + +import path from 'path'; +import {union} from 'lodash'; + +const pluginRe = new RegExp( + [ + '^react-native-', + '^@(.*)/react-native-', + '^@react-native(.*)/(?!rnpm-plugin-)', + ].join('|'), +); + +function findDependencies() { + let pjson; + + try { + pjson = require(path.join(process.cwd(), 'package.json')); + } catch (e) { + return []; + } + + const deps = union( + Object.keys(pjson.dependencies || {}), + Object.keys(pjson.devDependencies || {}), + ); + + return deps.filter(dependency => pluginRe.test(dependency)); +} + +export default findDependencies; diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 0f4bcd7bc..045197844 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -1,153 +1,84 @@ /** - * @flow + * */ -import comsmiconfig from 'cosmiconfig'; +import {get, mapValues} from 'lodash'; import path from 'path'; import merge from 'deepmerge'; -import {get} from 'lodash'; -import getProjectDependencies from '../../commands/link/getProjectDependencies'; -import * as dependency from './dependency'; - -const explorer = comsmiconfig('react-native'); - -type DependencyConfig = { - ios: ?DependencyConfigIOS, - android: ?DependencyConfigAndroid, -}; - -type DependencyConfigIOS = { - podspec?: string, -}; - -type DetectedDependencyConfigIOS = { - podspec: string, -}; - -type DependencyConfigAndroid = { - packageImportPath?: string, - packageInstance?: string, -}; - -type DetectedDependencyConfigAndroid = { - packageImportPath: string, - packageInstance: string, -}; - -type PlatformConfig = { - getDependencyConfig: (string, T) => ?K, -}; - -type Platforms = { - [key: string]: PlatformConfig<*>, - ios: PlatformConfig, - android: PlatformConfig< - DependencyConfigAndroid, - DetectedDependencyConfigAndroid, - >, -}; - -type ProjectConfig = { - root: string, - reactNativePath: string, - dependencies: { - [key: string]: DependencyConfig, - }, -}; - -type Options = { - root: ?string, -}; - -/** - * Default options - */ -const DEFAULT_OPTIONS: Options = { - root: process.cwd(), -}; - -function readConfigFromDisk(root: string) { - const {config} = explorer.searchSync(root) || {config: {}}; - return config; -} - -function getDefaultConfig(config: ProjectConfig, root: string) { - const platforms: Platforms = { - ios: { - getDependencyConfig: dependency.ios, - }, - android: { - getDependencyConfig: dependency.android, +import * as ios from '../ios'; +import * as android from '../android'; + +import findDependencies from './findDependencies'; + +import { + readProjectConfigFromDisk, + readDependencyConfigFromDisk, +} from './readConfigFromDisk'; + +function loadConfig() { + const defaultConfig = findDependencies().reduce( + (acc, dependencyName) => { + const config = readDependencyConfigFromDisk(dependencyName); + return { + dependencies: { + ...acc.dependencies, + get [dependencyName]() { + return Object.keys(acc.platforms).reduce((dependency, platform) => { + const platformConfig = get(config, `dependency.${platform}`, {}); + if (platformConfig === null) { + return dependency; + } + const detectedConfig = acc.platforms[platform].dependencyConfig( + config.root, + platformConfig, + ); + if (detectedConfig === null) { + return dependency; + } + dependency[platform] = { + ...detectedConfig, + ...platformConfig, + }; + return dependency; + }, {}); + }, + }, + commands: acc.commands.concat( + (config.commands || []).map(pathToCommand => + path.join(dependencyName, pathToCommand), + ), + ), + platforms: { + ...acc.platforms, + ...mapValues(config.platforms, pathToPlatform => + require(path.join(dependencyName, pathToPlatform)), + ), + }, + haste: { + providesModuleNodeModules: acc.haste.providesModuleNodeModules.concat( + get(config, 'haste.providesModuleNodeModules', []), + ), + platforms: acc.haste.platforms.concat( + get(config, 'haste.platforms', []), + ), + }, + }; }, - ...config.platforms, - }; - - const dependencies = getProjectDependencies(root).reduce((deps, dep) => { - const folder = path.join(root, 'node_modules', dep); - const dependencyConfig = readConfigFromDisk(folder); - - deps[dep] = Object.keys(platforms).reduce( - (acc, platform) => { - const dependencyPlatformConfig = get( - dependencyConfig, - `dependency.${platform}`, - {}, - ); - if (dependencyPlatformConfig === null) { - return acc; - } - const detectedConfig = platforms[platform].getDependencyConfig( - folder, - dependencyPlatformConfig, - ); - if (detectedConfig === null) { - return acc; - } - acc[platform] = { - ...detectedConfig, - ...dependencyPlatformConfig, - }; - return acc; + { + dependencies: {}, + commands: [], + platforms: { + ios, + android, }, - { - ios: null, - android: null, + haste: { + providesModuleNodeModules: [], + platforms: [], }, - ); - return deps; - }, {}); - - return merge( - { - dependencies, }, - config, ); -} - -async function loadConfig(opts: Options = DEFAULT_OPTIONS): ProjectConfig { - const config = readConfigFromDisk(opts.root); - return { - ...getDefaultConfig(config, opts.root), - root: opts.root, - reactNativePath: config.reactNativePath - ? path.resolve(config.reactNativePath) - : (() => { - try { - return path.dirname( - // $FlowIssue: Wrong `require.resolve` type definition - require.resolve('react-native/package.json', { - paths: [opts.root], - }), - ); - } catch (_ignored) { - throw new Error( - 'Unable to find React Native files. Make sure "react-native" module is installed in your project dependencies.', - ); - } - })(), - }; + return merge(defaultConfig, readProjectConfigFromDisk()); } export default loadConfig; diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js new file mode 100644 index 000000000..af8be3890 --- /dev/null +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -0,0 +1,77 @@ +/** + * @flow + * + * Loads and validates a project configuration + */ + +import comsmiconfig from 'cosmiconfig'; +import path from 'path'; +import {validate} from 'jest-validate'; + +import type {ProjectConfig, DependencyConfig} from './types.flow'; + +import resolveReactNativePath from './resolveReactNativePath'; + +const exampleProjectConfig: ProjectConfig = { + reactNativePath: '.', +}; + +const exampleDependencyConfig = { + dependency: { + android: '', + ios: '', + }, + platforms: { + windows: './platform.js', + }, + commands: ['./local-cli/runWindows/runWindows.js'], +}; + +const exampleConfig = { + ...exampleProjectConfig, + ...exampleDependencyConfig, +}; + +const searchPlaces = ['react-native.config.js', 'package.json']; + +export function readProjectConfigFromDisk(): ProjectConfig { + const explorer = comsmiconfig('react-native', {searchPlaces}); + + const {config} = explorer.searchSync() || {config: {}}; + + validate(config, {exampleConfig}); + + return { + ...config, + reactNativePath: config.reactNativePath + ? config.reactNativePath + : resolveReactNativePath(), + }; +} + +export function readDependencyConfigFromDisk( + dependencyName: string, +): DependencyConfig { + const root = path.resolve(process.cwd(), 'node_modules', dependencyName); + + const explorer = comsmiconfig('react-native', { + stopDir: root, + searchPlaces, + }); + + const {config} = explorer.searchSync(root) || {config: {}}; + + validate(config, { + exampleConfig, + title: { + warning: `Warnings from ${dependencyName}`, + error: `Errors from ${dependencyName}`, + deprecation: `Deprecations from ${dependencyName}`, + }, + }); + + return { + ...config, + root, + }; +} diff --git a/packages/cli/src/tools/config/resolveReactNativePath.js b/packages/cli/src/tools/config/resolveReactNativePath.js new file mode 100644 index 000000000..1ee1564e4 --- /dev/null +++ b/packages/cli/src/tools/config/resolveReactNativePath.js @@ -0,0 +1,21 @@ +/** + * @flow + */ +import path from 'path'; + +function resolveReactNativePath() { + try { + return path.dirname( + // $FlowIssue: Wrong `require.resolve` type definition + require.resolve('react-native/package.json', { + paths: [process.cwd()], + }), + ); + } catch (_ignored) { + throw new Error( + 'Unable to find React Native files. Make sure "react-native" module is installed in your project dependencies.', + ); + } +} + +export default resolveReactNativePath; diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 076ca1eeb..25aa29e32 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -1,50 +1,64 @@ /** * @flow */ -export type DependencyConfig = { - ios: ?DependencyConfigIOS, - android: ?DependencyConfigAndroid, -}; -export type InputDependencyConfigIOS = { - podspec?: string, +export type ProjectConfig = { + reactNativePath: string, }; -export type DependencyConfigIOS = { - podspec: string, +export type DependencyConfig = { + dependency: { + android?: string, + ios?: string, + }, + commands?: [], + platforms?: {}, }; -export type InputDependencyConfigAndroid = { - packageImportPath?: string, - packageInstance?: string, -}; +// export type DependencyConfig = { +// ios: ?DependencyConfigIOS, +// android: ?DependencyConfigAndroid, +// }; -export type DependencyConfigAndroid = { - packageImportPath: string, - packageInstance: string, -}; +// export type InputDependencyConfigIOS = { +// podspec?: string, +// }; -export type PlatformConfig = { - getDependencyConfig: (string, T) => ?K, -}; +// export type DependencyConfigIOS = { +// podspec: string, +// }; -export type Platforms = { - [key: string]: PlatformConfig<*>, - ios: PlatformConfig, - android: PlatformConfig< - InputDependencyConfigAndroid, - DependencyConfigAndroid, - >, -}; +// export type InputDependencyConfigAndroid = { +// packageImportPath?: string, +// packageInstance?: string, +// }; -export type ProjectConfig = { - root: string, - reactNativePath: string, - dependencies: { - [key: string]: DependencyConfig, - }, -}; +// export type DependencyConfigAndroid = { +// packageImportPath: string, +// packageInstance: string, +// }; -export type Options = { - root: ?string, -}; +// export type PlatformConfig = { +// getDependencyConfig: (string, T) => ?K, +// }; + +// export type Platforms = { +// [key: string]: PlatformConfig<*>, +// ios: PlatformConfig, +// android: PlatformConfig< +// InputDependencyConfigAndroid, +// DependencyConfigAndroid, +// >, +// }; + +// export type ProjectConfig = { +// root: string, +// reactNativePath: string, +// dependencies: { +// [key: string]: DependencyConfig, +// }, +// }; + +// export type Options = { +// root: ?string, +// }; diff --git a/packages/cli/src/tools/loadMetroConfig.js b/packages/cli/src/tools/loadMetroConfig.js index 3d15e59d3..6323c4073 100644 --- a/packages/cli/src/tools/loadMetroConfig.js +++ b/packages/cli/src/tools/loadMetroConfig.js @@ -6,7 +6,6 @@ import path from 'path'; import {createBlacklist} from 'metro'; import {loadConfig} from 'metro-config'; import type {ContextT} from './types.flow'; -import findPlugins from './findPlugins'; import findSymlinkedModules from './findSymlinkedModules'; const resolveSymlinksForRoots = roots => @@ -32,16 +31,14 @@ const getBlacklistRE = () => createBlacklist([/.*\/__fixtures__\/.*/]); * Otherwise, a.native.js will not load on Windows or other platforms */ export const getDefaultConfig = (ctx: ContextT) => { - const plugins = findPlugins(ctx.root); - return { resolver: { resolverMainFields: ['react-native', 'browser', 'main'], blacklistRE: getBlacklistRE(), - platforms: ['ios', 'android', 'native', ...plugins.haste.platforms], + platforms: ['ios', 'android', 'native', ...ctx.haste.platforms], providesModuleNodeModules: [ 'react-native', - ...plugins.haste.providesModuleNodeModules, + ...ctx.haste.providesModuleNodeModules, ], hasteImplModulePath: path.join(ctx.reactNativePath, 'jest/hasteImpl'), }, From 9815ff222c949dc07c33d36582a98b5f32eb40b8 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 17:02:46 +0100 Subject: [PATCH 16/47] Simplify diff by removing things we can tweak later --- packages/cli/src/cliEntry.js | 21 ++++++++------------- packages/cli/src/commands/index.js | 20 ++++++++------------ packages/cli/src/tools/config/index.js | 1 + 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index e1dcf3481..f55250e09 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -89,16 +89,16 @@ const addCommand = (command: CommandT, ctx: ContextT) => { const cmd = commander .command(command.name) .description(command.description) - .action(async function handleAction(...args) { + .action(function handleAction(...args) { const passedOptions = this.opts(); const argv: Array = Array.from(args).slice(0, -1); - try { - assertRequiredOptions(options, passedOptions); - return command.func(argv, ctx, passedOptions); - } catch (e) { - handleError(e); - } + Promise.resolve() + .then(() => { + assertRequiredOptions(options, passedOptions); + return command.func(argv, ctx, passedOptions); + }) + .catch(handleError); }); cmd.helpInformation = printHelpInformation.bind( @@ -146,13 +146,8 @@ async function setupAndRun() { } } - const ctx = { - ...loadConfig(), - // @todo: Let's use process.cwd directly where possible - root: process.cwd(), - }; + const ctx = loadConfig(); - // @todo this shouldn't be called here setProjectDir(ctx.root); const commands = getCommands(ctx.commands); diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index 737b38fbd..3c2ecd0e1 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -58,9 +58,10 @@ const loadLocalCommands: Array = [ * This checks all CLI plugins for presence of 3rd party packages that define commands * and loads them */ -const loadProjectCommands = ( - commands: Array, -): Array => { +const loadProjectCommands = ({ + root, + commands, +}: ContextT): Array => { return commands.reduce((acc: Array, pathToCommands: string) => { /** * `pathToCommand` is a path to a file where commands are defined, relative to `node_modules` @@ -77,17 +78,12 @@ const loadProjectCommands = ( .join(path.sep) : pathToCommands.split(path.sep)[0]; - const pkg = require(path.join( - process.cwd(), - 'node_modules', - name, - 'package.json', - )); + const pkg = require(path.join(root, 'node_modules', name, 'package.json')); const requiredCommands: | ProjectCommandT | Array = require(path.join( - process.cwd(), + root, 'node_modules', pathToCommands, )); @@ -105,7 +101,7 @@ const loadProjectCommands = ( /** * Loads all the commands inside a given `root` folder */ -export function getCommands(commands: Array): Array { +export function getCommands(ctx: ContextT): Array { return [ ...loadLocalCommands, { @@ -119,6 +115,6 @@ export function getCommands(commands: Array): Array { ); }, }, - ...loadProjectCommands(commands), + ...loadProjectCommands(ctx), ]; } diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 045197844..fbb56639b 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -65,6 +65,7 @@ function loadConfig() { }; }, { + root: process.cwd(), dependencies: {}, commands: [], platforms: { From e50001aa0b1161d75061a81e2db0b021a59c5525 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 17:41:43 +0100 Subject: [PATCH 17/47] Wip 3: Legacy link config --- packages/cli/src/cliEntry.js | 4 ++-- packages/cli/src/tools/config/index.js | 24 ++++++++++++++----- .../src/tools/config/readConfigFromDisk.js | 23 ++++++++++++++---- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index f55250e09..c2b9e1fa8 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -35,7 +35,7 @@ commander.on('command:*', () => { const defaultOptParser = val => val; const handleError = err => { - logger.error(err.message); + logger.error(err.stack); process.exit(1); }; @@ -150,7 +150,7 @@ async function setupAndRun() { setProjectDir(ctx.root); - const commands = getCommands(ctx.commands); + const commands = getCommands(ctx); commands.forEach(command => addCommand(command, ctx)); diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index fbb56639b..6e10e2410 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -13,12 +13,18 @@ import findDependencies from './findDependencies'; import { readProjectConfigFromDisk, readDependencyConfigFromDisk, + readLegacyDependencyConfigFromDisk, } from './readConfigFromDisk'; function loadConfig() { const defaultConfig = findDependencies().reduce( (acc, dependencyName) => { - const config = readDependencyConfigFromDisk(dependencyName); + const root = path.resolve(process.cwd(), 'node_modules', dependencyName); + + const config = + readDependencyConfigFromDisk(dependencyName) || + readLegacyDependencyConfigFromDisk(dependencyName); + console.log(config); return { dependencies: { ...acc.dependencies, @@ -29,7 +35,7 @@ function loadConfig() { return dependency; } const detectedConfig = acc.platforms[platform].dependencyConfig( - config.root, + root, platformConfig, ); if (detectedConfig === null) { @@ -50,8 +56,12 @@ function loadConfig() { ), platforms: { ...acc.platforms, - ...mapValues(config.platforms, pathToPlatform => - require(path.join(dependencyName, pathToPlatform)), + ...mapValues( + config.platforms, + pathOrObject => + typeof pathOrObject === 'string' + ? require(path.join(dependencyName, pathOrObject)) + : pathOrObject, ), }, haste: { @@ -65,7 +75,6 @@ function loadConfig() { }; }, { - root: process.cwd(), dependencies: {}, commands: [], platforms: { @@ -79,7 +88,10 @@ function loadConfig() { }, ); - return merge(defaultConfig, readProjectConfigFromDisk()); + return merge( + {...defaultConfig, root: process.cwd()}, + readProjectConfigFromDisk(), + ); } export default loadConfig; diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index af8be3890..25d191a2e 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -3,7 +3,7 @@ * * Loads and validates a project configuration */ - +import {get} from 'lodash'; import comsmiconfig from 'cosmiconfig'; import path from 'path'; import {validate} from 'jest-validate'; @@ -11,6 +11,7 @@ import {validate} from 'jest-validate'; import type {ProjectConfig, DependencyConfig} from './types.flow'; import resolveReactNativePath from './resolveReactNativePath'; +import getPackageConfiguration from '../getPackageConfiguration'; const exampleProjectConfig: ProjectConfig = { reactNativePath: '.', @@ -59,7 +60,11 @@ export function readDependencyConfigFromDisk( searchPlaces, }); - const {config} = explorer.searchSync(root) || {config: {}}; + const {config} = explorer.searchSync(root) || {config: undefined}; + + if (!config) { + return null; + } validate(config, { exampleConfig, @@ -70,8 +75,18 @@ export function readDependencyConfigFromDisk( }, }); + return config; +} + +export function readLegacyDependencyConfigFromDisk(dependencyName: string) { + const root = path.resolve(process.cwd(), 'node_modules', dependencyName); + const config = getPackageConfiguration(root); + return { - ...config, - root, + commands: [].concat(config.plugin), + platforms: config.platform + ? require(path.join(root, config.platform)) + : undefined, + haste: config.haste, }; } From 2b3e01658aeee976fc40392ab4eebea948f0b96a Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 19:37:52 +0100 Subject: [PATCH 18/47] Updates --- packages/cli/src/tools/config/index.js | 27 +++++++++---------- .../src/tools/config/readConfigFromDisk.js | 15 ++++++++++- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 6e10e2410..633652e51 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -23,41 +23,40 @@ function loadConfig() { const config = readDependencyConfigFromDisk(dependencyName) || - readLegacyDependencyConfigFromDisk(dependencyName); - console.log(config); + readLegacyDependencyConfigFromDisk(dependencyName) || + {}; + return { dependencies: { ...acc.dependencies, get [dependencyName]() { return Object.keys(acc.platforms).reduce((dependency, platform) => { - const platformConfig = get(config, `dependency.${platform}`, {}); - if (platformConfig === null) { - return dependency; - } + const customConfig = get(config, `dependency.${platform}`, {}); const detectedConfig = acc.platforms[platform].dependencyConfig( root, - platformConfig, + customConfig, ); if (detectedConfig === null) { - return dependency; + dependency[platform] = null; + } else { + dependency[platform] = { + ...detectedConfig, + ...customConfig, + }; } - dependency[platform] = { - ...detectedConfig, - ...platformConfig, - }; return dependency; }, {}); }, }, commands: acc.commands.concat( - (config.commands || []).map(pathToCommand => + get(config, 'commands', []).map(pathToCommand => path.join(dependencyName, pathToCommand), ), ), platforms: { ...acc.platforms, ...mapValues( - config.platforms, + get(config, 'platforms', {}), pathOrObject => typeof pathOrObject === 'string' ? require(path.join(dependencyName, pathOrObject)) diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 25d191a2e..39d7fd9ad 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -28,6 +28,15 @@ const exampleDependencyConfig = { commands: ['./local-cli/runWindows/runWindows.js'], }; +const exampleDeprecatedConfig = { + plugin: './path/to/a/plugin.js', + platform: './path/to/a/platform.js', + haste: { + platforms: ['windows'], + providesModuleNodeModules: ['react-native-windows'], + }, +}; + const exampleConfig = { ...exampleProjectConfig, ...exampleDependencyConfig, @@ -63,7 +72,7 @@ export function readDependencyConfigFromDisk( const {config} = explorer.searchSync(root) || {config: undefined}; if (!config) { - return null; + return undefined; } validate(config, { @@ -82,6 +91,10 @@ export function readLegacyDependencyConfigFromDisk(dependencyName: string) { const root = path.resolve(process.cwd(), 'node_modules', dependencyName); const config = getPackageConfiguration(root); + if (Object.keys(config).length === 0) { + return undefined; + } + return { commands: [].concat(config.plugin), platforms: config.platform From bfc9ed7c628c474556b41708e8b4b7b661b7dc82 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 19:40:01 +0100 Subject: [PATCH 19/47] Update name: --- packages/cli/src/tools/config/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 633652e51..2e3f2b406 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -31,17 +31,17 @@ function loadConfig() { ...acc.dependencies, get [dependencyName]() { return Object.keys(acc.platforms).reduce((dependency, platform) => { - const customConfig = get(config, `dependency.${platform}`, {}); + const platformConfig = get(config, `dependency.${platform}`, {}); const detectedConfig = acc.platforms[platform].dependencyConfig( root, - customConfig, + platformConfig, ); if (detectedConfig === null) { dependency[platform] = null; } else { dependency[platform] = { ...detectedConfig, - ...customConfig, + ...platformConfig, }; } return dependency; From 2180a5a35700e6bb5eeae5d87139e8f6ebd225c3 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 19:56:46 +0100 Subject: [PATCH 20/47] Add deprecation messages for soon-to-be-deprecated rnpm configuration --- packages/cli/package.json | 1 + .../src/tools/config/readConfigFromDisk.js | 53 ++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 6be959b38..db7d13f7b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,6 +24,7 @@ "compression": "^1.7.1", "connect": "^3.6.5", "cosmiconfig": "^5.1.0", + "dedent": "^0.7.0", "deepmerge": "^3.2.0", "denodeify": "^1.2.1", "envinfo": "^5.7.0", diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 39d7fd9ad..a9dea8949 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -3,10 +3,10 @@ * * Loads and validates a project configuration */ -import {get} from 'lodash'; import comsmiconfig from 'cosmiconfig'; import path from 'path'; import {validate} from 'jest-validate'; +import dedent from 'dedent'; import type {ProjectConfig, DependencyConfig} from './types.flow'; @@ -95,6 +95,57 @@ export function readLegacyDependencyConfigFromDisk(dependencyName: string) { return undefined; } + validate(config, { + exampleConfig: exampleDeprecatedConfig, + deprecatedConfig: { + plugin: ({plugin}) => dedent` + Setting \`rnpm.plugin\` in \`package.json\` in order to extend + React Native CLI has been deprecated and will stop working in the next + React Native release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "commands": ${JSON.stringify([].concat(plugin))} + } + }`, + platform: ({platform}) => dedent` + Setting \`rnpm.platform\` in \`package.json\` in order to define + additional platforms has been deprecated and will stop working in the next + React Native release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "platforms": { + "": "${platform}" + } + } + }`, + haste: ({haste}) => dedent` + Setting \`rnpm.haste\` in \`package.json\` in order to define + additional settings for Metro has been deprecated and will stop + working in next release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "haste": { + "platforms": ${JSON.stringify(haste.platforms)}, + "providesModuleNodeModules": ${JSON.stringify( + haste.providesModuleNodeModules, + )} + } + } + }`, + }, + title: { + warning: `Warnings from ${dependencyName}`, + error: `Errors from ${dependencyName}`, + deprecation: `Deprecations from ${dependencyName}`, + }, + }); + return { commands: [].concat(config.plugin), platforms: config.platform From 7abcaebb4aa03cef47a1022a9796e818bcda9ee4 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 19:58:37 +0100 Subject: [PATCH 21/47] Both array and string were acceptable in rnpm configuration --- packages/cli/src/tools/config/readConfigFromDisk.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index a9dea8949..86c378c70 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -5,7 +5,7 @@ */ import comsmiconfig from 'cosmiconfig'; import path from 'path'; -import {validate} from 'jest-validate'; +import {validate, multipleValidOptions} from 'jest-validate'; import dedent from 'dedent'; import type {ProjectConfig, DependencyConfig} from './types.flow'; @@ -29,7 +29,10 @@ const exampleDependencyConfig = { }; const exampleDeprecatedConfig = { - plugin: './path/to/a/plugin.js', + plugin: multipleValidOptions('./path/to/a/plugin.js', [ + './path/to/foo/plugin.js', + './path/to/bar/plugin.js', + ]), platform: './path/to/a/platform.js', haste: { platforms: ['windows'], From 225d7398e77a2199956684f8dcae292c3b6d480c Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 20:00:29 +0100 Subject: [PATCH 22/47] Share root --- packages/cli/src/tools/config/index.js | 6 +++--- .../cli/src/tools/config/readConfigFromDisk.js | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 2e3f2b406..fffad1782 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -19,11 +19,11 @@ import { function loadConfig() { const defaultConfig = findDependencies().reduce( (acc, dependencyName) => { - const root = path.resolve(process.cwd(), 'node_modules', dependencyName); + const root = path.join(process.cwd(), 'node_modules', dependencyName); const config = - readDependencyConfigFromDisk(dependencyName) || - readLegacyDependencyConfigFromDisk(dependencyName) || + readDependencyConfigFromDisk(root, dependencyName) || + readLegacyDependencyConfigFromDisk(root, dependencyName) || {}; return { diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 86c378c70..b9eda14bf 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -63,16 +63,15 @@ export function readProjectConfigFromDisk(): ProjectConfig { } export function readDependencyConfigFromDisk( + rootFolder: string, dependencyName: string, ): DependencyConfig { - const root = path.resolve(process.cwd(), 'node_modules', dependencyName); - const explorer = comsmiconfig('react-native', { - stopDir: root, + stopDir: rootFolder, searchPlaces, }); - const {config} = explorer.searchSync(root) || {config: undefined}; + const {config} = explorer.searchSync(rootFolder) || {config: undefined}; if (!config) { return undefined; @@ -90,9 +89,11 @@ export function readDependencyConfigFromDisk( return config; } -export function readLegacyDependencyConfigFromDisk(dependencyName: string) { - const root = path.resolve(process.cwd(), 'node_modules', dependencyName); - const config = getPackageConfiguration(root); +export function readLegacyDependencyConfigFromDisk( + rootFolder: string, + dependencyName: string, +) { + const config = getPackageConfiguration(rootFolder); if (Object.keys(config).length === 0) { return undefined; @@ -152,7 +153,7 @@ export function readLegacyDependencyConfigFromDisk(dependencyName: string) { return { commands: [].concat(config.plugin), platforms: config.platform - ? require(path.join(root, config.platform)) + ? require(path.join(rootFolder, config.platform)) : undefined, haste: config.haste, }; From 623b643a10e69b512ebbe86d1db1acc127bfbe13 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Tue, 26 Mar 2019 23:25:02 +0100 Subject: [PATCH 23/47] Documentation and refining the code --- .../cli/src/tools/config/findDependencies.js | 8 +- packages/cli/src/tools/config/index.js | 22 +-- .../src/tools/config/readConfigFromDisk.js | 142 ++++++++++----- .../tools/config/resolveReactNativePath.js | 24 ++- packages/cli/src/tools/config/samples.js | 71 ++++++++ packages/cli/src/tools/config/types.flow.js | 166 ++++++++++++------ packages/cli/src/tools/types.flow.js | 24 ++- 7 files changed, 332 insertions(+), 125 deletions(-) create mode 100644 packages/cli/src/tools/config/samples.js diff --git a/packages/cli/src/tools/config/findDependencies.js b/packages/cli/src/tools/config/findDependencies.js index 0b5c50db0..573edc193 100644 --- a/packages/cli/src/tools/config/findDependencies.js +++ b/packages/cli/src/tools/config/findDependencies.js @@ -13,7 +13,11 @@ const pluginRe = new RegExp( ].join('|'), ); -function findDependencies() { +/** + * Returns an array of dependencies from project's package.json that + * are likely to be React Native packages (see regular expression above) + */ +export default function findDependencies(): Array { let pjson; try { @@ -29,5 +33,3 @@ function findDependencies() { return deps.filter(dependency => pluginRe.test(dependency)); } - -export default findDependencies; diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index fffad1782..a9549d76a 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -26,6 +26,14 @@ function loadConfig() { readLegacyDependencyConfigFromDisk(root, dependencyName) || {}; + const platforms = mapValues( + get(config, 'platforms', {}), + pathOrObject => + typeof pathOrObject === 'string' + ? require(path.join(dependencyName, pathOrObject)) + : pathOrObject, + ); + return { dependencies: { ...acc.dependencies, @@ -55,21 +63,13 @@ function loadConfig() { ), platforms: { ...acc.platforms, - ...mapValues( - get(config, 'platforms', {}), - pathOrObject => - typeof pathOrObject === 'string' - ? require(path.join(dependencyName, pathOrObject)) - : pathOrObject, - ), + ...platforms, }, haste: { providesModuleNodeModules: acc.haste.providesModuleNodeModules.concat( - get(config, 'haste.providesModuleNodeModules', []), - ), - platforms: acc.haste.platforms.concat( - get(config, 'haste.platforms', []), + Object.keys(platforms).length > 0 ? dependencyName : [], ), + platforms: [...acc.haste.platforms, ...Object.keys(platforms)], }, }; }, diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index b9eda14bf..920fc39b1 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -5,49 +5,29 @@ */ import comsmiconfig from 'cosmiconfig'; import path from 'path'; -import {validate, multipleValidOptions} from 'jest-validate'; +import {validate} from 'jest-validate'; import dedent from 'dedent'; -import type {ProjectConfig, DependencyConfig} from './types.flow'; +import type {DependencyUserConfigT, ProjectUserConfigT} from './types.flow'; import resolveReactNativePath from './resolveReactNativePath'; import getPackageConfiguration from '../getPackageConfiguration'; -const exampleProjectConfig: ProjectConfig = { - reactNativePath: '.', -}; - -const exampleDependencyConfig = { - dependency: { - android: '', - ios: '', - }, - platforms: { - windows: './platform.js', - }, - commands: ['./local-cli/runWindows/runWindows.js'], -}; - -const exampleDeprecatedConfig = { - plugin: multipleValidOptions('./path/to/a/plugin.js', [ - './path/to/foo/plugin.js', - './path/to/bar/plugin.js', - ]), - platform: './path/to/a/platform.js', - haste: { - platforms: ['windows'], - providesModuleNodeModules: ['react-native-windows'], - }, -}; - -const exampleConfig = { - ...exampleProjectConfig, - ...exampleDependencyConfig, -}; +import { + config as exampleConfig, + legacyConfig as exampleLegacyConfig, +} from './samples'; +/** + * Places to look for the new configuration + */ const searchPlaces = ['react-native.config.js', 'package.json']; -export function readProjectConfigFromDisk(): ProjectConfig { +/** + * Reads a project configuration as defined by the user in the current + * workspace. + */ +export function readProjectConfigFromDisk(): ProjectUserConfigT { const explorer = comsmiconfig('react-native', {searchPlaces}); const {config} = explorer.searchSync() || {config: {}}; @@ -62,10 +42,17 @@ export function readProjectConfigFromDisk(): ProjectConfig { }; } +/** + * Reads a dependency configuration as defined by the developer + * inside `node_modules`. + * + * Returns `undefined` when no custom configuration is found + * in the dependency root. + */ export function readDependencyConfigFromDisk( rootFolder: string, dependencyName: string, -): DependencyConfig { +): ?DependencyUserConfigT { const explorer = comsmiconfig('react-native', { stopDir: rootFolder, searchPlaces, @@ -89,18 +76,27 @@ export function readDependencyConfigFromDisk( return config; } +/** + * Reads a legacy configuaration from a `package.json` "rnpm" key. + * + * Prints deprecation warnings for each of the keys along the upgrade instructions. + * + * Returns `undefined` when no configuration is provided. + */ export function readLegacyDependencyConfigFromDisk( rootFolder: string, dependencyName: string, -) { +): ?DependencyUserConfigT { const config = getPackageConfiguration(rootFolder); + // For historical reasons, `getPackageConfiguration` always returns an + // object, including empty when no cofinguration found. if (Object.keys(config).length === 0) { return undefined; } validate(config, { - exampleConfig: exampleDeprecatedConfig, + exampleConfig: exampleLegacyConfig, deprecatedConfig: { plugin: ({plugin}) => dedent` Setting \`rnpm.plugin\` in \`package.json\` in order to extend @@ -131,14 +127,64 @@ export function readLegacyDependencyConfigFromDisk( additional settings for Metro has been deprecated and will stop working in next release. + We now automatically configure Metro to support your platform.`, + ios: ({ios}) => dedent` + Setting \`rnpm.ios\` in \`package.json\` has been deprecated and will stop + working in next release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "dependency": { + "ios": ${JSON.stringify(ios)} + } + } + }`, + android: ({android}) => dedent` + Setting \`rnpm.android\` in \`package.json\` has been deprecated and will stop + working in next release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "dependency": { + "android": ${JSON.stringify(android)} + } + } + }`, + assets: ({assets}) => dedent` + Setting \`rnpm.assets\` in \`package.json\` has been deprecated and will stop + working in next release. + Consider setting the following in the \`package.json\` instead: { "react-native": { - "haste": { - "platforms": ${JSON.stringify(haste.platforms)}, - "providesModuleNodeModules": ${JSON.stringify( - haste.providesModuleNodeModules, - )} + "dependency": { + "assets": ${JSON.stringify(assets)} + } + } + }`, + commands: ({commands}) => dedent` + Setting \`rnpm.commands\` in \`package.json\` has been deprecated and will stop + working in next release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "dependency": { + "hooks": ${JSON.stringify(commands)} + } + } + }`, + params: ({params}) => dedent` + Setting \`rnpm.params\` in \`package.json\` has been deprecated and will stop + working in next release. + + Consider setting the following in the \`package.json\` instead: + { + "react-native": { + "dependency": { + "params": ${JSON.stringify(params)} } } }`, @@ -151,10 +197,18 @@ export function readLegacyDependencyConfigFromDisk( }); return { - commands: [].concat(config.plugin), + dependency: { + platforms: { + ios: config.ios, + android: config.android, + }, + assets: config.assets, + hooks: config.commands, + params: config.params, + }, + commands: [].concat(config.plugin || []), platforms: config.platform ? require(path.join(rootFolder, config.platform)) : undefined, - haste: config.haste, }; } diff --git a/packages/cli/src/tools/config/resolveReactNativePath.js b/packages/cli/src/tools/config/resolveReactNativePath.js index 1ee1564e4..9ba73faff 100644 --- a/packages/cli/src/tools/config/resolveReactNativePath.js +++ b/packages/cli/src/tools/config/resolveReactNativePath.js @@ -2,8 +2,13 @@ * @flow */ import path from 'path'; +import dedent from 'dedent'; -function resolveReactNativePath() { +/** + * Finds path to React Native inside `node_modules` + * or throws an error otherwise + */ +export default function resolveReactNativePath() { try { return path.dirname( // $FlowIssue: Wrong `require.resolve` type definition @@ -12,10 +17,17 @@ function resolveReactNativePath() { }), ); } catch (_ignored) { - throw new Error( - 'Unable to find React Native files. Make sure "react-native" module is installed in your project dependencies.', - ); + throw new Error(dedent` + Unable to find React Native files. Make sure "react-native" module is installed + in your project dependencies. + + If you are using React Native from a non-standard location, consider setting: + { + "react-native": { + "reactNativePath": "./path/to/react-native" + } + } + in your \`package.json\`. + `); } } - -export default resolveReactNativePath; diff --git a/packages/cli/src/tools/config/samples.js b/packages/cli/src/tools/config/samples.js new file mode 100644 index 000000000..d6f703aaa --- /dev/null +++ b/packages/cli/src/tools/config/samples.js @@ -0,0 +1,71 @@ +/** + * @flow + * + * Sample configuration objects that are used for `jest-validate`. + * + * See `./types.flow` for explanation of what + * the properties are responsible for. + */ +import {multipleValidOptions} from 'jest-validate'; + +import type {UserConfigT, LegacyDependencyUserConfigT} from './types.flow'; + +const projectConfig = { + reactNativePath: '.', +}; + +const dependencyConfig = { + dependency: { + platforms: { + android: { + sourceDir: './android/app', + manifestPath: './path/to/AndroidManifest.xml', + packageImportPath: 'import com.my.package.MyReactPackage', + packageInstance: 'new MyReactPackage()', + }, + ios: { + project: 'MyProject.xcodeproj', + sharedLibraries: ['libz'], + libraryFolder: 'Libraries', + }, + }, + assets: ['./path/to/asset.png'], + hooks: { + prelink: './path/to/a/command.sh', + postlink: './path/to/a/command.sh', + }, + params: [ + { + type: 'input', + name: 'myKey', + message: 'What is your deployment key value?', + }, + ], + }, + platforms: { + windows: './platform.js', + }, + commands: ['./local-cli/runWindows/runWindows.js'], +}; + +export const config: UserConfigT = { + ...projectConfig, + ...dependencyConfig, +}; + +export const legacyConfig: LegacyDependencyUserConfigT = { + plugin: multipleValidOptions('./path/to/a/plugin.js', [ + './path/to/foo/plugin.js', + './path/to/bar/plugin.js', + ]), + platform: './path/to/a/platform.js', + haste: { + platforms: ['windows'], + providesModuleNodeModules: ['react-native-windows'], + }, + assets: dependencyConfig.dependency.assets, + commands: dependencyConfig.dependency.hooks, + android: dependencyConfig.dependency.platforms.android, + ios: dependencyConfig.dependency.platforms.ios, + params: dependencyConfig.dependency.params, +}; diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 25aa29e32..5d00072e6 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -2,63 +2,119 @@ * @flow */ -export type ProjectConfig = { - reactNativePath: string, +import type { + AndroidConfigParamsT, + IOSConfigParamsT, + InquirerPromptT, +} from '../types.flow'; + +/** + * Depending on the context, the configuration of the CLI can have both configurations + * within. + */ +export type UserConfigT = ProjectUserConfigT & DependencyUserConfigT; + +/** + * Configuration that ships with a project + */ +export type ProjectUserConfigT = { + /** + * A path to a React Native module. Useful, when running from source and there's + * no "react-native" module that `require` can resolve. + */ + reactNativePath?: string, }; -export type DependencyConfig = { - dependency: { - android?: string, - ios?: string, +/** + * Configuration that ships with a dependency + */ +export type DependencyUserConfigT = { + /** + * Key that defines settings related to native code and linking + */ + dependency?: { + /** + * Additional configuration for native code on per platform basis. + * Useful to overwrite built-in heurstics in non-default setups + */ + platforms?: { + android?: ?AndroidConfigParamsT, + ios?: ?IOSConfigParamsT, + [key: string]: any, + }, + /** + * An array of assets defined by a library to link to a project + */ + assets?: Array, + /** + * A map of hooks to run pre/post some of the CLI actions + */ + hooks?: { + [key: string]: string, + prelink?: string, + postlink?: string, + }, + /** + * Params to ask during linking process if library requires additional + * configuration + */ + params?: InquirerPromptT[], + }, + /** + * Defines an array of commands that a dependency can add to a React Native CLI + */ + commands?: Array, + /** + * A map with additional platforms that ship with a dependency. + * + * String format is deprecated and will be removed in the future. + */ + platforms?: { + [key: string]: string, }, - commands?: [], - platforms?: {}, }; -// export type DependencyConfig = { -// ios: ?DependencyConfigIOS, -// android: ?DependencyConfigAndroid, -// }; - -// export type InputDependencyConfigIOS = { -// podspec?: string, -// }; - -// export type DependencyConfigIOS = { -// podspec: string, -// }; - -// export type InputDependencyConfigAndroid = { -// packageImportPath?: string, -// packageInstance?: string, -// }; - -// export type DependencyConfigAndroid = { -// packageImportPath: string, -// packageInstance: string, -// }; - -// export type PlatformConfig = { -// getDependencyConfig: (string, T) => ?K, -// }; - -// export type Platforms = { -// [key: string]: PlatformConfig<*>, -// ios: PlatformConfig, -// android: PlatformConfig< -// InputDependencyConfigAndroid, -// DependencyConfigAndroid, -// >, -// }; - -// export type ProjectConfig = { -// root: string, -// reactNativePath: string, -// dependencies: { -// [key: string]: DependencyConfig, -// }, -// }; - -// export type Options = { -// root: ?string, -// }; +/** + * Legacy configuration that can be defined in package.json, + * under "rnpm" key. + */ +export type LegacyDependencyUserConfigT = { + /** + * See DependencyUserConfigT.dependency.assets + */ + assets?: Array, + /** + * See DependencyUserConfigT.dependency.hooks + */ + commands?: {[name: string]: string}, + /** + * See DependencyUserConfigT.dependency.params + */ + params?: InquirerPromptT[], + /** + * See DependencyUserConfigT.dependency.platforms.android + */ + android?: ?AndroidConfigParamsT, + /** + * See DependencyUserConfigT.dependency.platforms.ios + */ + ios?: ?IOSConfigParamsT, + /** + * See DependencyUserConfigT.commands + */ + plugin?: string | Array, + /** + * See DependencyUserConfigT.platforms + */ + platform?: string, + /** + * Metro-related configuration to define to make 3rd party platforms + * work. + * + * We don't read this configuration, but infer it from other properties. + */ + haste?: { + platforms: Array, + providesModuleNodeModules: Array, + }, +}; diff --git a/packages/cli/src/tools/types.flow.js b/packages/cli/src/tools/types.flow.js index 495c26190..8c3df1d75 100644 --- a/packages/cli/src/tools/types.flow.js +++ b/packages/cli/src/tools/types.flow.js @@ -69,13 +69,18 @@ export type PlatformConfigT = { }, }; -/** - * The following types will be useful when we type `link` itself. For now, - * they can be treated as aliases. - */ -export type AndroidConfigParamsT = {}; +export type AndroidConfigParamsT = { + sourceDir?: string, + manifestPath?: string, + packageImportPath?: string, + packageInstance?: string, +}; -export type IOSConfigParamsT = {}; +export type IOSConfigParamsT = { + project?: string, + sharedLibraries?: string[], + libraryFolder?: string, +}; export type ProjectConfigIOST = {}; @@ -142,4 +147,11 @@ export type PackageConfigurationT = { params?: InquirerPromptT[], android: AndroidConfigParamsT, ios: IOSConfigParamsT, + + plugin?: string | Array, + platform?: string, + haste?: { + platforms?: Array, + providesModuleNodeModules?: Array, + }, }; From 3c7f36bf99d3d7d1570a436ed14e18734c3215b3 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 00:33:51 +0100 Subject: [PATCH 24/47] Start wrapping it up --- packages/cli/src/tools/config/index.js | 137 +++++++++++++---- .../src/tools/config/readConfigFromDisk.js | 5 +- .../tools/config/resolveReactNativePath.js | 4 +- packages/cli/src/tools/config/samples.js | 2 +- packages/cli/src/tools/config/types.flow.js | 143 +++++++++++++----- packages/cli/src/tools/loadMetroConfig.js | 6 +- 6 files changed, 225 insertions(+), 72 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index a9549d76a..cf0663a5b 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -1,32 +1,53 @@ /** - * + * @flow */ -import {get, mapValues} from 'lodash'; +import {get, pickBy, mapValues} from 'lodash'; import path from 'path'; import merge from 'deepmerge'; -import * as ios from '../ios'; -import * as android from '../android'; - import findDependencies from './findDependencies'; - import { readProjectConfigFromDisk, readDependencyConfigFromDisk, readLegacyDependencyConfigFromDisk, } from './readConfigFromDisk'; -function loadConfig() { +import { + type DependencyConfigT, + type ConfigT, + type PlatformsT, + type DependenciesConfigT, +} from './types.flow'; + +/** + * Built-in platforms + */ +import * as ios from '../ios'; +import * as android from '../android'; + +/** + * Loads CLI configuration + */ +function loadConfig(): ConfigT { const defaultConfig = findDependencies().reduce( - (acc, dependencyName) => { + (acc: DependenciesConfigT, dependencyName) => { const root = path.join(process.cwd(), 'node_modules', dependencyName); + /** + * Read user-defined configuration for a dependency from new and old location. + * We provide empty object at the end on purpose to access it easily with `get` + */ const config = readDependencyConfigFromDisk(root, dependencyName) || readLegacyDependencyConfigFromDisk(root, dependencyName) || {}; - const platforms = mapValues( + /** + * Because of legacy reasons (`rnpm` configuration), platforms can be an object. + * This code handles this special case. In future releases, we will allow paths + * to files only. + */ + const availablePlatforms: PlatformsT = mapValues( get(config, 'platforms', {}), pathOrObject => typeof pathOrObject === 'string' @@ -34,26 +55,63 @@ function loadConfig() { : pathOrObject, ); + /** + * Lazily gets dependency config for a current dependency. + * + * Note: this code is outside of the dynamic getter + * on purpose to make Flow work. + */ + const getDependencyConfig = (): DependencyConfigT => { + /** + * At the time of executing this function, `acc.platforms` will already + * have all the platforms that are defined by other dependencies too. + */ + const dependencyPlatforms = Object.keys(acc.platforms).reduce( + (dependency, platform) => { + /** + * We generate a configuration for a given platform by passing + * some non-standard developer-defined settings too + */ + const platformConfig = get( + config, + `dependency.platforms.${platform}`, + {}, + ); + // $FlowIssue: Invalid platforms are already filtered-out below + const detectedConfig = acc.platforms[platform].dependencyConfig( + root, + platformConfig, + ); + if (detectedConfig === null) { + dependency[platform] = null; + } else { + dependency[platform] = { + ...detectedConfig, + ...platformConfig, + }; + } + return dependency; + }, + { + ios: null, + android: null, + }, + ); + return { + root, + platforms: dependencyPlatforms, + assets: get(config, 'assets', []), + hooks: get(config, 'hooks', {}), + params: get(config, 'params', []), + }; + }; + return { dependencies: { ...acc.dependencies, + // $FlowIssue: Computed getters are not yet supported. get [dependencyName]() { - return Object.keys(acc.platforms).reduce((dependency, platform) => { - const platformConfig = get(config, `dependency.${platform}`, {}); - const detectedConfig = acc.platforms[platform].dependencyConfig( - root, - platformConfig, - ); - if (detectedConfig === null) { - dependency[platform] = null; - } else { - dependency[platform] = { - ...detectedConfig, - ...platformConfig, - }; - } - return dependency; - }, {}); + return getDependencyConfig(); }, }, commands: acc.commands.concat( @@ -61,19 +119,35 @@ function loadConfig() { path.join(dependencyName, pathToCommand), ), ), + /** + * Note: In this context, a `platform` is a valid target that we can + * link dependencies for. + * + * This is impossible when either `projectConfig` and `dependencyConfig` are + * not provided hence the `pickBy` check. + */ platforms: { ...acc.platforms, - ...platforms, + ...pickBy( + availablePlatforms, + (platform: PlatformsT) => + typeof platform.dependencyConfig === 'function' && + typeof platform.projectConfig === 'function' && + typeof platform.linkConfig === 'function', + ), }, haste: { providesModuleNodeModules: acc.haste.providesModuleNodeModules.concat( - Object.keys(platforms).length > 0 ? dependencyName : [], + Object.keys(availablePlatforms).length > 0 ? dependencyName : [], ), - platforms: [...acc.haste.platforms, ...Object.keys(platforms)], + platforms: [ + ...acc.haste.platforms, + ...Object.keys(availablePlatforms), + ], }, }; }, - { + ({ dependencies: {}, commands: [], platforms: { @@ -84,9 +158,12 @@ function loadConfig() { providesModuleNodeModules: [], platforms: [], }, - }, + }: DependenciesConfigT), ); + /** + * Default configuration can be overriden by a project + */ return merge( {...defaultConfig, root: process.cwd()}, readProjectConfigFromDisk(), diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 920fc39b1..bec948c1b 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -8,7 +8,10 @@ import path from 'path'; import {validate} from 'jest-validate'; import dedent from 'dedent'; -import type {DependencyUserConfigT, ProjectUserConfigT} from './types.flow'; +import { + type DependencyUserConfigT, + type ProjectUserConfigT, +} from './types.flow'; import resolveReactNativePath from './resolveReactNativePath'; import getPackageConfiguration from '../getPackageConfiguration'; diff --git a/packages/cli/src/tools/config/resolveReactNativePath.js b/packages/cli/src/tools/config/resolveReactNativePath.js index 9ba73faff..dabc30177 100644 --- a/packages/cli/src/tools/config/resolveReactNativePath.js +++ b/packages/cli/src/tools/config/resolveReactNativePath.js @@ -5,8 +5,8 @@ import path from 'path'; import dedent from 'dedent'; /** - * Finds path to React Native inside `node_modules` - * or throws an error otherwise + * Finds path to React Native inside `node_modules` or throws + * an error otherwise. */ export default function resolveReactNativePath() { try { diff --git a/packages/cli/src/tools/config/samples.js b/packages/cli/src/tools/config/samples.js index d6f703aaa..6520b8c9d 100644 --- a/packages/cli/src/tools/config/samples.js +++ b/packages/cli/src/tools/config/samples.js @@ -8,7 +8,7 @@ */ import {multipleValidOptions} from 'jest-validate'; -import type {UserConfigT, LegacyDependencyUserConfigT} from './types.flow'; +import {type UserConfigT, type LegacyDependencyUserConfigT} from './types.flow'; const projectConfig = { reactNativePath: '.', diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 5d00072e6..ce6e03870 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -6,6 +6,8 @@ import type { AndroidConfigParamsT, IOSConfigParamsT, InquirerPromptT, + DependencyConfigAndroidT, + DependencyConfigIOST, } from '../types.flow'; /** @@ -25,6 +27,62 @@ export type ProjectUserConfigT = { reactNativePath?: string, }; +/** + * Project configuration after being processed by the loading mechanism + */ +export type ProjectConfigT = { + reactNativePath: string, +}; + +/** + * An array of assets defined by a library to link to a project + */ +type AssetsT = Array; + +/** + * A map of hooks to run pre/post some of the CLI actions + */ +type HooksT = { + [key: string]: string, + prelink?: string, + postlink?: string, +}; + +/** + * Params to ask during linking process if library requires additional + * configuration + */ +type ParamsT = InquirerPromptT[]; + +/** + * Defines an array of commands that a dependency can add to a React Native CLI + */ +type CommandsT = Array; + +/** + * A map with additional platforms that ship with a dependency. + * + * String format is deprecated and will be removed in the future. + */ +export type PlatformsT = { + [key: string]: + | string + | { + dependencyConfig?: Function, + projectConfig?: Function, + linkConfig?: Function, + }, +}; + +/** + * Metro-related configuration to define to make 3rd party platforms + * work. + */ +type MetroConfigT = { + platforms: Array, + providesModuleNodeModules: Array, +}; + /** * Configuration that ships with a dependency */ @@ -42,36 +100,34 @@ export type DependencyUserConfigT = { ios?: ?IOSConfigParamsT, [key: string]: any, }, - /** - * An array of assets defined by a library to link to a project - */ - assets?: Array, - /** - * A map of hooks to run pre/post some of the CLI actions - */ - hooks?: { - [key: string]: string, - prelink?: string, - postlink?: string, - }, - /** - * Params to ask during linking process if library requires additional - * configuration - */ - params?: InquirerPromptT[], + assets?: AssetsT, + hooks?: HooksT, + params?: ParamsT, }, + commands?: CommandsT, + platforms?: PlatformsT, +}; + +/** + * Dependency configuration after being processed by the CLI + */ +export type DependencyConfigT = { /** - * Defines an array of commands that a dependency can add to a React Native CLI + * Dependency location on the disk */ - commands?: Array, + root: string, /** - * A map with additional platforms that ship with a dependency. - * - * String format is deprecated and will be removed in the future. + * An object containing configuration for each of the dependencies, + * or null, if dependency does not support given platform */ - platforms?: { - [key: string]: string, + platforms: { + android: DependencyConfigAndroidT | null, + ios: DependencyConfigIOST | null, + [key: string]: any, }, + assets: AssetsT, + hooks: HooksT, + params: ParamsT, }; /** @@ -82,15 +138,15 @@ export type LegacyDependencyUserConfigT = { /** * See DependencyUserConfigT.dependency.assets */ - assets?: Array, + assets?: AssetsT, /** * See DependencyUserConfigT.dependency.hooks */ - commands?: {[name: string]: string}, + commands?: HooksT, /** * See DependencyUserConfigT.dependency.params */ - params?: InquirerPromptT[], + params?: ParamsT, /** * See DependencyUserConfigT.dependency.platforms.android */ @@ -102,19 +158,36 @@ export type LegacyDependencyUserConfigT = { /** * See DependencyUserConfigT.commands */ - plugin?: string | Array, + plugin?: string | CommandsT, /** * See DependencyUserConfigT.platforms */ - platform?: string, + platform?: PlatformsT, /** - * Metro-related configuration to define to make 3rd party platforms - * work. - * * We don't read this configuration, but infer it from other properties. */ - haste?: { - platforms: Array, - providesModuleNodeModules: Array, + haste?: MetroConfigT, +}; + +export type DependenciesConfigT = { + dependencies: { + [key: string]: DependencyConfigT, }, + /** + * An array of platforms collected by parsing dependencies + */ + platforms: PlatformsT, + /** + * An array of commands collected by parsing dependencies + */ + commands: CommandsT, + /** + * Extra Metro configuration to use to support additonal platforms + */ + haste: MetroConfigT, }; + +export type ConfigT = ProjectConfigT & + DependenciesConfigT & { + root: string, + }; diff --git a/packages/cli/src/tools/loadMetroConfig.js b/packages/cli/src/tools/loadMetroConfig.js index 6323c4073..c7ada9556 100644 --- a/packages/cli/src/tools/loadMetroConfig.js +++ b/packages/cli/src/tools/loadMetroConfig.js @@ -5,7 +5,7 @@ import path from 'path'; import {createBlacklist} from 'metro'; import {loadConfig} from 'metro-config'; -import type {ContextT} from './types.flow'; +import type {ConfigT} from './config/types.flow'; import findSymlinkedModules from './findSymlinkedModules'; const resolveSymlinksForRoots = roots => @@ -30,7 +30,7 @@ const getBlacklistRE = () => createBlacklist([/.*\/__fixtures__\/.*/]); * @todo(grabbou): As a separate PR, haste.platforms should be added before "native". * Otherwise, a.native.js will not load on Windows or other platforms */ -export const getDefaultConfig = (ctx: ContextT) => { +export const getDefaultConfig = (ctx: ConfigT) => { return { resolver: { resolverMainFields: ['react-native', 'browser', 'main'], @@ -83,7 +83,7 @@ export type ConfigOptionsT = {| * This allows the CLI to always overwrite the file settings. */ export default (async function load( - ctx: ContextT, + ctx: ConfigT, // $FlowFixMe - troubles with empty object being inexact options?: ConfigOptionsT = {}, ) { From b3cbf3be549fb196f36781aa16b3ea8d2ba6d6cb Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 00:39:35 +0100 Subject: [PATCH 25/47] Fix some simple flow errors --- packages/cli/src/commands/bundle/buildBundle.js | 4 ++-- packages/cli/src/commands/index.js | 6 ++++-- packages/cli/src/tools/config/types.flow.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/bundle/buildBundle.js b/packages/cli/src/commands/bundle/buildBundle.js index f4d9fb406..0122d9857 100644 --- a/packages/cli/src/commands/bundle/buildBundle.js +++ b/packages/cli/src/commands/bundle/buildBundle.js @@ -13,14 +13,14 @@ import outputBundle from 'metro/src/shared/output/bundle'; import path from 'path'; import chalk from 'chalk'; import type {CommandLineArgs} from './bundleCommandLineArgs'; -import type {ContextT} from '../../tools/types.flow'; +import type {ConfigT} from '../../tools/config/types.flow'; import saveAssets from './saveAssets'; import loadMetroConfig from '../../tools/loadMetroConfig'; import logger from '../../tools/logger'; async function buildBundle( args: CommandLineArgs, - ctx: ContextT, + ctx: ConfigT, output: typeof outputBundle = outputBundle, ) { const config = await loadMetroConfig(ctx, { diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index 3c2ecd0e1..a8acc2018 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -12,6 +12,8 @@ import type { LocalCommandT, } from '../tools/types.flow'; +import {type ConfigT} from '../tools/config/types.flow'; + import server from './server/server'; import runIOS from './runIOS/runIOS'; import runAndroid from './runAndroid/runAndroid'; @@ -61,7 +63,7 @@ const loadLocalCommands: Array = [ const loadProjectCommands = ({ root, commands, -}: ContextT): Array => { +}: ConfigT): Array => { return commands.reduce((acc: Array, pathToCommands: string) => { /** * `pathToCommand` is a path to a file where commands are defined, relative to `node_modules` @@ -101,7 +103,7 @@ const loadProjectCommands = ({ /** * Loads all the commands inside a given `root` folder */ -export function getCommands(ctx: ContextT): Array { +export function getCommands(ctx: ConfigT): Array { return [ ...loadLocalCommands, { diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index ce6e03870..2a16794b0 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -162,7 +162,7 @@ export type LegacyDependencyUserConfigT = { /** * See DependencyUserConfigT.platforms */ - platform?: PlatformsT, + platform?: string, /** * We don't read this configuration, but infer it from other properties. */ From db90f50efaa363231e76acd4959d2e222dff3083 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 00:54:59 +0100 Subject: [PATCH 26/47] Fix flow errors except for tests --- packages/cli/src/commands/index.js | 12 ++++++------ .../cli/src/commands/link/getDependencyConfig.js | 1 + packages/cli/src/commands/link/getProjectConfig.js | 1 + packages/cli/src/tools/types.flow.js | 7 +++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index a8acc2018..4cdc17b72 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -64,7 +64,7 @@ const loadProjectCommands = ({ root, commands, }: ConfigT): Array => { - return commands.reduce((acc: Array, pathToCommands: string) => { + return commands.reduce((acc: Array, cmdPath: string) => { /** * `pathToCommand` is a path to a file where commands are defined, relative to `node_modules` * folder. @@ -73,12 +73,12 @@ const loadProjectCommands = ({ * into consideration. */ const name = - pathToCommands[0] === '@' - ? pathToCommands + cmdPath[0] === '@' + ? cmdPath .split(path.sep) .slice(0, 2) .join(path.sep) - : pathToCommands.split(path.sep)[0]; + : cmdPath.split(path.sep)[0]; const pkg = require(path.join(root, 'node_modules', name, 'package.json')); @@ -87,7 +87,7 @@ const loadProjectCommands = ({ | Array = require(path.join( root, 'node_modules', - pathToCommands, + cmdPath, )); if (Array.isArray(requiredCommands)) { @@ -96,7 +96,7 @@ const loadProjectCommands = ({ ); } - return acc.concat({...requiredCommands}); + return acc.concat({...requiredCommands, pkg}); }, []); }; diff --git a/packages/cli/src/commands/link/getDependencyConfig.js b/packages/cli/src/commands/link/getDependencyConfig.js index 3cc2e1af2..7983691c2 100644 --- a/packages/cli/src/commands/link/getDependencyConfig.js +++ b/packages/cli/src/commands/link/getDependencyConfig.js @@ -28,6 +28,7 @@ export default function getDependencyConfig( Object.keys(availablePlatforms).forEach(platform => { platformConfigs[platform] = availablePlatforms[platform].dependencyConfig( folder, + // $FlowIssue: Flow can't match platform config with its appropriate config function config[platform] || {}, ); }); diff --git a/packages/cli/src/commands/link/getProjectConfig.js b/packages/cli/src/commands/link/getProjectConfig.js index 9172f012b..d3b40c169 100644 --- a/packages/cli/src/commands/link/getProjectConfig.js +++ b/packages/cli/src/commands/link/getProjectConfig.js @@ -24,6 +24,7 @@ export default function getProjectConfig( logger.debug(`Getting project config for ${getPlatformName(platform)}...`); platformConfigs[platform] = availablePlatforms[platform].projectConfig( ctx.root, + // $FlowIssue: Flow can't match platform config with its appropriate config function config[platform] || {}, ); }); diff --git a/packages/cli/src/tools/types.flow.js b/packages/cli/src/tools/types.flow.js index 8c3df1d75..bd7ee7973 100644 --- a/packages/cli/src/tools/types.flow.js +++ b/packages/cli/src/tools/types.flow.js @@ -2,10 +2,9 @@ * @flow */ -export type ContextT = { - root: string, - reactNativePath: string, -}; +import {type ConfigT} from './config/types.flow'; + +export type ContextT = ConfigT; export type LocalCommandT = { name: string, From 4b67b8b91180211bd97673768ce16c0d2f95dc01 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 01:31:39 +0100 Subject: [PATCH 27/47] Override configuration --- packages/cli/src/tools/config/samples.js | 42 +++++++++++++++++++-- packages/cli/src/tools/config/types.flow.js | 17 ++++++++- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/tools/config/samples.js b/packages/cli/src/tools/config/samples.js index 6520b8c9d..bc1dbfb10 100644 --- a/packages/cli/src/tools/config/samples.js +++ b/packages/cli/src/tools/config/samples.js @@ -10,10 +10,6 @@ import {multipleValidOptions} from 'jest-validate'; import {type UserConfigT, type LegacyDependencyUserConfigT} from './types.flow'; -const projectConfig = { - reactNativePath: '.', -}; - const dependencyConfig = { dependency: { platforms: { @@ -48,6 +44,44 @@ const dependencyConfig = { commands: ['./local-cli/runWindows/runWindows.js'], }; +const projectConfig = { + dependencies: { + 'react-native-webview': { + root: './path/to/library', + platforms: { + ios: { + sourceDir: './path/to/library/ios', + folder: './path/to/library', + pbxprojPath: + './path/to/library/ios/CodePush.xcodeproj/project.pbxproj', + podfile: './path/to/podfile', + podspec: 'CodePush', + projectPath: './path/to/library/ios/CodePush.xcodeproj', + projectName: 'CodePush.xcodeproj', + libraryFolder: 'Libraries', + sharedLibraries: ['libz'], + }, + android: { + sourceDir: './path/to/library/android/app', + folder: './path/to/library', + manifest: {}, + packageImportPath: 'import com.hello.MyPackage;', + packageInstance: 'new MyPackage()', + }, + }, + assets: dependencyConfig.dependency.assets, + hooks: dependencyConfig.dependency.hooks, + params: dependencyConfig.dependency.params, + }, + }, + commands: dependencyConfig.commands, + haste: { + providesModuleNodeModules: ['react-native-windows'], + platforms: ['windows'], + }, + reactNativePath: './path/to/react-native', +}; + export const config: UserConfigT = { ...projectConfig, ...dependencyConfig, diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 2a16794b0..014a98efd 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -14,7 +14,9 @@ import type { * Depending on the context, the configuration of the CLI can have both configurations * within. */ -export type UserConfigT = ProjectUserConfigT & DependencyUserConfigT; +export type UserConfigT = ProjectUserConfigT & + DependenciesUserConfigT & + DependencyUserConfigT; /** * Configuration that ships with a project @@ -169,6 +171,19 @@ export type LegacyDependencyUserConfigT = { haste?: MetroConfigT, }; +/** + * Users can override "DependenciesConfigT" that we have automatically generated + * during the CLI startup + */ +export type DependenciesUserConfigT = { + dependencies?: { + [key: string]: ?DependencyConfigT, + }, + platforms?: PlatformsT, + commands?: CommandsT, + haste?: MetroConfigT, +}; + export type DependenciesConfigT = { dependencies: { [key: string]: DependencyConfigT, From 34224eb3f791a6129294afde584c86de712a404e Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 07:49:30 +0100 Subject: [PATCH 28/47] Simplify typing changes --- packages/cli/src/commands/bundle/buildBundle.js | 4 ++-- packages/cli/src/tools/loadMetroConfig.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/bundle/buildBundle.js b/packages/cli/src/commands/bundle/buildBundle.js index 0122d9857..f4d9fb406 100644 --- a/packages/cli/src/commands/bundle/buildBundle.js +++ b/packages/cli/src/commands/bundle/buildBundle.js @@ -13,14 +13,14 @@ import outputBundle from 'metro/src/shared/output/bundle'; import path from 'path'; import chalk from 'chalk'; import type {CommandLineArgs} from './bundleCommandLineArgs'; -import type {ConfigT} from '../../tools/config/types.flow'; +import type {ContextT} from '../../tools/types.flow'; import saveAssets from './saveAssets'; import loadMetroConfig from '../../tools/loadMetroConfig'; import logger from '../../tools/logger'; async function buildBundle( args: CommandLineArgs, - ctx: ConfigT, + ctx: ContextT, output: typeof outputBundle = outputBundle, ) { const config = await loadMetroConfig(ctx, { diff --git a/packages/cli/src/tools/loadMetroConfig.js b/packages/cli/src/tools/loadMetroConfig.js index c7ada9556..ae2b7df40 100644 --- a/packages/cli/src/tools/loadMetroConfig.js +++ b/packages/cli/src/tools/loadMetroConfig.js @@ -5,7 +5,7 @@ import path from 'path'; import {createBlacklist} from 'metro'; import {loadConfig} from 'metro-config'; -import type {ConfigT} from './config/types.flow'; +import {type ContextT} from './types.flow'; import findSymlinkedModules from './findSymlinkedModules'; const resolveSymlinksForRoots = roots => @@ -30,7 +30,7 @@ const getBlacklistRE = () => createBlacklist([/.*\/__fixtures__\/.*/]); * @todo(grabbou): As a separate PR, haste.platforms should be added before "native". * Otherwise, a.native.js will not load on Windows or other platforms */ -export const getDefaultConfig = (ctx: ConfigT) => { +export const getDefaultConfig = (ctx: ContextT) => { return { resolver: { resolverMainFields: ['react-native', 'browser', 'main'], @@ -83,7 +83,7 @@ export type ConfigOptionsT = {| * This allows the CLI to always overwrite the file settings. */ export default (async function load( - ctx: ConfigT, + ctx: ContextT, // $FlowFixMe - troubles with empty object being inexact options?: ConfigOptionsT = {}, ) { From 717687be40d026e995d209f4835e833e0617c405 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 07:55:33 +0100 Subject: [PATCH 29/47] Fix remaining flow errors --- .../cli/src/commands/upgrade/__tests__/upgrade.test.js | 7 +++++++ packages/cli/src/tools/config/index.js | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js b/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js index 2ae9d7df9..aa1569a17 100644 --- a/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js +++ b/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js @@ -55,6 +55,13 @@ const olderVersion = '0.56.0'; const ctx = { root: '/project/root', reactNativePath: '', + commands: [], + platforms: {}, + dependencies: {}, + haste: { + providesModuleNodeModules: [], + platforms: [], + }, }; const opts = { legacy: false, diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index cf0663a5b..2c5cc35a5 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -29,9 +29,11 @@ import * as android from '../android'; * Loads CLI configuration */ function loadConfig(): ConfigT { + const projectRoot = process.cwd(); + const defaultConfig = findDependencies().reduce( (acc: DependenciesConfigT, dependencyName) => { - const root = path.join(process.cwd(), 'node_modules', dependencyName); + const root = path.join(projectRoot, 'node_modules', dependencyName); /** * Read user-defined configuration for a dependency from new and old location. @@ -165,7 +167,7 @@ function loadConfig(): ConfigT { * Default configuration can be overriden by a project */ return merge( - {...defaultConfig, root: process.cwd()}, + {...defaultConfig, root: projectRoot}, readProjectConfigFromDisk(), ); } From 50674d7aaa79c2eaef97545ef329bf0d68a30cb2 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 10:14:51 +0100 Subject: [PATCH 30/47] Validate dependencies separately --- packages/cli/src/commands/config/config.js | 3 +- .../src/tools/config/readConfigFromDisk.js | 11 +++- packages/cli/src/tools/config/samples.js | 55 ++++++++++--------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js index c80463319..024d6e5a5 100644 --- a/packages/cli/src/commands/config/config.js +++ b/packages/cli/src/commands/config/config.js @@ -10,6 +10,7 @@ export default { name: 'config', description: 'Print CLI configuration', func: async (_, ctx) => { - console.log(JSON.stringify(ctx, null, 2)); + const a = JSON.stringify(ctx, null, 2); + console.log(); }, }; diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index bec948c1b..a313c007e 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -3,6 +3,7 @@ * * Loads and validates a project configuration */ +import {forEach} from 'lodash'; import comsmiconfig from 'cosmiconfig'; import path from 'path'; import {validate} from 'jest-validate'; @@ -19,6 +20,7 @@ import getPackageConfiguration from '../getPackageConfiguration'; import { config as exampleConfig, legacyConfig as exampleLegacyConfig, + detectedDependencyConfig, } from './samples'; /** @@ -33,12 +35,19 @@ const searchPlaces = ['react-native.config.js', 'package.json']; export function readProjectConfigFromDisk(): ProjectUserConfigT { const explorer = comsmiconfig('react-native', {searchPlaces}); - const {config} = explorer.searchSync() || {config: {}}; + const { + config: {dependencies, ...config}, + } = explorer.searchSync() || {config: {}}; validate(config, {exampleConfig}); + forEach(dependencies, dependency => + validate(dependency, {exampleConfig: detectedDependencyConfig}), + ); + return { ...config, + dependencies, reactNativePath: config.reactNativePath ? config.reactNativePath : resolveReactNativePath(), diff --git a/packages/cli/src/tools/config/samples.js b/packages/cli/src/tools/config/samples.js index bc1dbfb10..5096a3764 100644 --- a/packages/cli/src/tools/config/samples.js +++ b/packages/cli/src/tools/config/samples.js @@ -44,35 +44,36 @@ const dependencyConfig = { commands: ['./local-cli/runWindows/runWindows.js'], }; +export const detectedDependencyConfig = { + root: './path/to/library', + platforms: { + ios: { + sourceDir: './path/to/library/ios', + folder: './path/to/library', + pbxprojPath: './path/to/library/ios/CodePush.xcodeproj/project.pbxproj', + podfile: './path/to/podfile', + podspec: 'CodePush', + projectPath: './path/to/library/ios/CodePush.xcodeproj', + projectName: 'CodePush.xcodeproj', + libraryFolder: 'Libraries', + sharedLibraries: ['libz'], + }, + android: { + sourceDir: './path/to/library/android/app', + folder: './path/to/library', + manifest: {}, + packageImportPath: 'import com.hello.MyPackage;', + packageInstance: 'new MyPackage()', + }, + }, + assets: dependencyConfig.dependency.assets, + hooks: dependencyConfig.dependency.hooks, + params: dependencyConfig.dependency.params, +}; + const projectConfig = { dependencies: { - 'react-native-webview': { - root: './path/to/library', - platforms: { - ios: { - sourceDir: './path/to/library/ios', - folder: './path/to/library', - pbxprojPath: - './path/to/library/ios/CodePush.xcodeproj/project.pbxproj', - podfile: './path/to/podfile', - podspec: 'CodePush', - projectPath: './path/to/library/ios/CodePush.xcodeproj', - projectName: 'CodePush.xcodeproj', - libraryFolder: 'Libraries', - sharedLibraries: ['libz'], - }, - android: { - sourceDir: './path/to/library/android/app', - folder: './path/to/library', - manifest: {}, - packageImportPath: 'import com.hello.MyPackage;', - packageInstance: 'new MyPackage()', - }, - }, - assets: dependencyConfig.dependency.assets, - hooks: dependencyConfig.dependency.hooks, - params: dependencyConfig.dependency.params, - }, + 'react-native-webview': detectedDependencyConfig, }, commands: dependencyConfig.commands, haste: { From 752104b6f7ede2d3670216a17ded5a0c82fc518b Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 13:50:28 +0100 Subject: [PATCH 31/47] Use Joi to validate schema --- packages/cli/package.json | 3 +- packages/cli/src/commands/config/config.js | 2 +- .../src/tools/config/readConfigFromDisk.js | 141 +++--------------- packages/cli/src/tools/config/samples.js | 106 ------------- packages/cli/src/tools/config/schema.js | 115 ++++++++++++++ 5 files changed, 134 insertions(+), 233 deletions(-) delete mode 100644 packages/cli/src/tools/config/samples.js create mode 100644 packages/cli/src/tools/config/schema.js diff --git a/packages/cli/package.json b/packages/cli/package.json index db7d13f7b..fa86ac4bc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,7 +24,6 @@ "compression": "^1.7.1", "connect": "^3.6.5", "cosmiconfig": "^5.1.0", - "dedent": "^0.7.0", "deepmerge": "^3.2.0", "denodeify": "^1.2.1", "envinfo": "^5.7.0", @@ -35,7 +34,7 @@ "glob": "^7.1.1", "graceful-fs": "^4.1.3", "inquirer": "^3.0.6", - "jest-validate": "^24.5.0", + "joi": "^14.3.1", "lodash": "^4.17.5", "metro": "^0.53.1", "metro-config": "^0.53.1", diff --git a/packages/cli/src/commands/config/config.js b/packages/cli/src/commands/config/config.js index 024d6e5a5..e01642dbc 100644 --- a/packages/cli/src/commands/config/config.js +++ b/packages/cli/src/commands/config/config.js @@ -11,6 +11,6 @@ export default { description: 'Print CLI configuration', func: async (_, ctx) => { const a = JSON.stringify(ctx, null, 2); - console.log(); + console.log(a); }, }; diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index a313c007e..cee037896 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -3,11 +3,9 @@ * * Loads and validates a project configuration */ -import {forEach} from 'lodash'; +import Joi from 'joi'; import comsmiconfig from 'cosmiconfig'; import path from 'path'; -import {validate} from 'jest-validate'; -import dedent from 'dedent'; import { type DependencyUserConfigT, @@ -17,11 +15,7 @@ import { import resolveReactNativePath from './resolveReactNativePath'; import getPackageConfiguration from '../getPackageConfiguration'; -import { - config as exampleConfig, - legacyConfig as exampleLegacyConfig, - detectedDependencyConfig, -} from './samples'; +import * as schema from './schema'; /** * Places to look for the new configuration @@ -35,19 +29,16 @@ const searchPlaces = ['react-native.config.js', 'package.json']; export function readProjectConfigFromDisk(): ProjectUserConfigT { const explorer = comsmiconfig('react-native', {searchPlaces}); - const { - config: {dependencies, ...config}, - } = explorer.searchSync() || {config: {}}; + const {config} = explorer.searchSync() || {config: {}}; - validate(config, {exampleConfig}); + const result = Joi.validate(config, schema.projectUserConfig); - forEach(dependencies, dependency => - validate(dependency, {exampleConfig: detectedDependencyConfig}), - ); + if (result.error) { + throw result.error; + } return { ...config, - dependencies, reactNativePath: config.reactNativePath ? config.reactNativePath : resolveReactNativePath(), @@ -76,14 +67,11 @@ export function readDependencyConfigFromDisk( return undefined; } - validate(config, { - exampleConfig, - title: { - warning: `Warnings from ${dependencyName}`, - error: `Errors from ${dependencyName}`, - deprecation: `Deprecations from ${dependencyName}`, - }, - }); + const result = Joi.validate(config, schema.dependencyUserConfig); + + if (result.error) { + throw result.error; + } return config; } @@ -107,106 +95,11 @@ export function readLegacyDependencyConfigFromDisk( return undefined; } - validate(config, { - exampleConfig: exampleLegacyConfig, - deprecatedConfig: { - plugin: ({plugin}) => dedent` - Setting \`rnpm.plugin\` in \`package.json\` in order to extend - React Native CLI has been deprecated and will stop working in the next - React Native release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "commands": ${JSON.stringify([].concat(plugin))} - } - }`, - platform: ({platform}) => dedent` - Setting \`rnpm.platform\` in \`package.json\` in order to define - additional platforms has been deprecated and will stop working in the next - React Native release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "platforms": { - "": "${platform}" - } - } - }`, - haste: ({haste}) => dedent` - Setting \`rnpm.haste\` in \`package.json\` in order to define - additional settings for Metro has been deprecated and will stop - working in next release. - - We now automatically configure Metro to support your platform.`, - ios: ({ios}) => dedent` - Setting \`rnpm.ios\` in \`package.json\` has been deprecated and will stop - working in next release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "dependency": { - "ios": ${JSON.stringify(ios)} - } - } - }`, - android: ({android}) => dedent` - Setting \`rnpm.android\` in \`package.json\` has been deprecated and will stop - working in next release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "dependency": { - "android": ${JSON.stringify(android)} - } - } - }`, - assets: ({assets}) => dedent` - Setting \`rnpm.assets\` in \`package.json\` has been deprecated and will stop - working in next release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "dependency": { - "assets": ${JSON.stringify(assets)} - } - } - }`, - commands: ({commands}) => dedent` - Setting \`rnpm.commands\` in \`package.json\` has been deprecated and will stop - working in next release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "dependency": { - "hooks": ${JSON.stringify(commands)} - } - } - }`, - params: ({params}) => dedent` - Setting \`rnpm.params\` in \`package.json\` has been deprecated and will stop - working in next release. - - Consider setting the following in the \`package.json\` instead: - { - "react-native": { - "dependency": { - "params": ${JSON.stringify(params)} - } - } - }`, - }, - title: { - warning: `Warnings from ${dependencyName}`, - error: `Errors from ${dependencyName}`, - deprecation: `Deprecations from ${dependencyName}`, - }, - }); + const result = Joi.validate(config, schema.legacyDependencyConfig); + + if (result.error) { + throw result.error; + } return { dependency: { diff --git a/packages/cli/src/tools/config/samples.js b/packages/cli/src/tools/config/samples.js deleted file mode 100644 index 5096a3764..000000000 --- a/packages/cli/src/tools/config/samples.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @flow - * - * Sample configuration objects that are used for `jest-validate`. - * - * See `./types.flow` for explanation of what - * the properties are responsible for. - */ -import {multipleValidOptions} from 'jest-validate'; - -import {type UserConfigT, type LegacyDependencyUserConfigT} from './types.flow'; - -const dependencyConfig = { - dependency: { - platforms: { - android: { - sourceDir: './android/app', - manifestPath: './path/to/AndroidManifest.xml', - packageImportPath: 'import com.my.package.MyReactPackage', - packageInstance: 'new MyReactPackage()', - }, - ios: { - project: 'MyProject.xcodeproj', - sharedLibraries: ['libz'], - libraryFolder: 'Libraries', - }, - }, - assets: ['./path/to/asset.png'], - hooks: { - prelink: './path/to/a/command.sh', - postlink: './path/to/a/command.sh', - }, - params: [ - { - type: 'input', - name: 'myKey', - message: 'What is your deployment key value?', - }, - ], - }, - platforms: { - windows: './platform.js', - }, - commands: ['./local-cli/runWindows/runWindows.js'], -}; - -export const detectedDependencyConfig = { - root: './path/to/library', - platforms: { - ios: { - sourceDir: './path/to/library/ios', - folder: './path/to/library', - pbxprojPath: './path/to/library/ios/CodePush.xcodeproj/project.pbxproj', - podfile: './path/to/podfile', - podspec: 'CodePush', - projectPath: './path/to/library/ios/CodePush.xcodeproj', - projectName: 'CodePush.xcodeproj', - libraryFolder: 'Libraries', - sharedLibraries: ['libz'], - }, - android: { - sourceDir: './path/to/library/android/app', - folder: './path/to/library', - manifest: {}, - packageImportPath: 'import com.hello.MyPackage;', - packageInstance: 'new MyPackage()', - }, - }, - assets: dependencyConfig.dependency.assets, - hooks: dependencyConfig.dependency.hooks, - params: dependencyConfig.dependency.params, -}; - -const projectConfig = { - dependencies: { - 'react-native-webview': detectedDependencyConfig, - }, - commands: dependencyConfig.commands, - haste: { - providesModuleNodeModules: ['react-native-windows'], - platforms: ['windows'], - }, - reactNativePath: './path/to/react-native', -}; - -export const config: UserConfigT = { - ...projectConfig, - ...dependencyConfig, -}; - -export const legacyConfig: LegacyDependencyUserConfigT = { - plugin: multipleValidOptions('./path/to/a/plugin.js', [ - './path/to/foo/plugin.js', - './path/to/bar/plugin.js', - ]), - platform: './path/to/a/platform.js', - haste: { - platforms: ['windows'], - providesModuleNodeModules: ['react-native-windows'], - }, - assets: dependencyConfig.dependency.assets, - commands: dependencyConfig.dependency.hooks, - android: dependencyConfig.dependency.platforms.android, - ios: dependencyConfig.dependency.platforms.ios, - params: dependencyConfig.dependency.params, -}; diff --git a/packages/cli/src/tools/config/schema.js b/packages/cli/src/tools/config/schema.js new file mode 100644 index 000000000..b17fc4e43 --- /dev/null +++ b/packages/cli/src/tools/config/schema.js @@ -0,0 +1,115 @@ +/** + * @flow + */ +import t from 'joi'; + +const map = (key, value) => + t + .object() + .unknown(true) + .pattern(key, value); + +/** + * Schema for DependencyUserConfigT + */ +export const dependencyUserConfig = t.object({ + dependency: t.object({ + platforms: map(t.string(), t.any()).keys({ + ios: t + .object({ + project: t.string(), + sharedLibraries: t.array().items(t.string()), + libraryFolder: t.string(), + }) + .allow(null), + android: t + .object({ + sourceDir: t.string(), + manifestPath: t.string(), + packageImportPath: t.string(), + packageInstance: t.string(), + }) + .allow(null), + }), + assets: t.array().items(t.string()), + hooks: map(t.string(), t.string()), + params: t.array().items( + t.object({ + name: t.string(), + type: t.string(), + message: t.string(), + }), + ), + }), + platforms: map(t.string(), t.string()), + commands: t.array().items(t.string()), +}); + +/** + * Schema for LegacyDependencyUserConfigT + */ +export const legacyDependencyConfig = t.object({ + plugin: t.alternatives().try(t.array().items(t.string()), t.string()), + platform: t.string(), + haste: t.object({ + platforms: t.array().items(t.string()), + providesModuleNodeModules: t.array().items(t.string()), + }), + assets: t.reach(dependencyUserConfig, 'dependency.assets'), + commands: t.reach(dependencyUserConfig, 'dependency.hooks'), + android: t.reach(dependencyUserConfig, 'dependency.platforms.android'), + ios: t.reach(dependencyUserConfig, 'dependency.platforms.ios'), + params: t.reach(dependencyUserConfig, 'dependency.params'), +}); + +/** + * Schema for ProjectUserConfigT + */ +export const projectUserConfig = t.object({ + dependencies: map( + t.string(), + t + .object({ + platforms: map(t.string(), t.any()).keys({ + ios: t + .object({ + sourceDir: t.string(), + folder: t.string(), + pbxprojPath: t.string(), + podfile: t.string(), + podspec: t.string(), + projectPath: t.string(), + projectName: t.string(), + libraryFolder: t.string(), + sharedLibraries: t.array().items(t.string()), + }) + .allow(null), + android: t + .object({ + sourceDir: t.string(), + folder: t.string(), + packageImportPath: t.string(), + packageInstance: t.string(), + }) + .allow(null), + }), + assets: t.array().items(t.string()), + hooks: map(t.string(), t.string()), + params: t.array().items( + t.object({ + name: t.string(), + type: t.string(), + message: t.string(), + }), + ), + }) + .allow(null), + ).default({}), + commands: t.array().items(t.string()), + haste: t.object({ + providesModuleNodeModules: t.array().items(t.string()), + platforms: t.array().items(t.string()), + }), + reactNativePath: t.string(), + root: t.string(), +}); From 8a491a8108c68d32ca82ba883ee7c7176fe24d01 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 27 Mar 2019 14:08:31 +0100 Subject: [PATCH 32/47] Read Joi validated config --- .../src/tools/config/readConfigFromDisk.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index cee037896..9b6bb9038 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -38,7 +38,7 @@ export function readProjectConfigFromDisk(): ProjectUserConfigT { } return { - ...config, + ...result.value, reactNativePath: config.reactNativePath ? config.reactNativePath : resolveReactNativePath(), @@ -73,7 +73,7 @@ export function readDependencyConfigFromDisk( throw result.error; } - return config; + return result.value; } /** @@ -95,25 +95,25 @@ export function readLegacyDependencyConfigFromDisk( return undefined; } - const result = Joi.validate(config, schema.legacyDependencyConfig); + const {error, value} = Joi.validate(config, schema.legacyDependencyConfig); - if (result.error) { - throw result.error; + if (error) { + throw error; } return { dependency: { platforms: { - ios: config.ios, - android: config.android, + ios: value.ios, + android: value.android, }, - assets: config.assets, - hooks: config.commands, - params: config.params, + assets: value.assets, + hooks: value.commands, + params: value.params, }, - commands: [].concat(config.plugin || []), - platforms: config.platform - ? require(path.join(rootFolder, config.platform)) + commands: [].concat(value.plugin || []), + platforms: value.platform + ? require(path.join(rootFolder, value.platform)) : undefined, }; } From 1f1da28a54ced0a58aa34f436e122ef34f58202b Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Thu, 28 Mar 2019 16:14:22 +0100 Subject: [PATCH 33/47] Rewrite configuration once again --- packages/cli/src/tools/config/index.js | 155 +++++----------- .../src/tools/config/readConfigFromDisk.js | 43 +++-- .../tools/config/resolveReactNativePath.js | 14 +- packages/cli/src/tools/config/schema.js | 173 +++++++++++------- packages/cli/src/tools/config/types.flow.js | 100 ++++------ 5 files changed, 215 insertions(+), 270 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 2c5cc35a5..59eb4098c 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -1,7 +1,7 @@ /** * @flow */ -import {get, pickBy, mapValues} from 'lodash'; +import dedent from 'dedent'; import path from 'path'; import merge from 'deepmerge'; @@ -12,18 +12,14 @@ import { readLegacyDependencyConfigFromDisk, } from './readConfigFromDisk'; -import { - type DependencyConfigT, - type ConfigT, - type PlatformsT, - type DependenciesConfigT, -} from './types.flow'; +import {type ConfigT, type ProjectConfigT} from './types.flow'; /** * Built-in platforms */ import * as ios from '../ios'; import * as android from '../android'; +import resolveReactNativePath from './resolveReactNativePath'; /** * Loads CLI configuration @@ -31,125 +27,55 @@ import * as android from '../android'; function loadConfig(): ConfigT { const projectRoot = process.cwd(); - const defaultConfig = findDependencies().reduce( - (acc: DependenciesConfigT, dependencyName) => { + const inferredProjectConfig = findDependencies().reduce( + (acc: ProjectConfigT, dependencyName) => { const root = path.join(projectRoot, 'node_modules', dependencyName); - /** - * Read user-defined configuration for a dependency from new and old location. - * We provide empty object at the end on purpose to access it easily with `get` - */ const config = - readDependencyConfigFromDisk(root, dependencyName) || readLegacyDependencyConfigFromDisk(root, dependencyName) || - {}; - - /** - * Because of legacy reasons (`rnpm` configuration), platforms can be an object. - * This code handles this special case. In future releases, we will allow paths - * to files only. - */ - const availablePlatforms: PlatformsT = mapValues( - get(config, 'platforms', {}), - pathOrObject => - typeof pathOrObject === 'string' - ? require(path.join(dependencyName, pathOrObject)) - : pathOrObject, - ); - - /** - * Lazily gets dependency config for a current dependency. - * - * Note: this code is outside of the dynamic getter - * on purpose to make Flow work. - */ - const getDependencyConfig = (): DependencyConfigT => { - /** - * At the time of executing this function, `acc.platforms` will already - * have all the platforms that are defined by other dependencies too. - */ - const dependencyPlatforms = Object.keys(acc.platforms).reduce( - (dependency, platform) => { - /** - * We generate a configuration for a given platform by passing - * some non-standard developer-defined settings too - */ - const platformConfig = get( - config, - `dependency.platforms.${platform}`, - {}, - ); - // $FlowIssue: Invalid platforms are already filtered-out below - const detectedConfig = acc.platforms[platform].dependencyConfig( - root, - platformConfig, - ); - if (detectedConfig === null) { - dependency[platform] = null; - } else { - dependency[platform] = { - ...detectedConfig, - ...platformConfig, - }; - } - return dependency; - }, - { - ios: null, - android: null, - }, - ); - return { - root, - platforms: dependencyPlatforms, - assets: get(config, 'assets', []), - hooks: get(config, 'hooks', {}), - params: get(config, 'params', []), - }; - }; - + readDependencyConfigFromDisk(root, dependencyName); + console.log(config); return { + ...acc, dependencies: { ...acc.dependencies, // $FlowIssue: Computed getters are not yet supported. get [dependencyName]() { - return getDependencyConfig(); + return { + platforms: Object.keys(acc.platforms).reduce( + (dependency, platform) => + acc.platforms[platform].dependencyConfig( + root, + config.dependency.platforms[platform], + ), + {}, + ), + assets: config.dependency.assets, + hooks: config.dependency.hooks, + params: config.dependency.params, + }; }, }, commands: acc.commands.concat( - get(config, 'commands', []).map(pathToCommand => + config.commands.map(pathToCommand => path.join(dependencyName, pathToCommand), ), ), - /** - * Note: In this context, a `platform` is a valid target that we can - * link dependencies for. - * - * This is impossible when either `projectConfig` and `dependencyConfig` are - * not provided hence the `pickBy` check. - */ platforms: { ...acc.platforms, - ...pickBy( - availablePlatforms, - (platform: PlatformsT) => - typeof platform.dependencyConfig === 'function' && - typeof platform.projectConfig === 'function' && - typeof platform.linkConfig === 'function', - ), + ...config.platforms, }, haste: { providesModuleNodeModules: acc.haste.providesModuleNodeModules.concat( - Object.keys(availablePlatforms).length > 0 ? dependencyName : [], + Object.keys(config.platforms).length > 0 ? dependencyName : [], ), - platforms: [ - ...acc.haste.platforms, - ...Object.keys(availablePlatforms), - ], + platforms: [...acc.haste.platforms, ...Object.keys(config.platforms)], }, }; }, ({ + root: projectRoot, + reactNativePath: resolveReactNativePath(), dependencies: {}, commands: [], platforms: { @@ -160,16 +86,27 @@ function loadConfig(): ConfigT { providesModuleNodeModules: [], platforms: [], }, - }: DependenciesConfigT), + }: ProjectConfigT), ); - /** - * Default configuration can be overriden by a project - */ - return merge( - {...defaultConfig, root: projectRoot}, - readProjectConfigFromDisk(), - ); + const config = merge(inferredProjectConfig, readProjectConfigFromDisk()); + + if (config.reactNativePath === 'not-found') { + throw new Error(dedent` + Unable to find React Native files. Make sure "react-native" module is installed + in your project dependencies. + + If you are using React Native from a non-standard location, consider setting: + { + "react-native": { + "reactNativePath": "./path/to/react-native" + } + } + in your \`package.json\`. + `); + } + + return config; } export default loadConfig; diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 9b6bb9038..56eb1be21 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -55,7 +55,7 @@ export function readProjectConfigFromDisk(): ProjectUserConfigT { export function readDependencyConfigFromDisk( rootFolder: string, dependencyName: string, -): ?DependencyUserConfigT { +): DependencyUserConfigT { const explorer = comsmiconfig('react-native', { stopDir: rootFolder, searchPlaces, @@ -63,16 +63,12 @@ export function readDependencyConfigFromDisk( const {config} = explorer.searchSync(rootFolder) || {config: undefined}; - if (!config) { - return undefined; - } - const result = Joi.validate(config, schema.dependencyUserConfig); if (result.error) { throw result.error; } - + console.log(config); return result.value; } @@ -95,25 +91,36 @@ export function readLegacyDependencyConfigFromDisk( return undefined; } - const {error, value} = Joi.validate(config, schema.legacyDependencyConfig); + const legacyValidation = Joi.validate(config, schema.legacyDependencyConfig); - if (error) { - throw error; + if (legacyValidation.error) { + throw legacyValidation.error; } - return { + const transformedConfig = { dependency: { platforms: { - ios: value.ios, - android: value.android, + ios: legacyValidation.value.ios, + android: legacyValidation.value.android, }, - assets: value.assets, - hooks: value.commands, - params: value.params, + assets: legacyValidation.value.assets, + hooks: legacyValidation.value.commands, + params: legacyValidation.value.params, }, - commands: [].concat(value.plugin || []), - platforms: value.platform - ? require(path.join(rootFolder, value.platform)) + commands: [].concat(legacyValidation.value.plugin || []), + platforms: legacyValidation.value.platform + ? require(path.join(rootFolder, legacyValidation.value.platform)) : undefined, }; + + const validation = Joi.validate( + transformedConfig, + schema.dependencyUserConfig, + ); + + if (validation.error) { + throw validation.error; + } + + return validation.value; } diff --git a/packages/cli/src/tools/config/resolveReactNativePath.js b/packages/cli/src/tools/config/resolveReactNativePath.js index dabc30177..203db7661 100644 --- a/packages/cli/src/tools/config/resolveReactNativePath.js +++ b/packages/cli/src/tools/config/resolveReactNativePath.js @@ -2,7 +2,6 @@ * @flow */ import path from 'path'; -import dedent from 'dedent'; /** * Finds path to React Native inside `node_modules` or throws @@ -17,17 +16,6 @@ export default function resolveReactNativePath() { }), ); } catch (_ignored) { - throw new Error(dedent` - Unable to find React Native files. Make sure "react-native" module is installed - in your project dependencies. - - If you are using React Native from a non-standard location, consider setting: - { - "react-native": { - "reactNativePath": "./path/to/react-native" - } - } - in your \`package.json\`. - `); + return 'not-found'; } } diff --git a/packages/cli/src/tools/config/schema.js b/packages/cli/src/tools/config/schema.js index b17fc4e43..dec2dfb56 100644 --- a/packages/cli/src/tools/config/schema.js +++ b/packages/cli/src/tools/config/schema.js @@ -2,6 +2,7 @@ * @flow */ import t from 'joi'; +import {fromPairs} from 'lodash'; const map = (key, value) => t @@ -9,41 +10,65 @@ const map = (key, value) => .unknown(true) .pattern(key, value); +const obj = keys => fromPairs(keys.map(key => [key, undefined])); + /** * Schema for DependencyUserConfigT */ -export const dependencyUserConfig = t.object({ - dependency: t.object({ - platforms: map(t.string(), t.any()).keys({ - ios: t - .object({ - project: t.string(), - sharedLibraries: t.array().items(t.string()), - libraryFolder: t.string(), - }) - .allow(null), - android: t - .object({ - sourceDir: t.string(), - manifestPath: t.string(), - packageImportPath: t.string(), - packageInstance: t.string(), - }) - .allow(null), - }), - assets: t.array().items(t.string()), - hooks: map(t.string(), t.string()), - params: t.array().items( +export const dependencyUserConfig = t + .object({ + dependency: t + .object({ + platforms: map(t.string(), t.any()) + .keys({ + ios: t + .object({ + project: t.string(), + sharedLibraries: t.array().items(t.string()), + libraryFolder: t.string(), + }) + .default({}), + android: t + .object({ + sourceDir: t.string(), + manifestPath: t.string(), + packageImportPath: t.string(), + packageInstance: t.string(), + }) + .default({}), + }) + .default(), + assets: t + .array() + .items(t.string()) + .default([]), + hooks: map(t.string(), t.string()).default(), + params: t + .array() + .items( + t.object({ + name: t.string(), + type: t.string(), + message: t.string(), + }), + ) + .default(), + }) + .default(), + platforms: map( + t.string(), t.object({ - name: t.string(), - type: t.string(), - message: t.string(), + dependencyConfig: t.func(), + projectConfig: t.func(), + linkConfig: t.func(), }), - ), - }), - platforms: map(t.string(), t.string()), - commands: t.array().items(t.string()), -}); + ).default(), + commands: t + .array() + .items(t.string()) + .default([]), + }) + .default(); /** * Schema for LegacyDependencyUserConfigT @@ -70,46 +95,64 @@ export const projectUserConfig = t.object({ t.string(), t .object({ - platforms: map(t.string(), t.any()).keys({ - ios: t - .object({ - sourceDir: t.string(), - folder: t.string(), - pbxprojPath: t.string(), - podfile: t.string(), - podspec: t.string(), - projectPath: t.string(), - projectName: t.string(), - libraryFolder: t.string(), - sharedLibraries: t.array().items(t.string()), - }) - .allow(null), - android: t + platforms: map(t.string(), t.any()) + .keys({ + ios: t + .object({ + sourceDir: t.string(), + folder: t.string(), + pbxprojPath: t.string(), + podfile: t.string(), + podspec: t.string(), + projectPath: t.string(), + projectName: t.string(), + libraryFolder: t.string(), + sharedLibraries: t.array().items(t.string()), + }) + .allow(null), + android: t + .object({ + sourceDir: t.string(), + folder: t.string(), + packageImportPath: t.string(), + packageInstance: t.string(), + }) + .allow(null), + }) + .default(), + assets: t + .array() + .items(t.string()) + .default([]), + hooks: map(t.string(), t.string()).default([]), + params: t.array().items( + t .object({ - sourceDir: t.string(), - folder: t.string(), - packageImportPath: t.string(), - packageInstance: t.string(), + name: t.string(), + type: t.string(), + message: t.string(), }) - .allow(null), - }), - assets: t.array().items(t.string()), - hooks: map(t.string(), t.string()), - params: t.array().items( - t.object({ - name: t.string(), - type: t.string(), - message: t.string(), - }), + .default(), ), }) .allow(null), - ).default({}), - commands: t.array().items(t.string()), - haste: t.object({ - providesModuleNodeModules: t.array().items(t.string()), - platforms: t.array().items(t.string()), - }), + ).default(), + commands: t + .array() + .items(t.string()) + .default([]), + haste: t + .object({ + providesModuleNodeModules: t + .array() + .items(t.string()) + .default([]), + platforms: t + .array() + .items(t.string()) + .default([]), + }) + .default(), reactNativePath: t.string(), root: t.string(), }); diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 014a98efd..69091b56c 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -29,13 +29,6 @@ export type ProjectUserConfigT = { reactNativePath?: string, }; -/** - * Project configuration after being processed by the loading mechanism - */ -export type ProjectConfigT = { - reactNativePath: string, -}; - /** * An array of assets defined by a library to link to a project */ @@ -63,17 +56,13 @@ type CommandsT = Array; /** * A map with additional platforms that ship with a dependency. - * - * String format is deprecated and will be removed in the future. */ export type PlatformsT = { - [key: string]: - | string - | { - dependencyConfig?: Function, - projectConfig?: Function, - linkConfig?: Function, - }, + [key: string]: { + dependencyConfig?: Function, + projectConfig?: Function, + linkConfig?: Function, + }, }; /** @@ -85,43 +74,45 @@ type MetroConfigT = { providesModuleNodeModules: Array, }; -/** - * Configuration that ships with a dependency - */ export type DependencyUserConfigT = { - /** - * Key that defines settings related to native code and linking - */ - dependency?: { - /** - * Additional configuration for native code on per platform basis. - * Useful to overwrite built-in heurstics in non-default setups - */ - platforms?: { - android?: ?AndroidConfigParamsT, - ios?: ?IOSConfigParamsT, + dependency: { + platforms: { + android?: AndroidConfigParamsT, + ios?: IOSConfigParamsT, [key: string]: any, }, - assets?: AssetsT, - hooks?: HooksT, - params?: ParamsT, + assets: AssetsT, + hooks: HooksT, + params: ParamsT, }, - commands?: CommandsT, - platforms?: PlatformsT, + commands: CommandsT, + platforms: PlatformsT, +}; + +export type ProjectConfigT = { + root: string, + reactNativePath: string, + dependencies: { + [key: string]: { + platforms: { + android: DependencyConfigAndroidT | null, + ios: DependencyConfigIOST | null, + [key: string]: any, + }, + assets: AssetsT, + hooks: HooksT, + params: ParamsT, + }, + }, + platforms: PlatformsT, + commands: CommandsT, + haste: MetroConfigT, }; /** * Dependency configuration after being processed by the CLI */ export type DependencyConfigT = { - /** - * Dependency location on the disk - */ - root: string, - /** - * An object containing configuration for each of the dependencies, - * or null, if dependency does not support given platform - */ platforms: { android: DependencyConfigAndroidT | null, ios: DependencyConfigIOST | null, @@ -184,25 +175,4 @@ export type DependenciesUserConfigT = { haste?: MetroConfigT, }; -export type DependenciesConfigT = { - dependencies: { - [key: string]: DependencyConfigT, - }, - /** - * An array of platforms collected by parsing dependencies - */ - platforms: PlatformsT, - /** - * An array of commands collected by parsing dependencies - */ - commands: CommandsT, - /** - * Extra Metro configuration to use to support additonal platforms - */ - haste: MetroConfigT, -}; - -export type ConfigT = ProjectConfigT & - DependenciesConfigT & { - root: string, - }; +export type ConfigT = ProjectConfigT; From 089669b67edb50dd96cc4c500cb6a07a55b17f6f Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Thu, 28 Mar 2019 16:23:54 +0100 Subject: [PATCH 34/47] Further simplification --- packages/cli/src/tools/config/index.js | 6 +- .../src/tools/config/readConfigFromDisk.js | 50 ++++------- packages/cli/src/tools/config/schema.js | 88 ++++++------------- 3 files changed, 44 insertions(+), 100 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 59eb4098c..4faab21cd 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -32,9 +32,9 @@ function loadConfig(): ConfigT { const root = path.join(projectRoot, 'node_modules', dependencyName); const config = - readLegacyDependencyConfigFromDisk(root, dependencyName) || - readDependencyConfigFromDisk(root, dependencyName); - console.log(config); + readLegacyDependencyConfigFromDisk(root) || + readDependencyConfigFromDisk(root); + return { ...acc, dependencies: { diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 56eb1be21..31010e124 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -48,13 +48,9 @@ export function readProjectConfigFromDisk(): ProjectUserConfigT { /** * Reads a dependency configuration as defined by the developer * inside `node_modules`. - * - * Returns `undefined` when no custom configuration is found - * in the dependency root. */ export function readDependencyConfigFromDisk( rootFolder: string, - dependencyName: string, ): DependencyUserConfigT { const explorer = comsmiconfig('react-native', { stopDir: rootFolder, @@ -68,59 +64,43 @@ export function readDependencyConfigFromDisk( if (result.error) { throw result.error; } - console.log(config); + return result.value; } /** * Reads a legacy configuaration from a `package.json` "rnpm" key. - * - * Prints deprecation warnings for each of the keys along the upgrade instructions. - * - * Returns `undefined` when no configuration is provided. */ export function readLegacyDependencyConfigFromDisk( rootFolder: string, - dependencyName: string, ): ?DependencyUserConfigT { - const config = getPackageConfiguration(rootFolder); + const config = require(path.join(rootFolder, 'package.json')).rnpm; - // For historical reasons, `getPackageConfiguration` always returns an - // object, including empty when no cofinguration found. - if (Object.keys(config).length === 0) { + if (!config) { return undefined; } - const legacyValidation = Joi.validate(config, schema.legacyDependencyConfig); - - if (legacyValidation.error) { - throw legacyValidation.error; - } - const transformedConfig = { dependency: { platforms: { - ios: legacyValidation.value.ios, - android: legacyValidation.value.android, + ios: config.ios, + android: config.android, }, - assets: legacyValidation.value.assets, - hooks: legacyValidation.value.commands, - params: legacyValidation.value.params, + assets: config.assets, + hooks: config.commands, + params: config.params, }, - commands: [].concat(legacyValidation.value.plugin || []), - platforms: legacyValidation.value.platform - ? require(path.join(rootFolder, legacyValidation.value.platform)) + commands: [].concat(config.plugin || []), + platforms: config.platform + ? require(path.join(rootFolder, config.platform)) : undefined, }; - const validation = Joi.validate( - transformedConfig, - schema.dependencyUserConfig, - ); + const result = Joi.validate(transformedConfig, schema.dependencyUserConfig); - if (validation.error) { - throw validation.error; + if (result.error) { + throw result.error; } - return validation.value; + return result.value; } diff --git a/packages/cli/src/tools/config/schema.js b/packages/cli/src/tools/config/schema.js index dec2dfb56..a68c0abdd 100644 --- a/packages/cli/src/tools/config/schema.js +++ b/packages/cli/src/tools/config/schema.js @@ -2,7 +2,6 @@ * @flow */ import t from 'joi'; -import {fromPairs} from 'lodash'; const map = (key, value) => t @@ -10,8 +9,6 @@ const map = (key, value) => .unknown(true) .pattern(key, value); -const obj = keys => fromPairs(keys.map(key => [key, undefined])); - /** * Schema for DependencyUserConfigT */ @@ -70,33 +67,16 @@ export const dependencyUserConfig = t }) .default(); -/** - * Schema for LegacyDependencyUserConfigT - */ -export const legacyDependencyConfig = t.object({ - plugin: t.alternatives().try(t.array().items(t.string()), t.string()), - platform: t.string(), - haste: t.object({ - platforms: t.array().items(t.string()), - providesModuleNodeModules: t.array().items(t.string()), - }), - assets: t.reach(dependencyUserConfig, 'dependency.assets'), - commands: t.reach(dependencyUserConfig, 'dependency.hooks'), - android: t.reach(dependencyUserConfig, 'dependency.platforms.android'), - ios: t.reach(dependencyUserConfig, 'dependency.platforms.ios'), - params: t.reach(dependencyUserConfig, 'dependency.params'), -}); - /** * Schema for ProjectUserConfigT */ -export const projectUserConfig = t.object({ - dependencies: map( - t.string(), - t - .object({ - platforms: map(t.string(), t.any()) - .keys({ +export const projectUserConfig = t + .object({ + dependencies: map( + t.string(), + t + .object({ + platforms: map(t.string(), t.any()).keys({ ios: t .object({ sourceDir: t.string(), @@ -118,41 +98,25 @@ export const projectUserConfig = t.object({ packageInstance: t.string(), }) .allow(null), - }) - .default(), - assets: t - .array() - .items(t.string()) - .default([]), - hooks: map(t.string(), t.string()).default([]), - params: t.array().items( - t - .object({ + }), + assets: t.array().items(t.string()), + hooks: map(t.string(), t.string()), + params: t.array().items( + t.object({ name: t.string(), type: t.string(), message: t.string(), - }) - .default(), - ), - }) - .allow(null), - ).default(), - commands: t - .array() - .items(t.string()) - .default([]), - haste: t - .object({ - providesModuleNodeModules: t - .array() - .items(t.string()) - .default([]), - platforms: t - .array() - .items(t.string()) - .default([]), - }) - .default(), - reactNativePath: t.string(), - root: t.string(), -}); + }), + ), + }) + .allow(null), + ), + commands: t.array().items(t.string()), + haste: t.object({ + providesModuleNodeModules: t.array().items(t.string()), + platforms: t.array().items(t.string()), + }), + reactNativePath: t.string(), + root: t.string(), + }) + .default({}); From c2c30e79a1b92e72d328102c7b1dba9617004fe8 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Thu, 28 Mar 2019 16:31:37 +0100 Subject: [PATCH 35/47] Simplify code again --- packages/cli/src/tools/config/index.js | 9 +- .../src/tools/config/readConfigFromDisk.js | 18 +-- packages/cli/src/tools/config/schema.js | 8 +- packages/cli/src/tools/config/types.flow.js | 129 ++---------------- packages/cli/src/tools/types.flow.js | 4 +- 5 files changed, 29 insertions(+), 139 deletions(-) diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 4faab21cd..9f25455ff 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -12,7 +12,7 @@ import { readLegacyDependencyConfigFromDisk, } from './readConfigFromDisk'; -import {type ConfigT, type ProjectConfigT} from './types.flow'; +import {type ProjectConfigT} from './types.flow'; /** * Built-in platforms @@ -24,7 +24,7 @@ import resolveReactNativePath from './resolveReactNativePath'; /** * Loads CLI configuration */ -function loadConfig(): ConfigT { +function loadConfig(): ProjectConfigT { const projectRoot = process.cwd(); const inferredProjectConfig = findDependencies().reduce( @@ -89,7 +89,10 @@ function loadConfig(): ConfigT { }: ProjectConfigT), ); - const config = merge(inferredProjectConfig, readProjectConfigFromDisk()); + const config: ProjectConfigT = merge( + inferredProjectConfig, + readProjectConfigFromDisk(), + ); if (config.reactNativePath === 'not-found') { throw new Error(dedent` diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 31010e124..9bcedf490 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -7,13 +7,9 @@ import Joi from 'joi'; import comsmiconfig from 'cosmiconfig'; import path from 'path'; -import { - type DependencyUserConfigT, - type ProjectUserConfigT, -} from './types.flow'; +import {type DependencyConfigT, type ProjectConfigT} from './types.flow'; import resolveReactNativePath from './resolveReactNativePath'; -import getPackageConfiguration from '../getPackageConfiguration'; import * as schema from './schema'; @@ -26,12 +22,12 @@ const searchPlaces = ['react-native.config.js', 'package.json']; * Reads a project configuration as defined by the user in the current * workspace. */ -export function readProjectConfigFromDisk(): ProjectUserConfigT { +export function readProjectConfigFromDisk(): ProjectConfigT { const explorer = comsmiconfig('react-native', {searchPlaces}); const {config} = explorer.searchSync() || {config: {}}; - const result = Joi.validate(config, schema.projectUserConfig); + const result = Joi.validate(config, schema.projectConfig); if (result.error) { throw result.error; @@ -51,7 +47,7 @@ export function readProjectConfigFromDisk(): ProjectUserConfigT { */ export function readDependencyConfigFromDisk( rootFolder: string, -): DependencyUserConfigT { +): DependencyConfigT { const explorer = comsmiconfig('react-native', { stopDir: rootFolder, searchPlaces, @@ -59,7 +55,7 @@ export function readDependencyConfigFromDisk( const {config} = explorer.searchSync(rootFolder) || {config: undefined}; - const result = Joi.validate(config, schema.dependencyUserConfig); + const result = Joi.validate(config, schema.dependencyConfig); if (result.error) { throw result.error; @@ -73,7 +69,7 @@ export function readDependencyConfigFromDisk( */ export function readLegacyDependencyConfigFromDisk( rootFolder: string, -): ?DependencyUserConfigT { +): ?DependencyConfigT { const config = require(path.join(rootFolder, 'package.json')).rnpm; if (!config) { @@ -96,7 +92,7 @@ export function readLegacyDependencyConfigFromDisk( : undefined, }; - const result = Joi.validate(transformedConfig, schema.dependencyUserConfig); + const result = Joi.validate(transformedConfig, schema.dependencyConfig); if (result.error) { throw result.error; diff --git a/packages/cli/src/tools/config/schema.js b/packages/cli/src/tools/config/schema.js index a68c0abdd..f3f7c144a 100644 --- a/packages/cli/src/tools/config/schema.js +++ b/packages/cli/src/tools/config/schema.js @@ -10,9 +10,9 @@ const map = (key, value) => .pattern(key, value); /** - * Schema for DependencyUserConfigT + * Schema for DependencyConfigT */ -export const dependencyUserConfig = t +export const dependencyConfig = t .object({ dependency: t .object({ @@ -68,9 +68,9 @@ export const dependencyUserConfig = t .default(); /** - * Schema for ProjectUserConfigT + * Schema for ProjectConfigT */ -export const projectUserConfig = t +export const projectConfig = t .object({ dependencies: map( t.string(), diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js index 69091b56c..13802868d 100644 --- a/packages/cli/src/tools/config/types.flow.js +++ b/packages/cli/src/tools/config/types.flow.js @@ -10,30 +10,6 @@ import type { DependencyConfigIOST, } from '../types.flow'; -/** - * Depending on the context, the configuration of the CLI can have both configurations - * within. - */ -export type UserConfigT = ProjectUserConfigT & - DependenciesUserConfigT & - DependencyUserConfigT; - -/** - * Configuration that ships with a project - */ -export type ProjectUserConfigT = { - /** - * A path to a React Native module. Useful, when running from source and there's - * no "react-native" module that `require` can resolve. - */ - reactNativePath?: string, -}; - -/** - * An array of assets defined by a library to link to a project - */ -type AssetsT = Array; - /** * A map of hooks to run pre/post some of the CLI actions */ @@ -43,17 +19,6 @@ type HooksT = { postlink?: string, }; -/** - * Params to ask during linking process if library requires additional - * configuration - */ -type ParamsT = InquirerPromptT[]; - -/** - * Defines an array of commands that a dependency can add to a React Native CLI - */ -type CommandsT = Array; - /** * A map with additional platforms that ship with a dependency. */ @@ -65,27 +30,18 @@ export type PlatformsT = { }, }; -/** - * Metro-related configuration to define to make 3rd party platforms - * work. - */ -type MetroConfigT = { - platforms: Array, - providesModuleNodeModules: Array, -}; - -export type DependencyUserConfigT = { +export type DependencyConfigT = { dependency: { platforms: { android?: AndroidConfigParamsT, ios?: IOSConfigParamsT, [key: string]: any, }, - assets: AssetsT, + assets: string[], hooks: HooksT, - params: ParamsT, + params: InquirerPromptT[], }, - commands: CommandsT, + commands: string[], platforms: PlatformsT, }; @@ -99,80 +55,15 @@ export type ProjectConfigT = { ios: DependencyConfigIOST | null, [key: string]: any, }, - assets: AssetsT, + assets: string[], hooks: HooksT, - params: ParamsT, + params: InquirerPromptT[], }, }, platforms: PlatformsT, - commands: CommandsT, - haste: MetroConfigT, -}; - -/** - * Dependency configuration after being processed by the CLI - */ -export type DependencyConfigT = { - platforms: { - android: DependencyConfigAndroidT | null, - ios: DependencyConfigIOST | null, - [key: string]: any, - }, - assets: AssetsT, - hooks: HooksT, - params: ParamsT, -}; - -/** - * Legacy configuration that can be defined in package.json, - * under "rnpm" key. - */ -export type LegacyDependencyUserConfigT = { - /** - * See DependencyUserConfigT.dependency.assets - */ - assets?: AssetsT, - /** - * See DependencyUserConfigT.dependency.hooks - */ - commands?: HooksT, - /** - * See DependencyUserConfigT.dependency.params - */ - params?: ParamsT, - /** - * See DependencyUserConfigT.dependency.platforms.android - */ - android?: ?AndroidConfigParamsT, - /** - * See DependencyUserConfigT.dependency.platforms.ios - */ - ios?: ?IOSConfigParamsT, - /** - * See DependencyUserConfigT.commands - */ - plugin?: string | CommandsT, - /** - * See DependencyUserConfigT.platforms - */ - platform?: string, - /** - * We don't read this configuration, but infer it from other properties. - */ - haste?: MetroConfigT, -}; - -/** - * Users can override "DependenciesConfigT" that we have automatically generated - * during the CLI startup - */ -export type DependenciesUserConfigT = { - dependencies?: { - [key: string]: ?DependencyConfigT, + commands: string[], + haste: { + platforms: Array, + providesModuleNodeModules: Array, }, - platforms?: PlatformsT, - commands?: CommandsT, - haste?: MetroConfigT, }; - -export type ConfigT = ProjectConfigT; diff --git a/packages/cli/src/tools/types.flow.js b/packages/cli/src/tools/types.flow.js index bd7ee7973..9dddb4174 100644 --- a/packages/cli/src/tools/types.flow.js +++ b/packages/cli/src/tools/types.flow.js @@ -2,9 +2,9 @@ * @flow */ -import {type ConfigT} from './config/types.flow'; +import {type ProjectConfigT} from './config/types.flow'; -export type ContextT = ConfigT; +export type ContextT = ProjectConfigT; export type LocalCommandT = { name: string, From d71658b0a6169d3a1062cc60ff5e167766b846bb Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Thu, 28 Mar 2019 17:21:58 +0100 Subject: [PATCH 36/47] Print deprecation warning --- packages/cli/src/tools/config/readConfigFromDisk.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 9bcedf490..998fe5069 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -12,6 +12,7 @@ import {type DependencyConfigT, type ProjectConfigT} from './types.flow'; import resolveReactNativePath from './resolveReactNativePath'; import * as schema from './schema'; +import logger from '../logger'; /** * Places to look for the new configuration @@ -70,9 +71,9 @@ export function readDependencyConfigFromDisk( export function readLegacyDependencyConfigFromDisk( rootFolder: string, ): ?DependencyConfigT { - const config = require(path.join(rootFolder, 'package.json')).rnpm; + const config = require(path.join(rootFolder, 'package.json')); - if (!config) { + if (!config.rnpm) { return undefined; } @@ -92,6 +93,12 @@ export function readLegacyDependencyConfigFromDisk( : undefined, }; + logger.warn( + `Package '${path.basename( + config.name, + )}' is using deprecated "rnpm" config that will stop working from next release. Consider upgrading to the new config format.`, + ); + const result = Joi.validate(transformedConfig, schema.dependencyConfig); if (result.error) { From 5c13c1a22342865a3a3f0f7cbf75e996c7b0628a Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Thu, 28 Mar 2019 17:25:36 +0100 Subject: [PATCH 37/47] Fix flow --- packages/cli/src/commands/index.js | 6 +++--- packages/cli/src/tools/types.flow.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/index.js b/packages/cli/src/commands/index.js index 4cdc17b72..2bb71a051 100644 --- a/packages/cli/src/commands/index.js +++ b/packages/cli/src/commands/index.js @@ -12,7 +12,7 @@ import type { LocalCommandT, } from '../tools/types.flow'; -import {type ConfigT} from '../tools/config/types.flow'; +import {type ContextT} from '../tools/types.flow'; import server from './server/server'; import runIOS from './runIOS/runIOS'; @@ -63,7 +63,7 @@ const loadLocalCommands: Array = [ const loadProjectCommands = ({ root, commands, -}: ConfigT): Array => { +}: ContextT): Array => { return commands.reduce((acc: Array, cmdPath: string) => { /** * `pathToCommand` is a path to a file where commands are defined, relative to `node_modules` @@ -103,7 +103,7 @@ const loadProjectCommands = ({ /** * Loads all the commands inside a given `root` folder */ -export function getCommands(ctx: ConfigT): Array { +export function getCommands(ctx: ContextT): Array { return [ ...loadLocalCommands, { diff --git a/packages/cli/src/tools/types.flow.js b/packages/cli/src/tools/types.flow.js index 9dddb4174..32682a3e2 100644 --- a/packages/cli/src/tools/types.flow.js +++ b/packages/cli/src/tools/types.flow.js @@ -2,9 +2,9 @@ * @flow */ -import {type ProjectConfigT} from './config/types.flow'; +import {type ProjectConfigT as ConfigT} from './config/types.flow'; -export type ContextT = ProjectConfigT; +export type ContextT = ConfigT; export type LocalCommandT = { name: string, From a575d6ceacfbedcfa77e3061c4b8af87858c05d9 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Thu, 28 Mar 2019 20:00:54 +0100 Subject: [PATCH 38/47] Add basic error handling of Joi errors --- packages/cli/src/cliEntry.js | 2 +- .../src/tools/config/readConfigFromDisk.js | 7 +-- packages/cli/src/tools/errors.js | 43 +++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index c2b9e1fa8..1b69ccee7 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -35,7 +35,7 @@ commander.on('command:*', () => { const defaultOptParser = val => val; const handleError = err => { - logger.error(err.stack); + logger.error(err.message); process.exit(1); }; diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 998fe5069..400d14de9 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -10,6 +10,7 @@ import path from 'path'; import {type DependencyConfigT, type ProjectConfigT} from './types.flow'; import resolveReactNativePath from './resolveReactNativePath'; +import {JoiError} from '../errors'; import * as schema from './schema'; import logger from '../logger'; @@ -31,7 +32,7 @@ export function readProjectConfigFromDisk(): ProjectConfigT { const result = Joi.validate(config, schema.projectConfig); if (result.error) { - throw result.error; + throw new JoiError(result.error); } return { @@ -59,7 +60,7 @@ export function readDependencyConfigFromDisk( const result = Joi.validate(config, schema.dependencyConfig); if (result.error) { - throw result.error; + throw new JoiError(result.error); } return result.value; @@ -102,7 +103,7 @@ export function readLegacyDependencyConfigFromDisk( const result = Joi.validate(transformedConfig, schema.dependencyConfig); if (result.error) { - throw result.error; + throw new JoiError(result.error); } return result.value; diff --git a/packages/cli/src/tools/errors.js b/packages/cli/src/tools/errors.js index 278d35cc2..82bd9090c 100644 --- a/packages/cli/src/tools/errors.js +++ b/packages/cli/src/tools/errors.js @@ -2,6 +2,7 @@ * @flow */ import chalk from 'chalk'; +import dedent from 'dedent'; export class ProcessError extends Error { constructor(msg: string, processError: string) { @@ -9,3 +10,45 @@ export class ProcessError extends Error { Error.captureStackTrace(this, ProcessError); } } + +type JoiErrorT = { + details: Array<{ + message: string, + path: string[], + type: string, + context: { + key: string, + label: string, + value: Object, + }, + }>, +}; + +export class JoiError extends Error { + constructor(joiError: JoiErrorT) { + super( + joiError.details + .map(error => { + const name = error.path.join('.'); + const value = JSON.stringify(error.context.value); + switch (error.type) { + case 'object.allowUnknown': + return dedent` + Unknown option ${name} with value "${value}" was found. + This is either a typing error or a user mistake. Fixing it will remove this message. + `; + case 'object.base': + case 'string.base': + const expectedType = error.type.replace('.base', ''); + const actualType = typeof error.context.value; + return dedent` + Option ${name} must be a ${expectedType}, instead got ${actualType} + `; + default: + return error.message; + } + }) + .join(), + ); + } +} From 7b67f2e41e8099231be10f046f33def715be10ec Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Fri, 29 Mar 2019 12:49:53 +0100 Subject: [PATCH 39/47] wip --- .../cli/src/tools/config/__tests__/index.js | 27 +++++++++++++++++++ .../cli/src/tools/config/findDependencies.js | 4 +-- packages/cli/src/tools/config/index.js | 10 +++---- .../src/tools/config/readConfigFromDisk.js | 20 +++++--------- .../tools/config/resolveReactNativePath.js | 4 +-- 5 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 packages/cli/src/tools/config/__tests__/index.js diff --git a/packages/cli/src/tools/config/__tests__/index.js b/packages/cli/src/tools/config/__tests__/index.js new file mode 100644 index 000000000..67c059642 --- /dev/null +++ b/packages/cli/src/tools/config/__tests__/index.js @@ -0,0 +1,27 @@ +import path from 'path'; +import os from 'os'; + +import loadConfig from '../'; + +const root = path.resolve(os.tmpdir(), 'resolve_config_path_test'); + +beforeEach(() => cleanup(root)); +afterEach(() => cleanup(root)); + +describe('config', () => { + it('should have a valid structure by default', () => { + fs.__setMockFilesystem({ + root: { + 'react-native.config.js': JSON.stringify({ + reactNativePath: '.', + }), + 'package.json': JSON.stringify({ + dependencies: {}, + devDependencies: {}, + }), + }, + }); + const config = loadConfig(root); + console.log(config); + }); +}); diff --git a/packages/cli/src/tools/config/findDependencies.js b/packages/cli/src/tools/config/findDependencies.js index 573edc193..ce3420f59 100644 --- a/packages/cli/src/tools/config/findDependencies.js +++ b/packages/cli/src/tools/config/findDependencies.js @@ -17,11 +17,11 @@ const pluginRe = new RegExp( * Returns an array of dependencies from project's package.json that * are likely to be React Native packages (see regular expression above) */ -export default function findDependencies(): Array { +export default function findDependencies(root: string): Array { let pjson; try { - pjson = require(path.join(process.cwd(), 'package.json')); + pjson = require(path.join(root, 'package.json')); } catch (e) { return []; } diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index 9f25455ff..39e5d1a30 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -24,10 +24,8 @@ import resolveReactNativePath from './resolveReactNativePath'; /** * Loads CLI configuration */ -function loadConfig(): ProjectConfigT { - const projectRoot = process.cwd(); - - const inferredProjectConfig = findDependencies().reduce( +function loadConfig(projectRoot: string = process.cwd()): ProjectConfigT { + const inferredProjectConfig = findDependencies(projectRoot).reduce( (acc: ProjectConfigT, dependencyName) => { const root = path.join(projectRoot, 'node_modules', dependencyName); @@ -75,7 +73,7 @@ function loadConfig(): ProjectConfigT { }, ({ root: projectRoot, - reactNativePath: resolveReactNativePath(), + reactNativePath: resolveReactNativePath(projectRoot), dependencies: {}, commands: [], platforms: { @@ -91,7 +89,7 @@ function loadConfig(): ProjectConfigT { const config: ProjectConfigT = merge( inferredProjectConfig, - readProjectConfigFromDisk(), + readProjectConfigFromDisk(projectRoot), ); if (config.reactNativePath === 'not-found') { diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index 400d14de9..e4d0b4e99 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -4,12 +4,11 @@ * Loads and validates a project configuration */ import Joi from 'joi'; -import comsmiconfig from 'cosmiconfig'; +import cosmiconfig from 'cosmiconfig'; import path from 'path'; import {type DependencyConfigT, type ProjectConfigT} from './types.flow'; -import resolveReactNativePath from './resolveReactNativePath'; import {JoiError} from '../errors'; import * as schema from './schema'; @@ -24,10 +23,10 @@ const searchPlaces = ['react-native.config.js', 'package.json']; * Reads a project configuration as defined by the user in the current * workspace. */ -export function readProjectConfigFromDisk(): ProjectConfigT { - const explorer = comsmiconfig('react-native', {searchPlaces}); - - const {config} = explorer.searchSync() || {config: {}}; +export function readProjectConfigFromDisk(rootFolder: string): ProjectConfigT { + const explorer = cosmiconfig('react-native', {searchPlaces}); + console.log(searchPlaces, rootFolder); + const {config} = explorer.searchSync(rootFolder) || {config: undefined}; const result = Joi.validate(config, schema.projectConfig); @@ -35,12 +34,7 @@ export function readProjectConfigFromDisk(): ProjectConfigT { throw new JoiError(result.error); } - return { - ...result.value, - reactNativePath: config.reactNativePath - ? config.reactNativePath - : resolveReactNativePath(), - }; + return result.value; } /** @@ -50,7 +44,7 @@ export function readProjectConfigFromDisk(): ProjectConfigT { export function readDependencyConfigFromDisk( rootFolder: string, ): DependencyConfigT { - const explorer = comsmiconfig('react-native', { + const explorer = cosmiconfig('react-native', { stopDir: rootFolder, searchPlaces, }); diff --git a/packages/cli/src/tools/config/resolveReactNativePath.js b/packages/cli/src/tools/config/resolveReactNativePath.js index 203db7661..b2d367aa2 100644 --- a/packages/cli/src/tools/config/resolveReactNativePath.js +++ b/packages/cli/src/tools/config/resolveReactNativePath.js @@ -7,12 +7,12 @@ import path from 'path'; * Finds path to React Native inside `node_modules` or throws * an error otherwise. */ -export default function resolveReactNativePath() { +export default function resolveReactNativePath(root: string) { try { return path.dirname( // $FlowIssue: Wrong `require.resolve` type definition require.resolve('react-native/package.json', { - paths: [process.cwd()], + paths: [root], }), ); } catch (_ignored) { From 600539079bb7363f50ab5e9eea641087d5b5a0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 28 Mar 2019 14:06:30 +0100 Subject: [PATCH 40/47] chore: setup e2e tests (#264) * chore: setup e2e tests * add flowfixmes * use test.each * add docs to install/uninstall * remove dead code --- .flowconfig | 3 - e2e/__tests__/install.test.js | 34 ++ e2e/__tests__/uninstall.test.js | 55 +++ e2e/helpers.js | 149 ++++++ e2e/jest.config.js | 4 + flow-typed/npm/execa_v1.0.x.js | 103 +++++ package.json | 12 +- packages/cli/README.md | 52 +++ packages/cli/src/commands/upgrade/upgrade.js | 2 + yarn.lock | 454 ++++++++++++++++++- 10 files changed, 848 insertions(+), 20 deletions(-) create mode 100644 e2e/__tests__/install.test.js create mode 100644 e2e/__tests__/uninstall.test.js create mode 100644 e2e/helpers.js create mode 100644 e2e/jest.config.js create mode 100644 flow-typed/npm/execa_v1.0.x.js diff --git a/.flowconfig b/.flowconfig index c825f0b86..be12bc31a 100644 --- a/.flowconfig +++ b/.flowconfig @@ -63,6 +63,3 @@ unclear-type unsafe-getters-setters untyped-import untyped-type-import - -[version] -^0.94.0 diff --git a/e2e/__tests__/install.test.js b/e2e/__tests__/install.test.js new file mode 100644 index 000000000..49de1eac4 --- /dev/null +++ b/e2e/__tests__/install.test.js @@ -0,0 +1,34 @@ +// @flow +import path from 'path'; +import {run, getTempDirectory, cleanup, writeFiles} from '../helpers'; + +const DIR = getTempDirectory('command-install-test'); +const pkg = 'react-native-config'; + +beforeEach(() => { + cleanup(DIR); + writeFiles(DIR, { + 'node_modules/react-native/package.json': '{}', + 'package.json': '{}', + }); +}); +afterEach(() => cleanup(DIR)); + +test.each(['yarn', 'npm'])('install module with %s', pm => { + if (pm === 'yarn') { + writeFiles(DIR, {'yarn.lock': ''}); + } + const {stdout, code} = run(DIR, ['install', pkg]); + + expect(stdout).toContain(`Installing "${pkg}"`); + expect(stdout).toContain(`Linking "${pkg}"`); + // TODO – this behavior is a bug, linking should fail/warn without native deps + // to link. Not a high priority since we're changing how link works + expect(stdout).toContain(`Successfully installed and linked "${pkg}"`); + expect(require(path.join(DIR, 'package.json'))).toMatchObject({ + dependencies: { + [pkg]: expect.any(String), + }, + }); + expect(code).toBe(0); +}); diff --git a/e2e/__tests__/uninstall.test.js b/e2e/__tests__/uninstall.test.js new file mode 100644 index 000000000..1c9f6df05 --- /dev/null +++ b/e2e/__tests__/uninstall.test.js @@ -0,0 +1,55 @@ +// @flow +import {run, getTempDirectory, cleanup, writeFiles} from '../helpers'; + +const DIR = getTempDirectory('command-uninstall-test'); +const pkg = 'react-native-config'; + +beforeEach(() => { + cleanup(DIR); + writeFiles(DIR, { + 'node_modules/react-native/package.json': '{}', + 'node_modules/react-native-config/package.json': '{}', + 'package.json': `{ + "dependencies": { + "react-native-config": "*" + } + }`, + }); +}); +afterEach(() => cleanup(DIR)); + +test('uninstall fails when package is not defined', () => { + writeFiles(DIR, { + 'package.json': `{ + "dependencies": {} + }`, + }); + const {stderr, code} = run(DIR, ['uninstall']); + + expect(stderr).toContain('missing required argument'); + expect(code).toBe(1); +}); + +test('uninstall fails when package is not installed', () => { + writeFiles(DIR, { + 'package.json': `{ + "dependencies": {} + }`, + }); + const {stderr, code} = run(DIR, ['uninstall', pkg]); + + expect(stderr).toContain(`Project "${pkg}" is not a react-native library`); + expect(code).toBe(1); +}); + +test.each(['yarn', 'npm'])('uninstall module with %s', pm => { + if (pm === 'yarn') { + writeFiles(DIR, {'yarn.lock': ''}); + } + const {stdout, code} = run(DIR, ['uninstall', pkg]); + + expect(stdout).toContain(`Unlinking "${pkg}"`); + expect(stdout).toContain(`Uninstalling "${pkg}"`); + expect(stdout).toContain(`Successfully uninstalled and unlinked "${pkg}"`); + expect(code).toBe(0); +}); diff --git a/e2e/helpers.js b/e2e/helpers.js new file mode 100644 index 000000000..da4dca334 --- /dev/null +++ b/e2e/helpers.js @@ -0,0 +1,149 @@ +// @flow +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import {createDirectory} from 'jest-util'; +import rimraf from 'rimraf'; +import execa from 'execa'; +import {Writable} from 'readable-stream'; + +const CLI_PATH = path.resolve(__dirname, '../packages/cli/build/bin.js'); + +type RunOptions = { + nodeOptions?: string, + nodePath?: string, + timeout?: number, // kill the process after X milliseconds +}; + +export function run( + dir: string, + args?: Array, + options: RunOptions = {}, +) { + return spawnCli(dir, args, options); +} + +// Runs cli until a given output is achieved, then kills it with `SIGTERM` +export async function runUntil( + dir: string, + args: Array | void, + text: string, + options: RunOptions = {}, +) { + const spawnPromise = spawnCliAsync(dir, args, {timeout: 30000, ...options}); + + spawnPromise.stderr.pipe( + new Writable({ + write(chunk, _encoding, callback) { + const output = chunk.toString('utf8'); + + if (output.includes(text)) { + spawnPromise.kill(); + } + + callback(); + }, + }), + ); + + return spawnPromise; +} + +export const makeTemplate = ( + str: string, +): ((values?: Array) => string) => (values?: Array) => + str.replace(/\$(\d+)/g, (_match, number) => { + if (!Array.isArray(values)) { + throw new Error('Array of values must be passed to the template.'); + } + return values[number - 1]; + }); + +export const cleanup = (directory: string) => rimraf.sync(directory); + +/** + * Creates a nested directory with files and their contents + * writeFiles( + * '/home/tmp', + * { + * 'package.json': '{}', + * 'dir/file.js': 'module.exports = "x";', + * } + * ); + */ +export const writeFiles = ( + directory: string, + files: {[filename: string]: string}, +) => { + createDirectory(directory); + Object.keys(files).forEach(fileOrPath => { + const dirname = path.dirname(fileOrPath); + + if (dirname !== '/') { + createDirectory(path.join(directory, dirname)); + } + fs.writeFileSync( + path.resolve(directory, ...fileOrPath.split('/')), + files[fileOrPath], + ); + }); +}; + +export const copyDir = (src: string, dest: string) => { + const srcStat = fs.lstatSync(src); + if (srcStat.isDirectory()) { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest); + } + fs.readdirSync(src).map(filePath => + copyDir(path.join(src, filePath), path.join(dest, filePath)), + ); + } else { + fs.writeFileSync(dest, fs.readFileSync(src)); + } +}; + +export const getTempDirectory = (name: string) => + path.resolve(os.tmpdir(), name); + +function spawnCli(dir: string, args?: Array, options: RunOptions = {}) { + const {spawnArgs, spawnOptions} = getCliArguments({dir, args, options}); + + return execa.sync(process.execPath, spawnArgs, spawnOptions); +} + +function spawnCliAsync( + dir: string, + args?: Array, + options: RunOptions = {}, +) { + const {spawnArgs, spawnOptions} = getCliArguments({dir, args, options}); + + return execa(process.execPath, spawnArgs, spawnOptions); +} + +function getCliArguments({dir, args, options}) { + const isRelative = !path.isAbsolute(dir); + + if (isRelative) { + dir = path.resolve(__dirname, dir); + } + + const env = Object.assign({}, process.env, {FORCE_COLOR: '0'}); + + if (options.nodeOptions) { + env.NODE_OPTIONS = options.nodeOptions; + } + if (options.nodePath) { + env.NODE_PATH = options.nodePath; + } + + const spawnArgs = [CLI_PATH, ...(args || [])]; + const spawnOptions = { + cwd: dir, + env, + reject: false, + timeout: options.timeout || 0, + }; + return {spawnArgs, spawnOptions}; +} diff --git a/e2e/jest.config.js b/e2e/jest.config.js new file mode 100644 index 000000000..dcda2ee7b --- /dev/null +++ b/e2e/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + testEnvironment: 'node', + testPathIgnorePatterns: ['/(?:.+?)/__tests__/'], +}; diff --git a/flow-typed/npm/execa_v1.0.x.js b/flow-typed/npm/execa_v1.0.x.js new file mode 100644 index 000000000..d49b7a7b0 --- /dev/null +++ b/flow-typed/npm/execa_v1.0.x.js @@ -0,0 +1,103 @@ +// flow-typed signature: 613ee1ec7d728b6a312fcff21a7b2669 +// flow-typed version: 3163f7a6e3/execa_v1.0.x/flow_>=v0.75.x + +declare module 'execa' { + + declare type StdIoOption = + | 'pipe' + | 'ipc' + | 'ignore' + | 'inherit' + | stream$Stream + | number; + + declare type CommonOptions = {| + argv0?: string, + cleanup?: boolean, + cwd?: string, + detached?: boolean, + encoding?: string, + env?: {[string]: string}, + extendEnv?: boolean, + gid?: number, + killSignal?: string | number, + localDir?: string, + maxBuffer?: number, + preferLocal?: boolean, + reject?: boolean, + shell?: boolean | string, + stderr?: ?StdIoOption, + stdin?: ?StdIoOption, + stdio?: 'pipe' | 'ignore' | 'inherit' | $ReadOnlyArray, + stdout?: ?StdIoOption, + stripEof?: boolean, + timeout?: number, + uid?: number, + windowsVerbatimArguments?: boolean, + |}; + + declare type SyncOptions = {| + ...CommonOptions, + input?: string | Buffer, + |}; + + declare type Options = {| + ...CommonOptions, + input?: string | Buffer | stream$Readable, + |}; + + declare type SyncResult = {| + stdout: string, + stderr: string, + code: number, + failed: boolean, + signal: ?string, + cmd: string, + timedOut: boolean, + |}; + + declare type Result = {| + ...SyncResult, + killed: boolean, + |}; + + declare interface ThenableChildProcess extends child_process$ChildProcess { + then( + onfulfilled?: ?((value: Result) => R | Promise), + onrejected?: ?((reason: ExecaError) => E | Promise), + ): Promise; + + catch( + onrejected?: ?((reason: ExecaError) => E | Promise) + ): Promise; + } + + declare interface ExecaError extends ErrnoError { + stdout: string; + stderr: string; + failed: boolean; + signal: ?string; + cmd: string; + timedOut: boolean; + } + + declare interface Execa { + (file: string, args?: $ReadOnlyArray, options?: $ReadOnly): ThenableChildProcess; + (file: string, options?: $ReadOnly): ThenableChildProcess; + + stdout(file: string, args?: $ReadOnlyArray, options?: $ReadOnly): Promise; + stdout(file: string, options?: $ReadOnly): Promise; + + stderr(file: string, args?: $ReadOnlyArray, options?: $ReadOnly): Promise; + stderr(file: string, options?: $ReadOnly): Promise; + + shell(command: string, options?: $ReadOnly): ThenableChildProcess; + + sync(file: string, args?: $ReadOnlyArray, options?: $ReadOnly): SyncResult; + sync(file: string, options?: $ReadOnly): SyncResult; + + shellSync(command: string, options?: $ReadOnly): SyncResult; + } + + declare module.exports: Execa; +} diff --git a/package.json b/package.json index de4b3d899..f1710c7a4 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,9 @@ "babel-jest": "^24.0.0", "chalk": "^2.4.2", "eslint": "^5.10.0", - "flow-bin": "^0.94.0", + "execa": "^1.0.0", + "flow-bin": "^0.95.1", + "flow-typed": "^2.5.1", "glob": "^7.1.3", "jest": "^24.0.0", "lerna": "^3.10.6", @@ -39,12 +41,16 @@ "node": true }, "rules": { - "prettier/prettier": [2, "fb"] + "prettier/prettier": [ + 2, + "fb" + ] } }, "jest": { "projects": [ - "packages/*" + "packages/*", + "e2e" ] } } diff --git a/packages/cli/README.md b/packages/cli/README.md index 2639cde0c..f4158203a 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -25,8 +25,44 @@ CLI comes with a set of commands and flags you can pass to them. _Note: This document is still under development and doesn't represent the full API area._ +### `bundle` + +### `dependencies` + +### `eject` + +### `info` + +### `install` + +Usage: + +```sh +react-native install +``` + +Installs single package from npm and then links native dependencies. If `install` detects `yarn.lock` in your project, it will use Yarn as package manager. Otherwise `npm` will be used. + +### `library` + +### `link` + +### `log-android` + +### `log-ios` + +### `ram-bundle` + +### `run-android` + ### `run-ios` +Usage: + +```sh +react-native run-ios [options] +``` + Builds your app and starts it on iOS simulator. #### Options @@ -78,3 +114,19 @@ Do not use `xcpretty` even if installed. Runs packager on specified port Default: `process.env.RCT_METRO_PORT || 8081` + +### `server` + +### `uninstall` + +Usage: + +```sh +react-native uninstall +``` + +Unlinks single package native dependencies and then uninstalls it from `package.json`. If `uninstall` detects `yarn.lock` in your project, it will use Yarn as package manager. Otherwise `npm` will be used. + +### `unlink` + +### `upgrade` diff --git a/packages/cli/src/commands/upgrade/upgrade.js b/packages/cli/src/commands/upgrade/upgrade.js index aead25ac8..8d2e69b8c 100644 --- a/packages/cli/src/commands/upgrade/upgrade.js +++ b/packages/cli/src/commands/upgrade/upgrade.js @@ -19,6 +19,7 @@ const rnDiffPurgeUrl = const getLatestRNVersion = async (): Promise => { logger.info('No version passed. Fetching latest...'); + // $FlowFixMe - this is public API const {stdout} = await execa('npm', ['info', 'react-native', 'version']); return stdout; }; @@ -26,6 +27,7 @@ const getLatestRNVersion = async (): Promise => { const getRNPeerDeps = async ( version: string, ): Promise<{[key: string]: string}> => { + // $FlowFixMe - this is public API const {stdout} = await execa('npm', [ 'info', `react-native@${version}`, diff --git a/yarn.lock b/yarn.lock index 5efce960b..a9f2ce1a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1623,6 +1623,21 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@octokit/rest@^15.2.6": + version "15.18.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-15.18.1.tgz#ec7fb0f8775ef64dc095fae6635411d3fbff9b62" + integrity sha512-g2tecjp2TEtYV8bKAFvfQtu+W29HM7ektmWmw8zrMy9/XCKDEYRErR2YvvhN9+IxkLC4O3lDqYP4b6WgsL6Utw== + dependencies: + before-after-hook "^1.1.0" + btoa-lite "^1.0.0" + debug "^3.1.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.0" + lodash "^4.17.4" + node-fetch "^2.1.1" + universal-user-agent "^2.0.0" + url-template "^2.0.8" + "@react-native-community/eslint-config@^0.0.2": version "0.0.2" resolved "https://registry.yarnpkg.com/@react-native-community/eslint-config/-/eslint-config-0.0.2.tgz#042224762e00ef06b45693a14be78ebda5fb7def" @@ -1702,6 +1717,11 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +ajv-keywords@^3.0.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" + integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== + ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -1711,6 +1731,16 @@ ajv@^5.3.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.0.1: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.5.3: version "6.5.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1" @@ -1941,6 +1971,15 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + babel-preset-fbjs@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.2.0.tgz#c0e6347d3e0379ed84b3c2434d3467567aa05297" @@ -1982,6 +2021,14 @@ babel-preset-jest@^24.0.0: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" babel-plugin-jest-hoist "^24.0.0" +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -2014,6 +2061,16 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +before-after-hook@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.4.0.tgz#2b6bf23dca4f32e628fd2747c10a37c74a4b484d" + integrity sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg== + +big-integer@^1.6.17: + version "1.6.43" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.43.tgz#8ac15bf13e93e509500859061233e19d8d0d99d1" + integrity sha512-9dULc9jsKmXl0Aeunug8wbF+58n+hQoFjqClN7WeZwGLh0XJUWyJJ9Ee+Ep+Ql/J9fRsTVaeThp8MhiCCrY0Jg== + big-integer@^1.6.7: version "1.6.36" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" @@ -2029,6 +2086,14 @@ bin-links@^1.1.2: graceful-fs "^4.1.11" write-file-atomic "^2.3.0" +binary@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= + dependencies: + buffers "~0.1.1" + chainsaw "~0.1.0" + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -2041,6 +2106,11 @@ bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== +bluebird@~3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= + bplist-creator@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.7.tgz#37df1536092824b87c42f957b01344117372ae45" @@ -2100,6 +2170,11 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= + buffer-crc32@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -2108,6 +2183,21 @@ buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" +buffer-indexof-polyfill@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz#a9fb806ce8145d5428510ce72f278bb363a638bf" + integrity sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8= + +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + integrity sha1-mXjOMXOIxkmth5MCjDR37wRKi1E= + +buffers@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -2225,6 +2315,11 @@ camelcase@^2.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -2249,6 +2344,13 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +chainsaw@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= + dependencies: + traverse ">=0.3.0 <0.4" + chalk@^1.1.1: version "1.1.3" resolved "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2284,6 +2386,11 @@ chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" @@ -2379,6 +2486,11 @@ colors@0.6.x: version "0.6.2" resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" +colors@^1.1.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" + integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== + columnify@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -2587,7 +2699,7 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" -core-js@^2.2.2, core-js@^2.5.7: +core-js@^2.2.2, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: version "2.6.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== @@ -2636,6 +2748,11 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797" @@ -2747,6 +2864,13 @@ decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" +decompress-response@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -2900,6 +3024,18 @@ dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -3456,10 +3592,31 @@ flat-cache@^1.2.1: graceful-fs "^4.1.2" write "^0.2.1" -flow-bin@^0.94.0: - version "0.94.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.94.0.tgz#b5d58fe7559705b73a18229f97edfc3ab6ffffcb" - integrity sha512-DYF7r9CJ/AksfmmB4+q+TyLMoeQPRnqtF1Pk7KY3zgfkB/nVuA3nXyzqgsIPIvnMSiFEXQcFK4z+iPxSLckZhQ== +flow-bin@^0.95.1: + version "0.95.1" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.95.1.tgz#633113831ccff4b7ee70a2730f63fc43b69ba85f" + integrity sha512-06IOC/pqPMNRYtC6AMZEWYR9Fi6UdBC7gImGinPuNUpPZFnP5E9/0cBCl3DWrH4zz/gSM2HdDilU7vPGpYIr2w== + +flow-typed@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/flow-typed/-/flow-typed-2.5.1.tgz#0ff565cc94d2af8c557744ba364b6f14726a6b9f" + integrity sha1-D/VlzJTSr4xVd0S6NktvFHJqa58= + dependencies: + "@octokit/rest" "^15.2.6" + babel-polyfill "^6.26.0" + colors "^1.1.2" + fs-extra "^5.0.0" + glob "^7.1.2" + got "^7.1.0" + md5 "^2.1.0" + mkdirp "^0.5.1" + rimraf "^2.6.2" + semver "^5.5.0" + table "^4.0.2" + through "^2.3.8" + unzipper "^0.8.11" + which "^1.3.0" + yargs "^4.2.0" flush-write-stream@^1.0.0: version "1.0.3" @@ -3511,6 +3668,15 @@ fs-extra@^1.0.0: jsonfile "^2.1.0" klaw "^1.0.0" +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -3556,7 +3722,7 @@ fsevents@^1.2.3: nan "^2.9.2" node-pre-gyp "^0.10.0" -fstream@^1.0.0, fstream@^1.0.2: +fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= @@ -3744,6 +3910,26 @@ globby@^8.0.1: pify "^3.0.0" slash "^1.0.0" +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + 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" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" @@ -3783,10 +3969,22 @@ has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + has-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" +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" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -3869,7 +4067,7 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-proxy-agent@^2.2.1: +https-proxy-agent@^2.2.0, https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== @@ -3969,7 +4167,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -4082,7 +4280,7 @@ is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -4208,6 +4406,11 @@ is-obj@^1.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -4245,7 +4448,12 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" -is-stream@^1.0.1, is-stream@^1.1.0: +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4397,6 +4605,14 @@ istanbul-reports@^2.0.3: dependencies: handlebars "^4.0.11" +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + jest-changed-files@^24.0.0: version "24.0.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.0.0.tgz#c02c09a8cc9ca93f513166bc773741bd39898ff7" @@ -5047,6 +5263,11 @@ libnpmteam@^1.0.1: get-stream "^4.0.0" npm-registry-fetch "^3.8.0" +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" + integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -5104,6 +5325,11 @@ lodash._reinterpolate@~3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.assign@^4.0.3, lodash.assign@^4.0.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -5132,7 +5358,7 @@ lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" -lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1: +lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -5150,6 +5376,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + lru-cache@^4.0.1: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" @@ -5172,6 +5403,11 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +macos-release@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.1.0.tgz#c87935891fbeb0dba7537913fc66f469fee9d662" + integrity sha512-8TCbwvN1mfNxbBv0yBtfyIFMo3m1QsNbKHv7PYIp/abRBKVQBXN7ecu3aeGGgT18VC/Tf397LBDGZF9KBGJFFw== + make-dir@^1.0.0, make-dir@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -5227,6 +5463,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +md5@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" @@ -5548,6 +5793,11 @@ mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -5732,6 +5982,11 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" +node-fetch@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" + integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== + node-fetch@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.2.1.tgz#1fe551e0ded6c45b3b3b937d0fb46f76df718d1e" @@ -6020,6 +6275,13 @@ os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + os-locale@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" @@ -6036,6 +6298,14 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" +os-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.0.0.tgz#e1434dbfddb8e74b44c98b56797d951b7648a5d9" + integrity sha512-7c74tib2FsdFbQ3W+qj8Tyd1R3Z6tuVRNNxXjJcZ4NgjIEQU9N/prVMqcW29XZPXGACqaXN3jq58/6hoaoXH6g== + dependencies: + macos-release "^2.0.0" + windows-release "^3.1.0" + os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -6047,6 +6317,11 @@ osenv@0, osenv@^0.1.4, osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -6112,6 +6387,13 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -6324,6 +6606,11 @@ prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + prettier@1.13.6: version "1.13.6" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.6.tgz#00ae0b777ad92f81a9e7a1df2f0470b6dab0cb44" @@ -6349,6 +6636,11 @@ private@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" +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" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= + process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" @@ -6609,7 +6901,7 @@ read@1, read@1.0.x, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.6" resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -6621,6 +6913,19 @@ read@1, read@1.0.x, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + integrity sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA= + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + readdir-scoped-modules@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" @@ -6663,6 +6968,16 @@ regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg= + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + regenerator-transform@^0.13.3: version "0.13.3" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb" @@ -6984,7 +7299,7 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.5: +setimmediate@^1.0.5, setimmediate@~1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -7292,6 +7607,11 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -7387,6 +7707,18 @@ symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" +table@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" + integrity sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg== + dependencies: + ajv "^6.0.1" + ajv-keywords "^3.0.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + table@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/table/-/table-5.1.0.tgz#69a54644f6f01ad1628f8178715b408dc6bf11f7" @@ -7484,10 +7816,15 @@ through2@^2.0.0, through2@^2.0.2: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "http://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -7537,6 +7874,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +"traverse@>=0.3.0 <0.4": + version "0.3.9" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -7654,6 +7996,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universal-user-agent@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.0.3.tgz#9f6f09f9cc33de867bb720d84c08069b14937c6c" + integrity sha512-eRHEHhChCBHrZsA4WEhdgiOKgdvgrMIHwnwnqD0r5C6AO8kwKcG7qSku3iXdhvHL3YvsS9ZkSGN8h/hIpoFC8g== + dependencies: + os-name "^3.0.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -7670,6 +8019,21 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +unzipper@^0.8.11: + version "0.8.14" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.8.14.tgz#ade0524cd2fc14d11b8de258be22f9d247d3f79b" + integrity sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "~1.0.10" + listenercount "~1.0.1" + readable-stream "~2.1.5" + setimmediate "~1.0.4" + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -7680,6 +8044,23 @@ urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-template@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE= + +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" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -7800,6 +8181,11 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -7816,6 +8202,18 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= + +windows-release@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.1.0.tgz#8d4a7e266cbf5a233f6c717dac19ce00af36e12e" + integrity sha512-hBb7m7acFgQPQc222uEQTmdcGLeBmQLNLFIh0rDk3CwFOBrfjefLzEfEfmpMq8Af/n/GnFf3eYf203FY1PmudA== + dependencies: + execa "^0.10.0" + winston@0.8.x: version "0.8.3" resolved "http://registry.npmjs.org/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0" @@ -7979,6 +8377,14 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" @@ -8020,6 +8426,26 @@ yargs@^12.0.2: y18n "^3.2.1 || ^4.0.0" yargs-parser "^10.1.0" +yargs@^4.2.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.1" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.1" + yargs@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c" From 5941d8fb853cd4dc8cc17f69187146ada96fe0ef Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Fri, 29 Mar 2019 12:55:04 +0100 Subject: [PATCH 41/47] Add first snapshot test using e2e utils --- .../__tests__/__snapshots__/index.js.snap | 25 +++++++++++++++++++ .../cli/src/tools/config/__tests__/index.js | 22 ++++++++-------- .../src/tools/config/readConfigFromDisk.js | 2 +- 3 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap diff --git a/packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap b/packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap new file mode 100644 index 000000000..319fd68cd --- /dev/null +++ b/packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`config should have a valid structure by default 1`] = ` +Object { + "commands": Array [], + "dependencies": Object {}, + "haste": Object { + "platforms": Array [], + "providesModuleNodeModules": Array [], + }, + "platforms": Object { + "android": Object { + "dependencyConfig": [Function], + "linkConfig": [Function], + "projectConfig": [Function], + }, + "ios": Object { + "dependencyConfig": [Function], + "linkConfig": [Function], + "projectConfig": [Function], + }, + }, + "reactNativePath": ".", +} +`; diff --git a/packages/cli/src/tools/config/__tests__/index.js b/packages/cli/src/tools/config/__tests__/index.js index 67c059642..0cec40c2f 100644 --- a/packages/cli/src/tools/config/__tests__/index.js +++ b/packages/cli/src/tools/config/__tests__/index.js @@ -3,6 +3,8 @@ import os from 'os'; import loadConfig from '../'; +import {cleanup, writeFiles} from '../../../../../../e2e/helpers'; + const root = path.resolve(os.tmpdir(), 'resolve_config_path_test'); beforeEach(() => cleanup(root)); @@ -10,18 +12,14 @@ afterEach(() => cleanup(root)); describe('config', () => { it('should have a valid structure by default', () => { - fs.__setMockFilesystem({ - root: { - 'react-native.config.js': JSON.stringify({ - reactNativePath: '.', - }), - 'package.json': JSON.stringify({ - dependencies: {}, - devDependencies: {}, - }), - }, + writeFiles(root, { + 'package.json': JSON.stringify({ + dependencies: {}, + devDependencies: {}, + 'react-native': {reactNativePath: '.'}, + }), }); - const config = loadConfig(root); - console.log(config); + const {root: _root, ...config} = loadConfig(root); + expect(config).toMatchSnapshot(); }); }); diff --git a/packages/cli/src/tools/config/readConfigFromDisk.js b/packages/cli/src/tools/config/readConfigFromDisk.js index e4d0b4e99..d613ba8a9 100644 --- a/packages/cli/src/tools/config/readConfigFromDisk.js +++ b/packages/cli/src/tools/config/readConfigFromDisk.js @@ -25,7 +25,7 @@ const searchPlaces = ['react-native.config.js', 'package.json']; */ export function readProjectConfigFromDisk(rootFolder: string): ProjectConfigT { const explorer = cosmiconfig('react-native', {searchPlaces}); - console.log(searchPlaces, rootFolder); + const {config} = explorer.searchSync(rootFolder) || {config: undefined}; const result = Joi.validate(config, schema.projectConfig); From 8d08a007b263e9cedffec0131da4c05b97ba8303 Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Fri, 29 Mar 2019 12:58:57 +0100 Subject: [PATCH 42/47] Test for root too --- packages/cli/src/tools/config/__tests__/index.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/tools/config/__tests__/index.js b/packages/cli/src/tools/config/__tests__/index.js index 0cec40c2f..6d12ade88 100644 --- a/packages/cli/src/tools/config/__tests__/index.js +++ b/packages/cli/src/tools/config/__tests__/index.js @@ -5,21 +5,22 @@ import loadConfig from '../'; import {cleanup, writeFiles} from '../../../../../../e2e/helpers'; -const root = path.resolve(os.tmpdir(), 'resolve_config_path_test'); +const DIR = path.resolve(os.tmpdir(), 'resolve_config_path_test'); -beforeEach(() => cleanup(root)); -afterEach(() => cleanup(root)); +beforeEach(() => cleanup(DIR)); +afterEach(() => cleanup(DIR)); describe('config', () => { it('should have a valid structure by default', () => { - writeFiles(root, { + writeFiles(DIR, { 'package.json': JSON.stringify({ dependencies: {}, devDependencies: {}, 'react-native': {reactNativePath: '.'}, }), }); - const {root: _root, ...config} = loadConfig(root); + const {root, ...config} = loadConfig(DIR); + expect(root).toBe(DIR); expect(config).toMatchSnapshot(); }); }); From 5ad00b367f4051bd0a48308f3b055b8ab274351b Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 18 Apr 2019 09:43:46 +0100 Subject: [PATCH 43/47] move to platform-android --- packages/{cli => platform-android}/native_modules.gradle | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/{cli => platform-android}/native_modules.gradle (100%) diff --git a/packages/cli/native_modules.gradle b/packages/platform-android/native_modules.gradle similarity index 100% rename from packages/cli/native_modules.gradle rename to packages/platform-android/native_modules.gradle From a7fb13be307b031a4b5dc50ca83c820c1820bfaf Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 18 Apr 2019 09:49:51 +0100 Subject: [PATCH 44/47] cleanup --- .../__tests__/__snapshots__/index.js.snap | 25 ------ .../cli/src/tools/config/__tests__/index.js | 26 ------ packages/cli/src/tools/config/dependency.js | 84 ------------------- packages/cli/src/tools/config/types.flow.js | 74 ---------------- 4 files changed, 209 deletions(-) delete mode 100644 packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap delete mode 100644 packages/cli/src/tools/config/__tests__/index.js delete mode 100644 packages/cli/src/tools/config/dependency.js delete mode 100644 packages/cli/src/tools/config/types.flow.js diff --git a/packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap b/packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap deleted file mode 100644 index 319fd68cd..000000000 --- a/packages/cli/src/tools/config/__tests__/__snapshots__/index.js.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`config should have a valid structure by default 1`] = ` -Object { - "commands": Array [], - "dependencies": Object {}, - "haste": Object { - "platforms": Array [], - "providesModuleNodeModules": Array [], - }, - "platforms": Object { - "android": Object { - "dependencyConfig": [Function], - "linkConfig": [Function], - "projectConfig": [Function], - }, - "ios": Object { - "dependencyConfig": [Function], - "linkConfig": [Function], - "projectConfig": [Function], - }, - }, - "reactNativePath": ".", -} -`; diff --git a/packages/cli/src/tools/config/__tests__/index.js b/packages/cli/src/tools/config/__tests__/index.js deleted file mode 100644 index 6d12ade88..000000000 --- a/packages/cli/src/tools/config/__tests__/index.js +++ /dev/null @@ -1,26 +0,0 @@ -import path from 'path'; -import os from 'os'; - -import loadConfig from '../'; - -import {cleanup, writeFiles} from '../../../../../../e2e/helpers'; - -const DIR = path.resolve(os.tmpdir(), 'resolve_config_path_test'); - -beforeEach(() => cleanup(DIR)); -afterEach(() => cleanup(DIR)); - -describe('config', () => { - it('should have a valid structure by default', () => { - writeFiles(DIR, { - 'package.json': JSON.stringify({ - dependencies: {}, - devDependencies: {}, - 'react-native': {reactNativePath: '.'}, - }), - }); - const {root, ...config} = loadConfig(DIR); - expect(root).toBe(DIR); - expect(config).toMatchSnapshot(); - }); -}); diff --git a/packages/cli/src/tools/config/dependency.js b/packages/cli/src/tools/config/dependency.js deleted file mode 100644 index e8c5b0c3d..000000000 --- a/packages/cli/src/tools/config/dependency.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @flow - */ -import path from 'path'; - -import findAndroidAppFolder from '../android/findAndroidAppFolder'; -import findAndroidManifest from '../android/findManifest'; -import findAndroidPackageClassName from '../android/findPackageClassName'; -import readAndroidManifest from '../android/readManifest'; - -import findIOSPodspecName from '../ios/findPodspecName'; - -import type { - InputDependencyConfigIOS, - DependencyConfigIOS, - InputDependencyConfigAndroid, - DependencyConfigAndroid, -} from './types.flow'; - -const getAndroidSourceDir = (folder: string) => { - const androidFolder = findAndroidAppFolder(folder); - if (!androidFolder) { - return null; - } - return path.join(folder, androidFolder); -}; - -export function android( - folder: string, - userConfig: InputDependencyConfigAndroid, -): ?DependencyConfigAndroid { - const packageInstance = userConfig.packageInstance - ? userConfig.packageInstance - : (() => { - const sourceDir = getAndroidSourceDir(folder); - if (!sourceDir) { - return null; - } - const packageClassName = findAndroidPackageClassName(sourceDir); - return `new ${packageClassName}()`; - })(); - - const packageImportPath = userConfig.packageImportPath - ? userConfig.packageImportPath - : (() => { - const sourceDir = getAndroidSourceDir(folder); - if (!sourceDir) { - return null; - } - const manifestPath = findAndroidManifest(sourceDir); - if (!manifestPath) { - return null; - } - const manifest = readAndroidManifest(manifestPath); - const packageClassName = findAndroidPackageClassName(sourceDir); - const packageName = manifest.attr.package; - return `import ${packageName}.${packageClassName};`; - })(); - - if (packageInstance === null || packageImportPath === null) { - return null; - } - - return { - packageImportPath, - packageInstance, - sourceDir: getAndroidSourceDir(folder), - }; -} - -export function ios( - folder: string, - userConfig: InputDependencyConfigIOS, -): ?DependencyConfigIOS { - const podspec = userConfig.podspec - ? userConfig.podspec - : findIOSPodspecName(folder); - - if (!podspec) { - return null; - } - - return {podspec}; -} diff --git a/packages/cli/src/tools/config/types.flow.js b/packages/cli/src/tools/config/types.flow.js deleted file mode 100644 index 0d3d4b092..000000000 --- a/packages/cli/src/tools/config/types.flow.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @flow - */ - -import type { - AndroidConfigParamsT, - IOSConfigParamsT, - InquirerPromptT, - DependencyConfigAndroidT, - DependencyConfigIOST, -} from '../types.flow'; - -/** - * A map of hooks to run pre/post some of the CLI actions - */ -type HooksT = { - [key: string]: string, - prelink?: string, - postlink?: string, -}; - -export type DependencyConfigAndroid = { - packageImportPath: string, - packageInstance: string, - sourceDir: string | null, -}; -/** - * A map with additional platforms that ship with a dependency. - */ -export type PlatformsT = { - [key: string]: { - dependencyConfig?: Function, - projectConfig?: Function, - linkConfig?: Function, - }, -}; - -export type DependencyConfigT = { - dependency: { - platforms: { - android?: AndroidConfigParamsT, - ios?: IOSConfigParamsT, - [key: string]: any, - }, - assets: string[], - hooks: HooksT, - params: InquirerPromptT[], - }, - commands: string[], - platforms: PlatformsT, -}; - -export type ProjectConfigT = { - root: string, - reactNativePath: string, - dependencies: { - [key: string]: { - platforms: { - android: DependencyConfigAndroidT | null, - ios: DependencyConfigIOST | null, - [key: string]: any, - }, - assets: string[], - hooks: HooksT, - params: InquirerPromptT[], - }, - }, - platforms: PlatformsT, - commands: string[], - haste: { - platforms: Array, - providesModuleNodeModules: Array, - }, -}; From 5c3c7c9df38da779f05ec95b549af46b902df218 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 18 Apr 2019 10:19:31 +0100 Subject: [PATCH 45/47] update to new config output structure --- packages/platform-android/native_modules.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/platform-android/native_modules.gradle b/packages/platform-android/native_modules.gradle index 17d87e2a4..0cc096aef 100644 --- a/packages/platform-android/native_modules.gradle +++ b/packages/platform-android/native_modules.gradle @@ -95,7 +95,7 @@ class ReactNativeModules { reactNativeModules.forEach { reactNativeModule -> def name = reactNativeModule["name"] project.dependencies { - // TODO(salakar): other dependency scope methods such as `api` + // TODO(salakar): are other dependency scope methods such as `api` required? implementation project(path: ":${name}") } } @@ -187,7 +187,8 @@ class ReactNativeModules { def dependencies = json["dependencies"] dependencies.each { name, value -> - def androidConfig = value["android"] + def platformsConfig = value["platforms"]; + def androidConfig = platformsConfig["android"] if (androidConfig != null && androidConfig["sourceDir"] != null) { this.logger.info("${LOG_PREFIX}Automatically adding native module '${name}'") From 7a7115f6e8dd10f9eb33eb9d1e8b4c085e08a67f Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 18 Apr 2019 12:14:31 +0100 Subject: [PATCH 46/47] switch to using local cli path --- .../platform-android/native_modules.gradle | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/platform-android/native_modules.gradle b/packages/platform-android/native_modules.gradle index 0cc096aef..757ccf113 100644 --- a/packages/platform-android/native_modules.gradle +++ b/packages/platform-android/native_modules.gradle @@ -54,7 +54,8 @@ class ReactNativeModules { private ArrayList> reactNativeModules private static String LOG_PREFIX = ":ReactNative:" - private static String REACT_NATIVE_CONFIG_CMD = "react-native config" + private static String REACT_NATIVE_CLI_BIN = "node_modules${File.separator}@react-native-community${File.separator}cli${File.separator}build${File.separator}index.js" + private static String REACT_NATIVE_CONFIG_CMD = "node ${REACT_NATIVE_CLI_BIN} config" ReactNativeModules(Logger logger) { this.logger = logger @@ -145,7 +146,9 @@ class ReactNativeModules { if (packages.size() > 0) { packageImports = "import ${applicationId}.BuildConfig;\n\n" - packageImports = packageImports + packages.collect { "// ${it.name}\n${it.packageImportPath}" }.join(';\n') + packageImports = packageImports + packages.collect { + "// ${it.name}\n${it.packageImportPath}" + }.join(';\n') packageClassInstances = ",\n " + packages.collect { it.packageInstance }.join(',') } @@ -173,13 +176,11 @@ class ReactNativeModules { try { cmdProcess = Runtime.getRuntime().exec(REACT_NATIVE_CONFIG_CMD, null, getReactNativeProjectRoot()) - } catch(Exception exception) { - if (exception.message.contains("No such file or directory")) { - this.logger.warn("${LOG_PREFIX}Skipping automatic imports of native modules. (NO_GLOBAL_CLI)") - return reactNativeModules - } - - throw exception + cmdProcess.waitFor() + } catch (Exception exception) { + this.logger.warn("${LOG_PREFIX}${exception.message}") + this.logger.warn("${LOG_PREFIX}Automatic import of native modules failed. (UNKNOWN)") + return reactNativeModules } def reactNativeConfigOutput = cmdProcess.in.text From 108b873d90d9f02f1022e95c2b38ce60c73e8ff4 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 18 Apr 2019 12:16:54 +0100 Subject: [PATCH 47/47] add `native_modules.gradle` to package files --- packages/platform-android/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/platform-android/package.json b/packages/platform-android/package.json index 4cb657627..d728696e4 100644 --- a/packages/platform-android/package.json +++ b/packages/platform-android/package.json @@ -10,6 +10,7 @@ "xmldoc": "^0.4.0" }, "files": [ - "build" + "build", + "native_modules.gradle" ] }