Skip to content

Commit 4e5a2bc

Browse files
Martin Konicekfacebook-github-bot-9
authored andcommitted
Introduce react-native link
Reviewed By: martinbigio Differential Revision: D2575858 fb-gh-sync-id: 13be5a0411db5cd753ebbac3799a7a0139c0f952
1 parent 28c3f89 commit 4e5a2bc

2 files changed

Lines changed: 139 additions & 0 deletions

File tree

local-cli/cli.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var dependencies = require('./dependencies/dependencies');
2121
var fs = require('fs');
2222
var generate = require('./generate/generate');
2323
var library = require('./library/library');
24+
var link = require('./library/link');
2425
var path = require('path');
2526
var Promise = require('promise');
2627
var runAndroid = require('./runAndroid/runAndroid');
@@ -40,6 +41,7 @@ var documentedCommands = {
4041
'start': [server, 'starts the webserver'],
4142
'bundle': [bundle, 'builds the javascript bundle for offline use'],
4243
'new-library': [library, 'generates a native library bridge'],
44+
'link': [link, 'Adds a third-party library to your project. Example: react-native link awesome-camera'],
4345
'android': [generateWrapper, 'generates an Android project for your app'],
4446
'run-android': [runAndroid, 'builds your app and starts it on a connected Android emulator or device'],
4547
'upgrade': [upgrade, 'upgrade your app\'s template files to the latest version; run this after ' +

local-cli/library/link.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
'use strict';
10+
11+
const execSync = require('child_process').execSync;
12+
const eol = require('os').EOL;
13+
const fs = require('fs');
14+
const Promise = require('promise');
15+
16+
/**
17+
* Adds a third-party library to the current project.
18+
*
19+
* Usage:
20+
* react-native link awesome-camera
21+
* react-native link awesome-camera@0.2.0
22+
*
23+
* Does the following:
24+
* npm install --save awesome-camera
25+
* If the library contains native Android code, adds it to the build.
26+
*/
27+
function link(argv, config) {
28+
return new Promise((resolve, reject) => {
29+
try {
30+
_link(argv, config);
31+
} catch (e) {
32+
reject(e);
33+
return;
34+
}
35+
resolve();
36+
});
37+
}
38+
39+
function _link(argv, config) {
40+
// argv examples:
41+
// ['link', 'awesome-camera']
42+
// ['link', 'awesome-camera@0.2']
43+
if (argv.length !== 2) {
44+
throw 'Please provide one argument (library to install).\n' +
45+
'Usage example: react-native link awesome-camera';
46+
}
47+
48+
const libraryAndVersion = argv[1];
49+
const library = libraryAndVersion.split('@')[0];
50+
51+
_npmInstall(libraryAndVersion);
52+
_maybeLinkAndroid(library);
53+
}
54+
55+
function _npmInstall(library) {
56+
const command = 'npm install --save ' + library;
57+
console.log('Running ' + command);
58+
try {
59+
execSync(command);
60+
} catch (e) {
61+
throw command + ' failed';
62+
}
63+
}
64+
65+
/**
66+
* If this library contains Android native code, add it to the build.
67+
*/
68+
function _maybeLinkAndroid(library) {
69+
if (!_pathExists(`node_modules/${library}/android`)) {
70+
return;
71+
}
72+
if (
73+
(!_pathExists('android/settings.gradle')) ||
74+
(!_pathExists('android/app/build.gradle'))) {
75+
console.log('The library contains native Android code but this is not an ' +
76+
'Android project, skipping.'
77+
);
78+
return;
79+
}
80+
if (!_pathExists(`node_modules/${library}/android/build.gradle`)) {
81+
console.log('Looks like the Android library has incorrect format: ' +
82+
'android/build.gradle is missing.'
83+
);
84+
return;
85+
}
86+
87+
// Update settings.gradle
88+
if (fs.readFileSync('android/settings.gradle', 'utf8').indexOf(`include ':${library}'`) !== -1) {
89+
console.log('The library is already present in android/settings.gradle.');
90+
} else {
91+
fs.appendFileSync('android/settings.gradle',
92+
`${eol}// ${library} dependency${eol}` +
93+
`include ':${library}'${eol}` +
94+
`project(':${library}').projectDir = ` +
95+
`new File(rootProject.projectDir, '../node_modules/${library}/android')${eol}`
96+
);
97+
console.log('Updated android/settings.gradle');
98+
}
99+
100+
// Update android/app/build.gradle
101+
const build = fs.readFileSync('android/app/build.gradle', 'utf8');
102+
if (build.indexOf(`compile project(":${library}")`) !== -1) {
103+
console.log('The library is already present in android/app/build.gradle.');
104+
} else {
105+
const append = (
106+
// Include sources
107+
` compile project(":${library}")${eol}` +
108+
// Include libs/*.jar
109+
` compile fileTree(dir: "node_modules/${library}/android/libs", include: ["*.jar"])`
110+
);
111+
const buildWithDeps = build.replace(
112+
/dependencies {([^}]*)}/g,
113+
`dependencies {$1${eol}${append}${eol}}`
114+
);
115+
fs.writeFileSync('android/app/build.gradle', buildWithDeps, 'utf8');
116+
console.log('Updated android/app/build.gradle');
117+
}
118+
// We could try to automate this as well.
119+
// E.g. by convention the package name would end with Package.java.
120+
console.log('Next step: add the library to your MainActivity.java by calling addPackage().\n' +
121+
`Look for the package name in node_modules/${library}/android/src/main/java`
122+
);
123+
}
124+
125+
function _pathExists(path) {
126+
try {
127+
const stats = fs.statSync(path);
128+
return stats.isFile() || stats.isDirectory();
129+
} catch (e) {
130+
if (e.code === 'ENOENT') {
131+
return false;
132+
}
133+
throw e;
134+
}
135+
}
136+
137+
module.exports = link;

0 commit comments

Comments
 (0)