From 4e5a2bc0791a77ac67c2b564a220278bea889fcf Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Tue, 27 Oct 2015 11:19:38 -0700 Subject: [PATCH] Introduce react-native link Reviewed By: martinbigio Differential Revision: D2575858 fb-gh-sync-id: 13be5a0411db5cd753ebbac3799a7a0139c0f952 --- local-cli/cli.js | 2 + local-cli/library/link.js | 137 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 local-cli/library/link.js diff --git a/local-cli/cli.js b/local-cli/cli.js index 5fa142711..b7091876f 100644 --- a/local-cli/cli.js +++ b/local-cli/cli.js @@ -21,6 +21,7 @@ var dependencies = require('./dependencies/dependencies'); var fs = require('fs'); var generate = require('./generate/generate'); var library = require('./library/library'); +var link = require('./library/link'); var path = require('path'); var Promise = require('promise'); var runAndroid = require('./runAndroid/runAndroid'); @@ -40,6 +41,7 @@ var documentedCommands = { 'start': [server, 'starts the webserver'], 'bundle': [bundle, 'builds the javascript bundle for offline use'], 'new-library': [library, 'generates a native library bridge'], + 'link': [link, 'Adds a third-party library to your project. Example: react-native link awesome-camera'], 'android': [generateWrapper, 'generates an Android project for your app'], 'run-android': [runAndroid, 'builds your app and starts it on a connected Android emulator or device'], 'upgrade': [upgrade, 'upgrade your app\'s template files to the latest version; run this after ' + diff --git a/local-cli/library/link.js b/local-cli/library/link.js new file mode 100644 index 000000000..86efb174b --- /dev/null +++ b/local-cli/library/link.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +const execSync = require('child_process').execSync; +const eol = require('os').EOL; +const fs = require('fs'); +const Promise = require('promise'); + +/** + * Adds a third-party library to the current project. + * + * Usage: + * react-native link awesome-camera + * react-native link awesome-camera@0.2.0 + * + * Does the following: + * npm install --save awesome-camera + * If the library contains native Android code, adds it to the build. + */ +function link(argv, config) { + return new Promise((resolve, reject) => { + try { + _link(argv, config); + } catch (e) { + reject(e); + return; + } + resolve(); + }); +} + +function _link(argv, config) { + // argv examples: + // ['link', 'awesome-camera'] + // ['link', 'awesome-camera@0.2'] + if (argv.length !== 2) { + throw 'Please provide one argument (library to install).\n' + + 'Usage example: react-native link awesome-camera'; + } + + const libraryAndVersion = argv[1]; + const library = libraryAndVersion.split('@')[0]; + + _npmInstall(libraryAndVersion); + _maybeLinkAndroid(library); +} + +function _npmInstall(library) { + const command = 'npm install --save ' + library; + console.log('Running ' + command); + try { + execSync(command); + } catch (e) { + throw command + ' failed'; + } +} + +/** + * If this library contains Android native code, add it to the build. + */ +function _maybeLinkAndroid(library) { + if (!_pathExists(`node_modules/${library}/android`)) { + return; + } + if ( + (!_pathExists('android/settings.gradle')) || + (!_pathExists('android/app/build.gradle'))) { + console.log('The library contains native Android code but this is not an ' + + 'Android project, skipping.' + ); + return; + } + if (!_pathExists(`node_modules/${library}/android/build.gradle`)) { + console.log('Looks like the Android library has incorrect format: ' + + 'android/build.gradle is missing.' + ); + return; + } + + // Update settings.gradle + if (fs.readFileSync('android/settings.gradle', 'utf8').indexOf(`include ':${library}'`) !== -1) { + console.log('The library is already present in android/settings.gradle.'); + } else { + fs.appendFileSync('android/settings.gradle', + `${eol}// ${library} dependency${eol}` + + `include ':${library}'${eol}` + + `project(':${library}').projectDir = ` + + `new File(rootProject.projectDir, '../node_modules/${library}/android')${eol}` + ); + console.log('Updated android/settings.gradle'); + } + + // Update android/app/build.gradle + const build = fs.readFileSync('android/app/build.gradle', 'utf8'); + if (build.indexOf(`compile project(":${library}")`) !== -1) { + console.log('The library is already present in android/app/build.gradle.'); + } else { + const append = ( + // Include sources + ` compile project(":${library}")${eol}` + + // Include libs/*.jar + ` compile fileTree(dir: "node_modules/${library}/android/libs", include: ["*.jar"])` + ); + const buildWithDeps = build.replace( + /dependencies {([^}]*)}/g, + `dependencies {$1${eol}${append}${eol}}` + ); + fs.writeFileSync('android/app/build.gradle', buildWithDeps, 'utf8'); + console.log('Updated android/app/build.gradle'); + } + // We could try to automate this as well. + // E.g. by convention the package name would end with Package.java. + console.log('Next step: add the library to your MainActivity.java by calling addPackage().\n' + + `Look for the package name in node_modules/${library}/android/src/main/java` + ); +} + +function _pathExists(path) { + try { + const stats = fs.statSync(path); + return stats.isFile() || stats.isDirectory(); + } catch (e) { + if (e.code === 'ENOENT') { + return false; + } + throw e; + } +} + +module.exports = link;