react-native/local-cli/library/link.js

138 lines
4.0 KiB
JavaScript

/**
* 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;