Skip to content

Commit

Permalink
Enable platforms to configure CLI commands
Browse files Browse the repository at this point in the history
Summary:
This change adds hooks via the `package.json` for a platform plugin to specify hooks for generating platform configurations. This change also adds hooks to allow platform plugins to participate in `link` and `unlink` commands. The goal is to move platform-specific code for platform plugins into their own repositories / node_modules.

<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.

Help us understand your motivation by explaining why you decided to make this change.

You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html

Happy contributing!

-->

We need to be able to configure the CLI commands for plugin platforms (e.g., react-native-windows) in their own repositories.

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!)

- All jest tests, including new tests, pass.
- `link` and `unlink` commands are successful on iOS and Android.

(If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/react-native-website, and link to your PR here.)

microsoft/react-native-windows#1601

<!--
Help reviewers and the release process by writing your own release notes

**INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.**

  CATEGORY
[----------]        TYPE
[ CLI      ]   [-------------]      LOCATION
[ DOCS     ]   [ BREAKING    ]   [-------------]
[ GENERAL  ]   [ BUGFIX      ]   [-{Component}-]
[ INTERNAL ]   [ ENHANCEMENT ]   [ {File}      ]
[ IOS      ]   [ FEATURE     ]   [ {Directory} ]   |-----------|
[ ANDROID  ]   [ MINOR       ]   [ {Framework} ] - | {Message} |
[----------]   [-------------]   [-------------]   |-----------|

[CATEGORY] [TYPE] [LOCATION] - MESSAGE

 EXAMPLES:

 [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things
 [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput
 [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with
 [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word
 [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position
 [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see
-->

[CLI][FEATURE][local-cli/core/index.js] - Allow platform plugins to contribute to the RNConfig.
[CLI][FEATURE][local-cli/link/link.js] - Allow platform plugins to participate in the `link` command.
[CLI][FEATURE][local-cli/link/unlink.js] - Allow platform plugins to participate in the `unlink` command.
Closes #17745

Differential Revision: D6883558

Pulled By: hramos

fbshipit-source-id: ea32fe21cedd4cc02c5c0d48229f2cdb2ac8142b
  • Loading branch information
rozele authored and facebook-github-bot committed Feb 8, 2018
1 parent 79a63d0 commit a40bfa7
Show file tree
Hide file tree
Showing 22 changed files with 236 additions and 497 deletions.
28 changes: 20 additions & 8 deletions local-cli/core/__tests__/findPlugins.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,45 @@ describe('findPlugins', () => {
jest.mock(pjsonPath, () => ({
dependencies: {'rnpm-plugin-test': '*'},
}));
expect(findPlugins([ROOT])).toHaveLength(1);
expect(findPlugins([ROOT])[0]).toBe('rnpm-plugin-test');

expect(findPlugins([ROOT])).toHaveProperty('commands');
expect(findPlugins([ROOT])).toHaveProperty('platforms');
expect(findPlugins([ROOT]).commands).toHaveLength(1);
expect(findPlugins([ROOT]).commands[0]).toBe('rnpm-plugin-test');
expect(findPlugins([ROOT]).platforms).toHaveLength(0);
});

it('returns an empty array if there are no plugins in this folder', () => {
jest.mock(pjsonPath, () => ({}));
expect(findPlugins([ROOT])).toHaveLength(0);
expect(findPlugins([ROOT])).toHaveProperty('commands');
expect(findPlugins([ROOT])).toHaveProperty('platforms');
expect(findPlugins([ROOT]).commands).toHaveLength(0);
expect(findPlugins([ROOT]).platforms).toHaveLength(0);
});

it('returns an empty array if there is no package.json in the supplied folder', () => {
expect(Array.isArray(findPlugins(['fake-path']))).toBeTruthy();
expect(findPlugins(['fake-path'])).toHaveLength(0);
it('returns an object with empty arrays if there is no package.json in the supplied folder', () => {
expect(findPlugins(['fake-path'])).toHaveProperty('commands');
expect(findPlugins(['fake-path'])).toHaveProperty('platforms');
expect(findPlugins(['fake-path']).commands).toHaveLength(0);
expect(findPlugins(['fake-path']).platforms).toHaveLength(0);
});

it('returns plugins from both dependencies and dev dependencies', () => {
jest.mock(pjsonPath, () => ({
dependencies: {'rnpm-plugin-test': '*'},
devDependencies: {'rnpm-plugin-test-2': '*'},
}));
expect(findPlugins([ROOT])).toHaveLength(2);
expect(findPlugins([ROOT])).toHaveProperty('commands');
expect(findPlugins([ROOT])).toHaveProperty('platforms');
expect(findPlugins([ROOT]).commands).toHaveLength(2);
expect(findPlugins([ROOT]).platforms).toHaveLength(0);
});

it('returns unique list of plugins', () => {
jest.mock(pjsonPath, () => ({
dependencies: {'rnpm-plugin-test': '*'},
devDependencies: {'rnpm-plugin-test': '*'},
}));
expect(findPlugins([ROOT])).toHaveLength(1);
expect(findPlugins([ROOT]).commands).toHaveLength(1);
});
});
32 changes: 23 additions & 9 deletions local-cli/core/findPlugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,19 @@ const findPluginsInReactNativePackage = (pjson) => {
return path.join(pjson.name, pjson.rnpm.plugin);
};

const findPlatformsInPackage = (pjson) => {
if (!pjson.rnpm || !pjson.rnpm.platform) {
return [];
}

return path.join(pjson.name, pjson.rnpm.platform);
};

const findPluginInFolder = (folder) => {
const pjson = readPackage(folder);

if (!pjson) {
return [];
return {commands: [], platforms: []};
}

const deps = union(
Expand All @@ -51,27 +59,33 @@ const findPluginInFolder = (folder) => {

return deps.reduce(
(acc, pkg) => {
let commands = acc.commands;
let platforms = acc.platforms;
if (isRNPMPlugin(pkg)) {
return acc.concat(pkg);
commands = commands.concat(pkg);
}
if (isReactNativePlugin(pkg)) {
const pkgJson = readPackage(path.join(folder, 'node_modules', pkg));
if (!pkgJson) {
return acc;
if (pkgJson) {
commands = commands.concat(findPluginsInReactNativePackage(pkgJson));
platforms = platforms.concat(findPlatformsInPackage(pkgJson));
}
return acc.concat(findPluginsInReactNativePackage(pkgJson));
}
return acc;
return {commands: commands, platforms: platforms};
},
[]
{commands: [], platforms: []}
);
};

/**
* Find plugins in package.json of the given folder
* @param {String} folder Path to the folder to get the package.json from
* @type {Array} Array of plugins or an empty array if no package.json found
* @type {Object} Object of commands and platform plugins
*/
module.exports = function findPlugins(folders) {
return uniq(flatten(folders.map(findPluginInFolder)));
const plugins = folders.map(findPluginInFolder);
return {
commands: uniq(flatten(plugins.map(p => p.commands))),
platforms: uniq(flatten(plugins.map(p => p.platforms)))
};
};
54 changes: 42 additions & 12 deletions local-cli/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const Config = require('../util/Config');
const findPlugins = require('./findPlugins');
const findAssets = require('./findAssets');
const ios = require('./ios');
const windows = require('./windows');
const wrapCommands = require('./wrapCommands');

/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
Expand All @@ -33,6 +32,10 @@ import type {ConfigT} from 'metro';

export type RNConfig = {
...ConfigT,
/**
* Returns an object with all platform configurations.
*/
getPlatformConfig(): Object,
/**
* Returns an array of project commands used by the CLI to load
*/
Expand All @@ -55,10 +58,21 @@ const attachPackage = (command, pkg) => Array.isArray(command)
? command.map(cmd => attachPackage(cmd, pkg))
: { ...command, pkg };

const appRoot = process.cwd();
const plugins = findPlugins([appRoot]);
const pluginPlatforms = plugins
.platforms
.reduce((acc, pathToPlatforms) => {
// $FlowFixMe non-literal require
return Object.assign(acc, require(path.join(appRoot, 'node_modules', pathToPlatforms)));
},
{});

const defaultRNConfig = {

getProjectCommands(): Array<CommandT> {
const appRoot = process.cwd();
const plugins = findPlugins([appRoot])
const commands = plugins
.commands
.map(pathToCommands => {
const name = pathToCommands.split(path.sep)[0];

Expand All @@ -70,35 +84,51 @@ const defaultRNConfig = {
);
});

return flatten(plugins);
return flatten(commands);
},

getPlatformConfig(): Object {
return {
ios,
android,
...pluginPlatforms
};
},

getProjectConfig(): Object {
const platforms = this.getPlatformConfig();
const folder = process.cwd();
const rnpm = getRNPMConfig(folder);

return Object.assign({}, rnpm, {
ios: ios.projectConfig(folder, rnpm.ios || {}),
android: android.projectConfig(folder, rnpm.android || {}),
windows: windows.projectConfig(folder, rnpm.windows || {}),
let config = Object.assign({}, rnpm, {
assets: findAssets(folder, rnpm.assets),
});

Object.keys(platforms).forEach(key => {
config[key] = platforms[key].projectConfig(folder, rnpm[key] || {});
});

return config;
},

getDependencyConfig(packageName: string) {
const platforms = this.getPlatformConfig();
const folder = path.join(process.cwd(), 'node_modules', packageName);
const rnpm = getRNPMConfig(
path.join(process.cwd(), 'node_modules', packageName)
);

return Object.assign({}, rnpm, {
ios: ios.dependencyConfig(folder, rnpm.ios || {}),
android: android.dependencyConfig(folder, rnpm.android || {}),
windows: windows.dependencyConfig(folder, rnpm.windows || {}),
let config = Object.assign({}, rnpm, {
assets: findAssets(folder, rnpm.assets),
commands: wrapCommands(rnpm.commands),
params: rnpm.params || [],
});

Object.keys(platforms).forEach(key => {
config[key] = platforms[key].dependencyConfig(folder, rnpm[key] || {});
});

return config;
},
};

Expand Down
30 changes: 0 additions & 30 deletions local-cli/core/windows/findNamespace.js

This file was deleted.

30 changes: 0 additions & 30 deletions local-cli/core/windows/findPackageClassName.js

This file was deleted.

27 changes: 0 additions & 27 deletions local-cli/core/windows/findProject.js

This file was deleted.

60 changes: 0 additions & 60 deletions local-cli/core/windows/findWindowsSolution.js

This file was deleted.

10 changes: 0 additions & 10 deletions local-cli/core/windows/generateGUID.js

This file was deleted.

Loading

0 comments on commit a40bfa7

Please sign in to comment.