Files
react-native/local-cli/link/unlink.js
Eric Rozell a40bfa730e Enable platforms to configure CLI commands
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.)

https://github.com/Microsoft/react-native-windows/pull/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 https://github.com/facebook/react-native/pull/17745

Differential Revision: D6883558

Pulled By: hramos

fbshipit-source-id: ea32fe21cedd4cc02c5c0d48229f2cdb2ac8142b
2018-02-08 09:47:28 -08:00

187 lines
5.7 KiB
JavaScript

const log = require('npmlog');
const getProjectDependencies = require('./getProjectDependencies');
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
const unregisterDependencyPods = require('./pods/unregisterNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const unlinkAssetsAndroid = require('./android/unlinkAssets');
const unlinkAssetsIOS = require('./ios/unlinkAssets');
const getDependencyConfig = require('./getDependencyConfig');
const compact = require('lodash').compact;
const difference = require('lodash').difference;
const filter = require('lodash').filter;
const flatten = require('lodash').flatten;
const isEmpty = require('lodash').isEmpty;
const promiseWaterfall = require('./promiseWaterfall');
const commandStub = require('./commandStub');
const promisify = require('./promisify');
log.heading = 'rnpm-link';
const unlinkDependencyAndroid = (androidProject, dependency, packageName) => {
if (!androidProject || !dependency.android) {
return;
}
const isInstalled = isInstalledAndroid(androidProject, packageName);
if (!isInstalled) {
log.info(`Android module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} android dependency`);
unregisterDependencyAndroid(packageName, dependency.android, androidProject);
log.info(`Android module ${packageName} has been successfully unlinked`);
};
const unlinkDependencyPlatforms = (platforms, project, dependency, packageName) => {
const ignorePlatforms = ['android', 'ios'];
Object.keys(platforms || {})
.filter(platform => ignorePlatforms.indexOf(platform) < 0)
.forEach(platform => {
if (!project[platform] || !dependency[platform]) {
return null;
}
const linkConfig = platforms[platform] && platforms[platform].linkConfig && platforms[platform].linkConfig();
if (!linkConfig || !linkConfig.isInstalled || !linkConfig.unregister) {
return null;
}
const isInstalled = linkConfig.isInstalled(project[platform], dependency[platform]);
if (!isInstalled) {
log.info(`Platform '${platform}' module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} ${platform} dependency`);
linkConfig.unregister(
packageName,
dependency[platform],
project[platform]
);
log.info(`Platform '${platform}' module ${dependency.name} has been successfully unlinked`);
});
};
const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencies) => {
if (!iOSProject || !dependency.ios) {
return;
}
const isIosInstalled = isInstalledIOS(iOSProject, dependency.ios);
const isPodInstalled = isInstalledPods(iOSProject, dependency.ios);
if (!isIosInstalled && !isPodInstalled) {
log.info(`iOS module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} ios dependency`);
if (isIosInstalled) {
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
}
else if (isPodInstalled) {
unregisterDependencyPods(dependency.ios, iOSProject);
}
log.info(`iOS module ${packageName} has been successfully unlinked`);
};
/**
* Updates project and unlink specific dependency
*
* If optional argument [packageName] is provided, it's the only one
* that's checked
*/
function unlink(args, config) {
const packageName = args[0];
let platforms;
let project;
let dependency;
try {
platforms = config.getPlatformConfig();
project = config.getProjectConfig();
} catch (err) {
log.error(
'ERRPACKAGEJSON',
'No package found. Are you sure it\'s a React Native project?'
);
return Promise.reject(err);
}
try {
dependency = config.getDependencyConfig(packageName);
} catch (err) {
log.warn(
'ERRINVALIDPROJ',
`Project ${packageName} is not a react-native library`
);
return Promise.reject(err);
}
const allDependencies = getDependencyConfig(config, getProjectDependencies());
const otherDependencies = filter(allDependencies, d => d.name !== packageName);
const iOSDependencies = compact(otherDependencies.map(d => d.config.ios));
const tasks = [
() => promisify(dependency.commands.preunlink || commandStub),
() => unlinkDependencyAndroid(project.android, dependency, packageName),
() => unlinkDependencyIOS(project.ios, dependency, packageName, iOSDependencies),
() => unlinkDependencyPlatforms(platforms, project, dependency, packageName),
() => promisify(dependency.commands.postunlink || commandStub)
];
return promiseWaterfall(tasks)
.then(() => {
// @todo move all these to `tasks` array, just like in
// link
const assets = difference(
dependency.assets,
flatten(allDependencies, d => d.assets)
);
if (isEmpty(assets)) {
return Promise.resolve();
}
if (project.ios) {
log.info('Unlinking assets from ios project');
unlinkAssetsIOS(assets, project.ios);
}
if (project.android) {
log.info('Unlinking assets from android project');
unlinkAssetsAndroid(assets, project.android.assetsPath);
}
log.info(
`${packageName} assets has been successfully unlinked from your project`
);
})
.catch(err => {
log.error(
`It seems something went wrong while unlinking. Error: ${err.message}`
);
throw err;
});
}
module.exports = {
func: unlink,
description: 'unlink native dependency',
name: 'unlink <packageName>',
};