diff --git a/docs/plugins.md b/docs/plugins.md index cec16598a..072e2096d 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -27,6 +27,8 @@ At the startup, React Native CLI reads configuration from all dependencies liste At the end, an array of commands concatenated from all plugins is passed on to the CLI to be loaded after built-in commands. +Also, you can provide your own configuration by creating a `react-native.config.js` file in the root of the project, which overrides the configuration from plugins. + > See [`healthChecks`](./healthChecks.md) for information on how plugins can provide additional health checks for `npx react-native doctor`. ## Command interface diff --git a/packages/cli-config/src/__tests__/__snapshots__/index-test.ts.snap b/packages/cli-config/src/__tests__/__snapshots__/index-test.ts.snap index 453523b62..7e0d70fd5 100644 --- a/packages/cli-config/src/__tests__/__snapshots__/index-test.ts.snap +++ b/packages/cli-config/src/__tests__/__snapshots__/index-test.ts.snap @@ -1,5 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`command specified in root config should overwrite command in "react-native-foo" and "react-native-bar" packages 1`] = ` +Object { + "func": [Function], + "name": "foo-command", + "options": Array [ + Object { + "description": "Custom option", + "name": "--option", + }, + ], +} +`; + exports[`should apply build types from dependency config 1`] = ` Object { "name": "react-native-test", @@ -35,11 +48,17 @@ exports[`should load commands from "react-native-foo" and "react-native-bar" pac Array [ Object { "func": [Function], - "name": "foo-command", + "name": "bar-command", }, Object { "func": [Function], - "name": "bar-command", + "name": "foo-command", + "options": Array [ + Object { + "description": "Custom option", + "name": "--option", + }, + ], }, ] `; diff --git a/packages/cli-config/src/__tests__/index-test.ts b/packages/cli-config/src/__tests__/index-test.ts index 11bc73aaf..612255e7b 100644 --- a/packages/cli-config/src/__tests__/index-test.ts +++ b/packages/cli-config/src/__tests__/index-test.ts @@ -128,6 +128,59 @@ test('should read a config of a dependency and use it to load other settings', ( ).toMatchSnapshot(); }); +test('command specified in root config should overwrite command in "react-native-foo" and "react-native-bar" packages', () => { + DIR = getTempDirectory('config_test_packages'); + writeFiles(DIR, { + 'node_modules/react-native-foo/package.json': '{}', + 'node_modules/react-native-foo/react-native.config.js': `module.exports = { + commands: [ + { + name: 'foo-command', + func: () => console.log('foo') + } + ] + }`, + 'node_modules/react-native-bar/package.json': '{}', + 'node_modules/react-native-bar/react-native.config.js': `module.exports = { + commands: [ + { + name: 'bar-command', + func: () => console.log('bar') + } + ] + }`, + 'package.json': `{ + "dependencies": { + "react-native-foo": "0.0.1", + "react-native-bar": "0.0.1" + } + }`, + 'react-native.config.js': `module.exports = { + reactNativePath: '.', + commands: [ + { + name: 'foo-command', + func: () => { + console.log('overridden foo command'); + }, + options: [ + { + name: '--option', + description: 'Custom option', + }, + ], + }, + ], + };`, + }); + const {commands} = loadConfig(DIR); + const commandsNames = commands.map(({name}) => name); + const commandIndex = commandsNames.indexOf('foo-command'); + + expect(commands[commandIndex].options).not.toBeNull(); + expect(commands[commandIndex]).toMatchSnapshot(); +}); + test('should merge project configuration with default values', () => { DIR = getTempDirectory('config_test_merge'); writeFiles(DIR, { diff --git a/packages/cli-config/src/loadConfig.ts b/packages/cli-config/src/loadConfig.ts index ffd3533de..0e8d6bf9c 100644 --- a/packages/cli-config/src/loadConfig.ts +++ b/packages/cli-config/src/loadConfig.ts @@ -5,6 +5,7 @@ import { DependencyConfig, UserConfig, Config, + Command, } from '@react-native-community/cli-types'; import { findProjectRoot, @@ -72,6 +73,16 @@ function getReactNativeVersion(reactNativePath: string) { return 'unknown'; } +const removeDuplicateCommands = (commands: Command[]) => { + const uniqueCommandsMap = new Map(); + + commands.forEach((command) => { + uniqueCommandsMap.set(command.name, command); + }); + + return Array.from(uniqueCommandsMap.values()); +}; + /** * Loads CLI configuration */ @@ -143,7 +154,10 @@ function loadConfig(projectRoot: string = findProjectRoot()): Config { ); }, }), - commands: [...acc.commands, ...config.commands], + commands: removeDuplicateCommands([ + ...config.commands, + ...acc.commands, + ]), platforms: { ...acc.platforms, ...config.platforms,