Merge rnpm into react-native

Summary:
This is initial (first step) in the merging process. For now, we are just going to move our code as is into `local-cli` folder (first commit). There were other tweaks made in separate commits to make it easier to go through the code as the diff is expected to be rather large. The purpose of this is to make it easier to start working in small batches and improving the CLI incrementally on a daily basis.

Current codebase will still leave in `rnpm` organisation on Github where we keep working on new features, bugs and ship releases to `npm` until we finish our integration and provide a nice interface for users to migrate (in case it changes at all)

Flow, Jest and npm will ignore this folder for now until we integrate it properly.

Tests are to be rewritten from mocha to jest in `rnpm/link`. We will hook them all up as soon as we start using them in local-cli.

For now, there's no point in having them running and possibly breaking the builds.

We will announce next steps with Kureev later this week
Closes https://github.com/facebook/react-native/pull/7550

Differential Revision: D3327772

Pulled By: mkonicek

fbshipit-source-id: 90faa4bd78476d93ed21b1253e0d95c755d28a30
This commit is contained in:
Mike Grabowski
2016-05-20 04:52:08 -07:00
committed by Facebook Github Bot 9
parent 00c77800c9
commit 149d0b91c2
142 changed files with 5980 additions and 1 deletions
+3
View File
@@ -46,6 +46,9 @@
# Ignore BUCK generated folders
.*\.buckd/
# Ignore RNPM
.*/local-cli/rnpm/.*
.*/node_modules/is-my-json-valid/test/.*\.json
.*/node_modules/iconv-lite/encodings/tables/.*\.json
.*/node_modules/y18n/test/.*\.json
+2
View File
@@ -0,0 +1,2 @@
# rnpm
/local-cli/rnpm
+50
View File
@@ -0,0 +1,50 @@
{
"name": "rnpm",
"version": "1.7.0",
"description": "React Native Package Manager",
"main": "./src/getCommands.js",
"scripts": {
"test": "jest"
},
"jest": {
"testDirectoryName": "test",
"collectCoverage": true,
"testRunner": "<rootDir>/node_modules/jest-cli/src/testRunners/jasmine/jasmine2.js"
},
"author": "Amazing React Native Community (https://github.com/facebook/react-native)",
"contributors": [
"Alexey Kureev <kureev-mail@ya.ru> (https://github.com/Kureev)",
"Mike Grabowski <grabbou@gmail.com> (https://github.com/grabbou)"
],
"engines": {
"node": ">= 4.0.0"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/rnpm/rnpm/issues"
},
"keywords": [
"react-native",
"native-modules",
"packager",
"rnpm"
],
"homepage": "https://github.com/rnpm/rnpm#readme",
"dependencies": {
"commander": "^2.9.0",
"glob": "^7.0.1",
"lodash": "^3.10.1",
"rnpm-plugin-install": "^1.1.0",
"rnpm-plugin-link": "^1.7.4",
"update-notifier": "^0.6.0",
"xmldoc": "^0.4.0"
},
"devDependencies": {
"babel-eslint": "^4.1.5",
"eslint": "^1.9.0",
"mock-fs": "^3.5.0",
"mock-require": "^1.2.1",
"rewire": "^2.5.1",
"jest-cli": "^0.9.0-fb2"
}
}
@@ -0,0 +1,21 @@
const fs = require('fs');
const path = require('path');
/**
* @param {String} folder Folder to seek in
* @return {String}
*/
module.exports = function findAndroidAppFolder(folder) {
const flat = 'android';
const nested = path.join('android', 'app');
if (fs.existsSync(path.join(folder, nested))) {
return nested;
}
if (fs.existsSync(path.join(folder, flat))) {
return flat;
}
return null;
};
@@ -0,0 +1,17 @@
const glob = require('glob');
const path = require('path');
/**
* Find an android application path in the folder
*
* @param {String} folder Name of the folder where to seek
* @return {String}
*/
module.exports = function findManifest(folder) {
const manifestPath = glob.sync(path.join('**', 'AndroidManifest.xml'), {
cwd: folder,
ignore: ['node_modules/**', '**/build/**', 'Examples/**', 'examples/**'],
})[0];
return manifestPath ? path.join(folder, manifestPath) : null;
};
@@ -0,0 +1,20 @@
const fs = require('fs');
const path = require('path');
const glob = require('glob');
/**
* Gets package's class name (class that implements ReactPackage)
* by searching for its declaration in all Java files present in the folder
*
* @param {String} folder Folder to find java files
*/
module.exports = function getPackageClassName(folder) {
const files = glob.sync('**/*.java', { cwd: folder });
const packages = files
.map(filePath => fs.readFileSync(path.join(folder, filePath), 'utf8'))
.map(file => file.match(/class (.*) implements ReactPackage/))
.filter(match => match);
return packages.length ? packages[0][1] : null;
};
@@ -0,0 +1,111 @@
const path = require('path');
const fs = require('fs');
const glob = require('glob');
const findAndroidAppFolder = require('./findAndroidAppFolder');
const findManifest = require('./findManifest');
const readManifest = require('./readManifest');
const findPackageClassName = require('./findPackageClassName');
const getPackageName = (manifest) => manifest.attr.package;
/**
* Gets android project config by analyzing given folder and taking some
* defaults specified by user into consideration
*/
exports.projectConfig = function projectConfigAndroid(folder, userConfig) {
const src = userConfig.sourceDir || findAndroidAppFolder(folder);
if (!src) {
return null;
}
const sourceDir = path.join(folder, src);
const isFlat = sourceDir.indexOf('app') === -1;
const manifestPath = findManifest(sourceDir);
if (!manifestPath) {
return null;
}
const manifest = readManifest(manifestPath);
const packageName = userConfig.packageName || getPackageName(manifest);
const packageFolder = userConfig.packageFolder ||
packageName.replace(/\./g, path.sep);
const mainActivityPath = path.join(
sourceDir,
userConfig.mainActivityPath || `src/main/java/${packageFolder}/MainActivity.java`
);
const stringsPath = path.join(
sourceDir,
userConfig.stringsPath || 'src/main/res/values/strings.xml'
);
const settingsGradlePath = path.join(
folder,
'android',
userConfig.settingsGradlePath || 'settings.gradle'
);
const assetsPath = path.join(
sourceDir,
userConfig.assetsPath || 'src/main/assets'
);
const buildGradlePath = path.join(
sourceDir,
userConfig.buildGradlePath || 'build.gradle'
);
return {
sourceDir,
isFlat,
folder,
stringsPath,
manifestPath,
buildGradlePath,
settingsGradlePath,
assetsPath,
mainActivityPath,
};
};
/**
* Same as projectConfigAndroid except it returns
* different config that applies to packages only
*/
exports.dependencyConfig = function dependencyConfigAndroid(folder, userConfig) {
const src = userConfig.sourceDir || findAndroidAppFolder(folder);
if (!src) {
return null;
}
const sourceDir = path.join(folder, src);
const manifestPath = findManifest(sourceDir);
if (!manifestPath) {
return null;
}
const manifest = readManifest(manifestPath);
const packageName = userConfig.packageName || getPackageName(manifest);
const packageClassName = findPackageClassName(sourceDir);
/**
* This module has no package to export
*/
if (!packageClassName) {
return null;
}
const packageImportPath = userConfig.packageImportPath ||
`import ${packageName}.${packageClassName};`;
const packageInstance = userConfig.packageInstance ||
`new ${packageClassName}()`;
return { sourceDir, folder, manifest, packageImportPath, packageInstance };
};
@@ -0,0 +1,10 @@
const fs = require('fs');
const xml = require('xmldoc');
/**
* @param {String} manifestPath
* @return {XMLDocument} Parsed manifest's content
*/
module.exports = function readManifest(manifestPath) {
return new xml.XmlDocument(fs.readFileSync(manifestPath, 'utf8'));
};
@@ -0,0 +1,20 @@
const glob = require('glob');
const path = require('path');
const findAssetsInFolder = (folder) =>
glob.sync(path.join(folder, '**'), { nodir: true });
/**
* Given an array of assets folders, e.g. ['Fonts', 'Images'],
* it globs in them to find all files that can be copied.
*
* It returns an array of absolute paths to files found.
*/
module.exports = function findAssets(folder, assets) {
return (assets || [])
.map(assetsFolder => path.join(folder, assetsFolder))
.reduce((assets, assetsFolder) =>
assets.concat(findAssetsInFolder(assetsFolder)),
[]
);
};
+44
View File
@@ -0,0 +1,44 @@
const path = require('path');
const android = require('./android');
const ios = require('./ios');
const findAssets = require('./findAssets');
const wrapCommands = require('./wrapCommands');
const getRNPMConfig = (folder) =>
require(path.join(folder, './package.json')).rnpm || {};
/**
* Returns project config from the current working directory
* @return {Object}
*/
exports.getProjectConfig = function getProjectConfig() {
const folder = process.cwd();
const rnpm = getRNPMConfig(folder);
return Object.assign({}, rnpm, {
ios: ios.projectConfig(folder, rnpm.ios || {}),
android: android.projectConfig(folder, rnpm.android || {}),
assets: findAssets(folder, rnpm.assets),
});
};
/**
* Returns a dependency config from node_modules/<package_name>
* @param {String} packageName Dependency name
* @return {Object}
*/
exports.getDependencyConfig = function getDependencyConfig(packageName) {
const folder = path.join(process.cwd(), 'node_modules', packageName);
const rnpm = getRNPMConfig(
path.join(process.cwd(), 'node_modules', packageName.split('/')[0])
);
return Object.assign({}, rnpm, {
ios: ios.dependencyConfig(folder, rnpm.ios || {}),
android: android.dependencyConfig(folder, rnpm.android || {}),
assets: findAssets(folder, rnpm.assets),
commands: wrapCommands(rnpm.commands),
params: rnpm.params || [],
});
};
@@ -0,0 +1,47 @@
const glob = require('glob');
const path = require('path');
/**
* Glob pattern to look for xcodeproj
*/
const GLOB_PATTERN = '**/*.xcodeproj';
/**
* Regexp matching all test projects
*/
const TEST_PROJECTS = /test|example|sample/i;
/**
* Base iOS folder
*/
const IOS_BASE = 'ios';
/**
* These folders will be excluded from search to speed it up
*/
const GLOB_EXCLUDE_PATTERN = ['**/@(Pods|node_modules)/**'];
/**
* Finds iOS project by looking for all .xcodeproj files
* in given folder.
*
* Returns first match if files are found or null
*
* Note: `./ios/*.xcodeproj` are returned regardless of the name
*/
module.exports = function findProject(folder) {
const projects = glob
.sync(GLOB_PATTERN, {
cwd: folder,
ignore: GLOB_EXCLUDE_PATTERN,
})
.filter(project => {
return path.dirname(project) === IOS_BASE || !TEST_PROJECTS.test(project);
});
if (projects.length === 0) {
return null;
}
return projects[0];
};
@@ -0,0 +1,31 @@
const path = require('path');
const findProject = require('./findProject');
/**
* Returns project config by analyzing given folder and applying some user defaults
* when constructing final object
*/
exports.projectConfig = function projectConfigIOS(folder, userConfig) {
const project = userConfig.project || findProject(folder);
/**
* No iOS config found here
*/
if (!project) {
return null;
}
const projectPath = path.join(folder, project);
return {
sourceDir: path.dirname(projectPath),
folder: folder,
pbxprojPath: path.join(projectPath, 'project.pbxproj'),
projectPath: projectPath,
projectName: path.basename(projectPath),
libraryFolder: userConfig.libraryFolder || 'Libraries',
plist: userConfig.plist || [],
};
};
exports.dependencyConfig = exports.projectConfig;
@@ -0,0 +1,9 @@
const makeCommand = require('../makeCommand');
module.exports = function wrapCommands(commands) {
const mappedCommands = {};
Object.keys(commands || []).forEach((k) =>
mappedCommands[k] = makeCommand(commands[k])
);
return mappedCommands;
};
+37
View File
@@ -0,0 +1,37 @@
const path = require('path');
const fs = require('fs');
const union = require('lodash').union;
const uniq = require('lodash').uniq;
const flatten = require('lodash').flatten;
/**
* Filter dependencies by name pattern
* @param {String} dependency Name of the dependency
* @return {Boolean} If dependency is a rnpm plugin
*/
const isPlugin = (dependency) => !!~dependency.indexOf('rnpm-plugin-');
const findPluginInFolder = (folder) => {
var pjson;
try {
pjson = require(path.join(folder, 'package.json'));
} catch (e) {
return [];
}
const deps = union(
Object.keys(pjson.dependencies || {}),
Object.keys(pjson.devDependencies || {})
);
return deps.filter(isPlugin);
};
/**
* 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
*/
module.exports = function findPlugins(folders) {
return uniq(flatten(folders.map(findPluginInFolder)));
};
+20
View File
@@ -0,0 +1,20 @@
const path = require('path');
const fs = require('fs');
const uniq = require('lodash').uniq;
const flattenDeep = require('lodash').flattenDeep;
const findPlugins = require('./findPlugins');
/**
* @return {Array} Array of commands
*/
module.exports = function getCommands() {
const rnpmRoot = path.join(__dirname, '..');
const appRoot = process.cwd();
return uniq(
flattenDeep([
findPlugins([rnpmRoot]).map(require),
findPlugins([appRoot]).map(name => require(path.join(appRoot, 'node_modules', name))),
])
, 'name');
};
+25
View File
@@ -0,0 +1,25 @@
const spawn = require('child_process').spawn;
module.exports = function makeCommand(command) {
return (cb) => {
if (!cb) {
throw new Error(`You missed a callback function for the ${command} command`);
}
const args = command.split(' ');
const cmd = args.shift();
const commandProcess = spawn(cmd, args, {
stdio: 'inherit',
stdin: 'inherit',
});
commandProcess.on('close', function prelink(code) {
if (code) {
throw new Error(`Error occured during executing "${command}" command`);
}
cb();
});
};
};
@@ -0,0 +1,30 @@
jest.autoMockOff();
const findAndroidAppFolder = require('../../src/config/android/findAndroidAppFolder');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
describe('android::findAndroidAppFolder', () => {
beforeAll(() => mockFs({
empty: {},
nested: {
android: {
app: mocks.valid,
},
},
flat: {
android: mocks.valid,
},
}));
it('should return an android app folder if it exists in the given folder', () => {
expect(findAndroidAppFolder('flat')).toBe('android');
expect(findAndroidAppFolder('nested')).toBe('android/app');
});
it('should return `null` if there\'s no android app folder', () => {
expect(findAndroidAppFolder('empty')).toBe(null);
});
afterAll(mockFs.restore);
});
@@ -0,0 +1,25 @@
jest.autoMockOff();
const findManifest = require('../../src/config/android/findManifest');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
describe('android::findManifest', () => {
beforeAll(() => mockFs({
empty: {},
flat: {
android: mocks.valid,
},
}));
it('should return a manifest path if file exists in the folder', () => {
expect(typeof findManifest('flat')).toBe('string');
});
it('should return `null` if there is no manifest in the folder', () => {
expect(findManifest('empty')).toBe(null);
});
afterAll(mockFs.restore);
});
@@ -0,0 +1,25 @@
jest.autoMockOff();
const findPackageClassName = require('../../src/config/android/findPackageClassName');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
describe('android::findPackageClassName', () => {
beforeAll(() => mockFs({
empty: {},
flat: {
android: mocks.valid,
},
}));
it('should return manifest content if file exists in the folder', () => {
expect(typeof findPackageClassName('flat')).toBe('string');
});
it('should return `null` if there\'s no matches', () => {
expect(findPackageClassName('empty')).toBe(null);
});
afterAll(mockFs.restore);
});
@@ -0,0 +1,49 @@
jest.autoMockOff();
const getDependencyConfig = require('../../src/config/android').dependencyConfig;
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
const userConfig = {};
describe('android::getDependencyConfig', () => {
beforeAll(() => mockFs({
empty: {},
nested: {
android: {
app: mocks.valid,
},
},
corrupted: {
android: {
app: mocks.corrupted,
},
},
noPackage: {
android: {},
},
}));
it('should return an object with android project configuration', () => {
expect(getDependencyConfig('nested', userConfig)).not.toBe(null);
expect(typeof getDependencyConfig('nested', userConfig)).toBe('object');
});
it('should return `null` if manifest file hasn\'t been found', () => {
expect(getDependencyConfig('empty', userConfig)).toBe(null);
});
it('should return `null` if android project was not found', () => {
expect(getDependencyConfig('empty', userConfig)).toBe(null);
});
it('should return `null` if android project does not contain ReactPackage', () => {
expect(getDependencyConfig('noPackage', userConfig)).toBe(null);
});
it('should return `null` if it can\'t find a packageClassName', () => {
expect(getDependencyConfig('corrupted', userConfig)).toBe(null);
});
afterAll(mockFs.restore);
});
@@ -0,0 +1,56 @@
jest.autoMockOff();
const getProjectConfig = require('../../src/config/android').projectConfig;
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
describe('android::getProjectConfig', () => {
beforeAll(() => mockFs({
empty: {},
nested: {
android: {
app: mocks.valid,
},
},
flat: {
android: mocks.valid,
},
noManifest: {
android: {},
},
}));
it('should return `null` if manifest file hasn\'t been found', () => {
const userConfig = {};
const folder = 'noManifest';
expect(getProjectConfig(folder, userConfig)).toBe(null);
});
describe('return an object with android project configuration for', () => {
it('nested structure', () => {
const userConfig = {};
const folder = 'nested';
expect(getProjectConfig(folder, userConfig)).not.toBe(null);
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
});
it('flat structure', () => {
const userConfig = {};
const folder = 'flat';
expect(getProjectConfig(folder, userConfig)).not.toBe(null);
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
});
});
it('should return `null` if android project was not found', () => {
const userConfig = {};
const folder = 'empty';
expect(getProjectConfig(folder, userConfig)).toBe(null);
});
afterAll(mockFs.restore);
});
@@ -0,0 +1,31 @@
jest.autoMockOff();
const findManifest = require('../../src/config/android/findManifest');
const readManifest = require('../../src/config/android/readManifest');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
describe('android::readManifest', () => {
beforeAll(() => mockFs({
empty: {},
nested: {
android: {
app: mocks.valid,
},
},
}));
it('should return manifest content if file exists in the folder', () => {
const manifestPath = findManifest('nested');
expect(readManifest(manifestPath)).not.toBe(null);
expect(typeof readManifest(manifestPath)).toBe('object');
});
it('should throw an error if there is no manifest in the folder', () => {
const fakeManifestPath = findManifest('empty');
expect(() => readManifest(fakeManifestPath)).toThrow();
});
afterAll(mockFs.restore);
});
@@ -0,0 +1,31 @@
jest.autoMockOff();
const findAssets = require('../src/config/findAssets');
const mockFs = require('mock-fs');
const dependencies = require('./fixtures/dependencies');
const isArray = (arg) =>
Object.prototype.toString.call(arg) === '[object Array]';
describe('findAssets', () => {
beforeEach(() => mockFs({ testDir: dependencies.withAssets }));
it('should return an array of all files in given folders', () => {
const assets = findAssets('testDir', ['fonts', 'images']);
expect(isArray(assets)).toBeTruthy();
expect(assets.length).toEqual(3);
});
it('should prepend assets paths with the folder path', () => {
const assets = findAssets('testDir', ['fonts', 'images']);
assets.forEach(assetPath => expect(assetPath).toContain('testDir'));
});
it('should return an empty array if given assets are null', () => {
expect(findAssets('testDir', null).length).toEqual(0);
});
afterEach(mockFs.restore);
});
+46
View File
@@ -0,0 +1,46 @@
jest.autoMockOff();
const path = require('path');
const findPlugins = require('../src/findPlugins');
const pjsonPath = path.join(process.cwd(), 'package.json');
const isArray = (arg) =>
Object.prototype.toString.call(arg) === '[object Array]';
describe('findPlugins', () => {
it('should return an array of dependencies', () => {
jest.setMock(pjsonPath, {
dependencies: { 'rnpm-plugin-test': '*' },
});
expect(findPlugins([process.cwd()]).length).toBe(1);
expect(findPlugins([process.cwd()])[0]).toBe('rnpm-plugin-test');
});
it('should return an empty array if there\'re no plugins in this folder', () => {
jest.setMock(pjsonPath, {});
expect(findPlugins([process.cwd()]).length).toBe(0);
});
it('should return an empty array if there\'s no package.json in the supplied folder', () => {
expect(isArray(findPlugins(['fake-path']))).toBeTruthy();
expect(findPlugins(['fake-path']).length).toBe(0);
});
it('should return plugins from both dependencies and dev dependencies', () => {
jest.setMock(pjsonPath, {
dependencies: { 'rnpm-plugin-test': '*' },
devDependencies: { 'rnpm-plugin-test-2': '*' },
});
expect(findPlugins([process.cwd()]).length).toEqual(2);
});
it('should return unique list of plugins', () => {
jest.setMock(pjsonPath, {
dependencies: { 'rnpm-plugin-test': '*' },
devDependencies: { 'rnpm-plugin-test': '*' },
});
expect(findPlugins([process.cwd()]).length).toEqual(1);
});
});
+49
View File
@@ -0,0 +1,49 @@
const fs = require('fs');
const path = require('path');
const manifest = fs.readFileSync(path.join(__dirname, './files/AndroidManifest.xml'));
const mainJavaClass = fs.readFileSync(path.join(__dirname, './files/Main.java'));
exports.valid = {
src: {
'AndroidManifest.xml': manifest,
main: {
com: {
some: {
example: {
'Main.java': mainJavaClass,
'ReactPackage.java': fs.readFileSync(path.join(__dirname, './files/ReactPackage.java')),
},
},
},
},
},
};
exports.corrupted = {
src: {
'AndroidManifest.xml': manifest,
main: {
com: {
some: {
example: {},
},
},
},
},
};
exports.noPackage = {
src: {
'AndroidManifest.xml': manifest,
main: {
com: {
some: {
example: {
'Main.java': mainJavaClass,
},
},
},
},
},
};
+15
View File
@@ -0,0 +1,15 @@
exports.single = {
func: () => {},
description: 'Test action',
name: 'test',
};
exports.multiple = [{
func: () => {},
description: 'Test action #1',
name: 'test1',
}, {
func: () => {},
description: 'Test action #2',
name: 'test2',
}];
+27
View File
@@ -0,0 +1,27 @@
const fs = require('fs');
const path = require('path');
const android = require('./android');
const pjson = fs.readFileSync(path.join(__dirname, 'files', 'package.json'));
module.exports = {
valid: {
'package.json': pjson,
android: android.valid,
},
withAssets: {
'package.json': pjson,
android: android.valid,
fonts: {
'A.ttf': '',
'B.ttf': '',
},
images: {
'C.jpg': '',
},
},
noPackage: {
'package.json': pjson,
android: android.noPackage,
},
};
@@ -0,0 +1,4 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.some.example">
</manifest>
+8
View File
@@ -0,0 +1,8 @@
package com.some.example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SomeExamplePackage {}
@@ -0,0 +1,35 @@
package com.some.example;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SomeExamplePackage implements ReactPackage {
public SomeExamplePackage() {}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new SomeExampleModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
+62
View File
@@ -0,0 +1,62 @@
{
"name": "react-native-vector-icons",
"version": "1.0.0",
"description": "Customizable Icons for React Native with support for NavBar/TabBar, image source and full styling. Choose from 3000+ bundled icons or use your own.",
"main": "index.js",
"bin": {
"generate-icon": "./generate-icon.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rm -rf {Fonts,Entypo.js,EvilIcons.js,FontAwesome.js,Foundation.js,Ionicons.js,MaterialIcons.js,Octicons.js,Zocial.js} && mkdir Fonts && npm run build-entypo && npm run build-evilicons && npm run build-fontawesome && npm run build-foundation && npm run build-ionicons && npm run build-materialicons && npm run build-octicons && npm run build-zocial",
"build-entypo": "mkdir -p tmp/svg && curl https://dl.dropboxusercontent.com/u/4339492/entypo.zip > tmp/entypo.zip && unzip -j tmp/entypo.zip *.svg -x __MACOSX/* -d tmp/svg && fontcustom compile tmp/svg -o tmp -n Entypo -t css -h && node generate-icon tmp/Entypo.css --componentName=Entypo --fontFamily=Entypo > Entypo.js && cp tmp/Entypo.ttf Fonts && rm -rf {tmp,.fontcustom-manifest.json}",
"build-evilicons": "fontcustom compile node_modules/evil-icons/assets/icons -o tmp -n EvilIcons -t css -h && node generate-icon tmp/EvilIcons.css --prefix=.icon-ei- --componentName=EvilIcons --fontFamily=EvilIcons > EvilIcons.js && cp tmp/EvilIcons.ttf Fonts && rm -rf {tmp,.fontcustom-manifest.json}",
"build-fontawesome": "node generate-icon node_modules/font-awesome/css/font-awesome.css --prefix=.fa- --componentName=FontAwesome --fontFamily=FontAwesome > FontAwesome.js && cp node_modules/font-awesome/fonts/fontawesome-webfont.ttf Fonts/FontAwesome.ttf",
"build-foundation": "node generate-icon bower_components/foundation-icon-fonts/foundation-icons.css --prefix=.fi- --componentName=Foundation --fontFamily=fontcustom > Foundation.js && cp bower_components/foundation-icon-fonts/foundation-icons.ttf Fonts/Foundation.ttf",
"build-ionicons": "node generate-icon bower_components/ionicons/css/ionicons.css --prefix=.ion- --componentName=Ionicons --fontFamily=Ionicons > Ionicons.js && cp bower_components/ionicons/fonts/ionicons.ttf Fonts/Ionicons.ttf",
"build-materialicons": "mkdir -p tmp/svg && for f in ./node_modules/material-design-icons/*/svg/production/ic_*_48px.svg; do t=${f/*\\/ic_/}; t=${t/_48px/}; cp \"$f\" \"./tmp/svg/${t//_/-}\"; done && fontcustom compile tmp/svg -o tmp -n MaterialIcons -t css -h && node generate-icon tmp/MaterialIcons.css --componentName=MaterialIcons --fontFamily=MaterialIcons > MaterialIcons.js && cp tmp/MaterialIcons.ttf Fonts && rm -rf {tmp,.fontcustom-manifest.json}",
"build-octicons": "node generate-icon bower_components/octicons/octicons/octicons.css --prefix=.octicon- --componentName=Octicons --fontFamily=octicons > Octicons.js && cp bower_components/octicons/octicons/octicons.ttf Fonts/Octicons.ttf",
"build-zocial": "node generate-icon bower_components/css-social-buttons/css/zocial.css --prefix=.zocial. --componentName=Zocial --fontFamily=zocial > Zocial.js && cp bower_components/css-social-buttons/css/zocial.ttf Fonts/Zocial.ttf"
},
"keywords": [
"react-native",
"react-component",
"react-native-component",
"react",
"mobile",
"ios",
"android",
"ui",
"icon",
"icons",
"vector",
"retina",
"font"
],
"author": {
"name": "Joel Arvidsson",
"email": "joel@oblador.se"
},
"homepage": "https://github.com/oblador/react-native-vector-icons",
"bugs": {
"url": "https://github.com/oblador/react-native-vector-icons/issues"
},
"repository": {
"type": "git",
"url": "git://github.com/oblador/react-native-vector-icons.git"
},
"license": "MIT",
"peerDependencies": {
"react-native": ">=0.4.0 || 0.5.0-rc1 || 0.6.0-rc || 0.7.0-rc || 0.7.0-rc.2 || 0.8.0-rc || 0.8.0-rc.2 || 0.9.0-rc || 0.10.0-rc || 0.11.0-rc || 0.12.0-rc || 0.13.0-rc || 0.14.0-rc || 0.15.0-rc || 0.16.0-rc"
},
"dependencies": {
"lodash": "^3.8.0",
"yargs": "^3.30.0",
"rnpm-plugin-test": "*"
},
"devDependencies": {
"evil-icons": "^1.7.6",
"font-awesome": "^4.4.0",
"material-design-icons": "^2.1.1"
}
}
+804
View File
@@ -0,0 +1,804 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* androidTestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* androidTestTests.m */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
672DE8B31B124B8088D0D29F /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B5255B7628A54AC2A9B4B2A0 /* libBVLinearGradient.a */; };
68FEB18F24414EF981BD7940 /* libCodePush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C67FE8F7294B7A83790610 /* libCodePush.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
C6C437D070BA42D6BE39198B /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A7396DFBAFA4CA092E367F5 /* libRCTVideo.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTActionSheet;
};
00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTGeolocation;
};
00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
remoteInfo = RCTImage;
};
00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B511DB1A9E6C8500147676;
remoteInfo = RCTNetwork;
};
00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
remoteInfo = RCTVibration;
};
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = androidTest;
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
remoteInfo = RCTWebSocket;
};
146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTLinking;
};
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = "<group>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
00E356EE1AD99517003FC87E /* androidTestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = androidTestTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* androidTestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = androidTestTests.m; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* androidTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = androidTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = androidTest/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = androidTest/AppDelegate.m; sourceTree = "<group>"; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = androidTest/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = androidTest/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = androidTest/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
53C67FE8F7294B7A83790610 /* libCodePush.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libCodePush.a; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
B5255B7628A54AC2A9B4B2A0 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = "<group>"; };
467A6CBCB2164E7D9B673D4C /* CodePush.xcodeproj */ = {isa = PBXFileReference; name = "CodePush.xcodeproj"; path = "../node_modules/react-native-code-push/CodePush.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
FD7121847BA447D8B737F22A /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; name = "BVLinearGradient.xcodeproj"; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
409DA945815C46DEB4F254DB /* RCTVideo.xcodeproj */ = {isa = PBXFileReference; name = "RCTVideo.xcodeproj"; path = "../node_modules/react-native-video/RCTVideo.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
3A7396DFBAFA4CA092E367F5 /* libRCTVideo.a */ = {isa = PBXFileReference; name = "libRCTVideo.a"; path = "libRCTVideo.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
00E356EB1AD99517003FC87E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
146834051AC3E58100842450 /* libReact.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
672DE8B31B124B8088D0D29F /* libBVLinearGradient.a in Frameworks */,
68FEB18F24414EF981BD7940 /* libCodePush.a in Frameworks */,
C6C437D070BA42D6BE39198B /* libRCTVideo.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
00C302A81ABCB8CE00DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302B61ABCB90400DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302BC1ABCB91800DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302D41ABCB9D200DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302E01ABCB9EE00DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */,
);
name = Products;
sourceTree = "<group>";
};
00E356EF1AD99517003FC87E /* androidTestTests */ = {
isa = PBXGroup;
children = (
00E356F21AD99517003FC87E /* androidTestTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
path = androidTestTests;
sourceTree = "<group>";
};
00E356F01AD99517003FC87E /* Supporting Files */ = {
isa = PBXGroup;
children = (
00E356F11AD99517003FC87E /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
139105B71AF99BAD00B5F7CC /* Products */ = {
isa = PBXGroup;
children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
);
name = Products;
sourceTree = "<group>";
};
139FDEE71B06529A00C62182 /* Products */ = {
isa = PBXGroup;
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
);
name = Products;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* androidTest */ = {
isa = PBXGroup;
children = (
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
);
name = androidTest;
sourceTree = "<group>";
};
146834001AC3E56700842450 /* Products */ = {
isa = PBXGroup;
children = (
146834041AC3E56700842450 /* libReact.a */,
);
name = Products;
sourceTree = "<group>";
};
78C398B11ACF4ADC00677621 /* Products */ = {
isa = PBXGroup;
children = (
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
);
name = Products;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
467A6CBCB2164E7D9B673D4C /* CodePush.xcodeproj */,
FD7121847BA447D8B737F22A /* BVLinearGradient.xcodeproj */,
409DA945815C46DEB4F254DB /* RCTVideo.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
};
832341B11AAA6A8300B99B32 /* Products */ = {
isa = PBXGroup;
children = (
832341B51AAA6A8300B99B32 /* libRCTText.a */,
);
name = Products;
sourceTree = "<group>";
};
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* androidTest */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* androidTestTests */,
83CBBA001A601CBA00E9B192 /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* androidTest.app */,
00E356EE1AD99517003FC87E /* androidTestTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
00E356ED1AD99517003FC87E /* androidTestTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "androidTestTests" */;
buildPhases = (
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
);
buildRules = (
);
dependencies = (
00E356F51AD99517003FC87E /* PBXTargetDependency */,
);
name = androidTestTests;
productName = androidTestTests;
productReference = 00E356EE1AD99517003FC87E /* androidTestTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
13B07F861A680F5B00A75B9A /* androidTest */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "androidTest" */;
buildPhases = (
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
);
buildRules = (
);
dependencies = (
);
name = androidTest;
productName = "Hello World";
productReference = 13B07F961A680F5B00A75B9A /* androidTest.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
00E356ED1AD99517003FC87E = {
CreatedOnToolsVersion = 6.2;
TestTargetID = 13B07F861A680F5B00A75B9A;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "androidTest" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 83CBB9F61A601CBA00E9B192;
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
},
{
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
},
{
ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
},
{
ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
},
{
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
},
{
ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
},
{
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
},
{
ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;
ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
},
{
ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
},
{
ProductGroup = 146834001AC3E56700842450 /* Products */;
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
},
);
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* androidTest */,
00E356ED1AD99517003FC87E /* androidTestTests */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTActionSheet.a;
remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTGeolocation.a;
remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTImage.a;
remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTNetwork.a;
remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTVibration.a;
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTSettings.a;
remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTWebSocket.a;
remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
146834041AC3E56700842450 /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libReact.a;
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTLinking.a;
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTText.a;
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
00E356EC1AD99517003FC87E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native code and images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../node_modules/react-native/packager/react-native-xcode.sh";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
00E356EA1AD99517003FC87E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
00E356F31AD99517003FC87E /* androidTestTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13B07F861A680F5B00A75B9A /* androidTest */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
path = androidTest;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = androidTestTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/androidTest.app/androidTest";
};
name = Debug;
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = androidTestTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/androidTest.app/androidTest";
};
name = Release;
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEAD_CODE_STRIPPING = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)",
"$(SRCROOT)/../node_modules/react-native-code-push",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/**",
"$(SRCROOT)/../node_modules/react-native-video",
);
INFOPLIST_FILE = androidTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = androidTest;
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)",
"$(SRCROOT)/../node_modules/react-native-code-push",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/**",
"$(SRCROOT)/../node_modules/react-native-video",
);
INFOPLIST_FILE = androidTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = androidTest;
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)",
"$(SRCROOT)/../node_modules/react-native-code-push",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/**",
"$(SRCROOT)/../node_modules/react-native-video",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
83CBBA211A601CBA00E9B192 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)",
"$(SRCROOT)/../node_modules/react-native-code-push",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/**",
"$(SRCROOT)/../node_modules/react-native-video",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "androidTestTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00E356F61AD99517003FC87E /* Debug */,
00E356F71AD99517003FC87E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "androidTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
13B07F951A680F5B00A75B9A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "androidTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,
83CBBA211A601CBA00E9B192 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
}
+14
View File
@@ -0,0 +1,14 @@
const fs = require('fs');
const path = require('path');
exports.valid = {
'demoProject.xcodeproj': {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
};
exports.validTestName = {
'MyTestProject.xcodeproj': {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
};
+24
View File
@@ -0,0 +1,24 @@
const fs = require('fs');
const path = require('path');
const android = require('./android');
const ios = require('./ios');
const flat = {
android: android.valid,
ios: ios.valid,
};
const nested = {
android: {
app: android.valid,
},
ios: ios.valid,
};
const withExamples = {
Examples: flat,
ios: ios.valid,
android: android.valid,
};
module.exports = { flat, nested, withExamples };
@@ -0,0 +1,171 @@
jest.autoMockOff();
const path = require('path');
const mock = require('mock-require');
const rewire = require('rewire');
const commands = require('./fixtures/commands');
const isArray = (arg) =>
Object.prototype.toString.call(arg) === '[object Array]';
/**
* Paths to two possible `node_modules` locations `rnpm` can be installed
*/
const LOCAL_NODE_MODULES = path.join(process.cwd(), 'node_modules');
const GLOBAL_NODE_MODULES = '/usr/local/lib/node_modules';
/**
* Paths to `package.json` of project, and rnpm - in two installation locations
*/
const APP_JSON = path.join(process.cwd(), 'package.json');
const GLOBAL_RNPM_PJSON = path.join(GLOBAL_NODE_MODULES, '/rnpm/package.json');
const LOCAL_RNPM_PJSON = path.join(LOCAL_NODE_MODULES, 'rnpm/package.json');
/**
* Sample `rnpm` plugin used in test cases
*/
const SAMPLE_RNPM_PLUGIN = 'rnpm-plugin-test';
/**
* Sample `package.json` of RNPM that will be used in test cases
*/
const SAMPLE_RNPM_JSON = {
dependencies: {
[SAMPLE_RNPM_PLUGIN]: '*',
},
};
/**
* Project without `rnpm` plugins defined
*/
const NO_PLUGINS_JSON = {
dependencies: {},
};
const getCommands = rewire('../src/getCommands');
var revert;
describe('getCommands', () => {
afterEach(mock.stopAll);
describe('in all installations', () => {
beforeEach(() => {
revert = getCommands.__set__({
__dirname: path.join(LOCAL_NODE_MODULES, 'rnpm/src'),
});
mock(APP_JSON, NO_PLUGINS_JSON);
});
afterEach(() => revert());
it('list of the commands should be a non-empty array', () => {
mock(APP_JSON, NO_PLUGINS_JSON);
mock(LOCAL_RNPM_PJSON, SAMPLE_RNPM_JSON);
mock(SAMPLE_RNPM_PLUGIN, commands.single);
expect(getCommands().length).not.toBe(0);
expect(isArray(getCommands())).toBeTruthy();
});
it('should export one command', () => {
mock(LOCAL_RNPM_PJSON, SAMPLE_RNPM_JSON);
mock(SAMPLE_RNPM_PLUGIN, commands.single);
expect(getCommands().length).toEqual(1);
});
it('should export multiple commands', () => {
mock(LOCAL_RNPM_PJSON, SAMPLE_RNPM_JSON);
mock(SAMPLE_RNPM_PLUGIN, commands.multiple);
expect(getCommands().length).toEqual(2);
});
it('should export unique list of commands by name', () => {
mock(LOCAL_RNPM_PJSON, {
dependencies: {
[SAMPLE_RNPM_PLUGIN]: '*',
[`${SAMPLE_RNPM_PLUGIN}-2`]: '*',
},
});
mock(SAMPLE_RNPM_PLUGIN, commands.single);
mock(`${SAMPLE_RNPM_PLUGIN}-2`, commands.single);
expect(getCommands().length).toEqual(1);
});
});
describe('project plugins', () => {
/**
* In this test suite we only test project plugins thus we make sure
* `rnpm` package.json is properly mocked
*/
beforeEach(() => {
mock(LOCAL_RNPM_PJSON, NO_PLUGINS_JSON);
mock(GLOBAL_RNPM_PJSON, NO_PLUGINS_JSON);
});
afterEach(() => revert());
it('shoud load when installed locally', () => {
revert = getCommands.__set__({
__dirname: path.join(LOCAL_NODE_MODULES, 'rnpm/src'),
});
mock(APP_JSON, SAMPLE_RNPM_JSON);
mock(
path.join(LOCAL_NODE_MODULES, SAMPLE_RNPM_PLUGIN),
commands.single
);
expect(getCommands()[0]).toEqual(commands.single);
});
it('should load when installed globally', () => {
revert = getCommands.__set__({
__dirname: path.join(GLOBAL_NODE_MODULES, 'rnpm/src'),
});
mock(APP_JSON, SAMPLE_RNPM_JSON);
mock(
path.join(LOCAL_NODE_MODULES, SAMPLE_RNPM_PLUGIN),
commands.single
);
expect(getCommands()[0]).toEqual(commands.single);
});
});
describe('rnpm and project plugins', () => {
beforeEach(() => {
revert = getCommands.__set__({
__dirname: path.join(LOCAL_NODE_MODULES, 'rnpm/src'),
});
});
afterEach(() => revert());
it('should load concatenated list of plugins', () => {
mock(APP_JSON, SAMPLE_RNPM_JSON);
mock(LOCAL_RNPM_PJSON, {
dependencies: {
[`${SAMPLE_RNPM_PLUGIN}-2`]: '*',
},
});
mock(
path.join(LOCAL_NODE_MODULES, SAMPLE_RNPM_PLUGIN),
commands.multiple
);
mock(`${SAMPLE_RNPM_PLUGIN}-2`, commands.single);
expect(getCommands().length).toEqual(3);
});
});
});
@@ -0,0 +1,84 @@
jest.autoMockOff();
const findProject = require('../../src/config/ios/findProject');
const mockFs = require('mock-fs');
const projects = require('../fixtures/projects');
const ios = require('../fixtures/ios');
const userConfig = {};
describe('ios::findProject', () => {
it('should return path to xcodeproj if found', () => {
mockFs(projects.flat);
expect(findProject('')).not.toBe(null);
});
it('should return null if there\'re no projects', () => {
mockFs({ testDir: projects });
expect(findProject('')).toBe(null);
});
it('should return ios project regardless of its name', () => {
mockFs({ ios: ios.validTestName });
expect(findProject('')).not.toBe(null);
});
it('should ignore node_modules', () => {
mockFs({ node_modules: projects.flat });
expect(findProject('')).toBe(null);
});
it('should ignore Pods', () => {
mockFs({ Pods: projects.flat });
expect(findProject('')).toBe(null);
});
it('should ignore Pods inside `ios` folder', () => {
mockFs({
ios: {
Pods: projects.flat,
DemoApp: projects.flat.ios,
},
});
expect(findProject('')).toBe('ios/DemoApp/demoProject.xcodeproj');
});
it('should ignore xcodeproj from example folders', () => {
mockFs({
examples: projects.flat,
Examples: projects.flat,
example: projects.flat,
KeychainExample: projects.flat,
Zpp: projects.flat,
});
expect(findProject('').toLowerCase()).not.toContain('example');
});
it('should ignore xcodeproj from sample folders', () => {
mockFs({
samples: projects.flat,
Samples: projects.flat,
sample: projects.flat,
KeychainSample: projects.flat,
Zpp: projects.flat,
});
expect(findProject('').toLowerCase()).not.toContain('sample');
});
it('should ignore xcodeproj from test folders at any level', () => {
mockFs({
test: projects.flat,
IntegrationTests: projects.flat,
tests: projects.flat,
Zpp: {
tests: projects.flat,
src: projects.flat,
},
});
expect(findProject('').toLowerCase()).not.toContain('test');
});
afterEach(mockFs.restore);
});
@@ -0,0 +1,26 @@
jest.autoMockOff();
const getProjectConfig = require('../../src/config/ios').projectConfig;
const mockFs = require('mock-fs');
const projects = require('../fixtures/projects');
describe('ios::getProjectConfig', () => {
const userConfig = {};
beforeEach(() => mockFs({ testDir: projects }));
it('should return an object with ios project configuration', () => {
const folder = 'testDir/nested';
expect(getProjectConfig(folder, userConfig)).not.toBe(null);
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
});
it('should return `null` if ios project was not found', () => {
const folder = 'testDir/empty';
expect(getProjectConfig(folder, userConfig)).toBe(null);
});
afterEach(mockFs.restore);
});
@@ -0,0 +1,35 @@
var spawnError = false;
jest.setMock('child_process', {
spawn: () => ({
on: (ev, cb) => cb(spawnError),
}),
});
jest.dontMock('../src/makeCommand');
const makeCommand = require('../src/makeCommand');
describe('makeCommand', () => {
const command = makeCommand('echo');
it('should generate a function around shell command', () => {
expect(typeof command).toBe('function');
});
it('should throw an error if there\'s no callback provided', () => {
expect(command).toThrow();
});
it('should invoke a callback after command execution', () => {
const spy = jest.genMockFunction();
command(spy);
expect(spy.mock.calls.length).toBe(1);
});
it('should throw an error if spawn ended up with error', () => {
spawnError = true;
const cb = jest.genMockFunction();
expect(() => command(cb)).toThrow();
});
});
+11
View File
@@ -0,0 +1,11 @@
module.exports = [
{
func: require('./src/install'),
description: 'Install and link native dependencies',
name: 'install [packageName]',
}, {
func: require('./src/uninstall'),
description: 'Uninstall and unlink native dependencies',
name: 'uninstall [packageName]',
},
];
+26
View File
@@ -0,0 +1,26 @@
{
"name": "rnpm-plugin-install",
"version": "1.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rnpm/rnpm-plugin-install.git"
},
"author": "Alexey Kureev <kureev-mail@ya.ru> (https://github.com/Kureev)",
"contributors": [
"Alexey Kureev <kureev-mail@ya.ru> (https://github.com/Kureev)",
"Mike Grabowski <grabbou@gmail.com> (https://github.com/grabbou)"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/rnpm/rnpm-plugin-install/issues"
},
"homepage": "https://github.com/rnpm/rnpm-plugin-install#readme",
"dependencies": {
"npmlog": "^2.0.2"
}
}
+26
View File
@@ -0,0 +1,26 @@
const spawnSync = require('child_process').spawnSync;
const log = require('npmlog');
const spawnOpts = {
stdio: 'inherit',
stdin: 'inherit',
};
log.heading = 'rnpm-install';
module.exports = function install(config, args, callback) {
const name = args[0];
var res = spawnSync('npm', ['install', name, '--save'], spawnOpts);
if (res.status) {
process.exit(res.status);
}
res = spawnSync('rnpm', ['link', name], spawnOpts);
if (res.status) {
process.exit(res.status);
}
log.info(`Module ${name} has been successfully installed & linked`);
};
+26
View File
@@ -0,0 +1,26 @@
const spawnSync = require('child_process').spawnSync;
const log = require('npmlog');
const spawnOpts = {
stdio: 'inherit',
stdin: 'inherit',
};
log.heading = 'rnpm-install';
module.exports = function install(config, args, callback) {
const name = args[0];
var res = spawnSync('rnpm', ['unlink', name], spawnOpts);
if (res.status) {
process.exit(res.status);
}
res = spawnSync('npm', ['uninstall', name], spawnOpts);
if (res.status) {
process.exit(res.status);
}
log.info(`Module ${name} has been successfully uninstalled & unlinked`);
};
+9
View File
@@ -0,0 +1,9 @@
module.exports = [{
func: require('./src/link'),
description: 'Links all native dependencies',
name: 'link [packageName]',
}, {
func: require('./src/unlink'),
description: 'Unlink native dependency',
name: 'unlink <packageName>',
}];
+52
View File
@@ -0,0 +1,52 @@
{
"name": "rnpm-plugin-link",
"version": "1.7.4",
"description": "rnpm plugin that links native dependencies to your project",
"main": "index.js",
"scripts": {
"test": "eslint ./ && mocha ./test --recursive"
},
"keywords": [
"rnpm",
"react-native",
"react-native link"
],
"repository": {
"type": "git",
"url": "git+https://github.com/rnpm/rnpm.git"
},
"engines": {
"node": ">= 4.0.0"
},
"author": "Amazing React Native Community (https://github.com/facebook/react-native)",
"contributors": [
"Alexey Kureev <kureev-mail@ya.ru> (https://github.com/Kureev)",
"Mike Grabowski <grabbou@gmail.com> (https://github.com/grabbou)"
],
"bugs": {
"url": "https://github.com/rnpm/rnpm/issues"
},
"homepage": "https://github.com/rnpm/rnpm#readme",
"license": "MIT",
"dependencies": {
"fs-extra": "^0.26.2",
"glob": "^7.0.0",
"inquirer": "^0.12.0",
"lodash": "^3.10.1",
"mime": "^1.3.4",
"npmlog": "^2.0.0",
"plist": "^1.2.0",
"semver": "^5.1.0",
"to-camel-case": "^1.0.0",
"xcode": "^0.8.2"
},
"devDependencies": {
"babel-eslint": "^4.1.5",
"chai": "^3.4.1",
"eslint": "^1.9.0",
"mocha": "^2.3.4",
"mock-fs": "^3.5.0",
"mock-require": "^1.2.1",
"sinon": "^1.17.2"
}
}
@@ -0,0 +1,17 @@
const fs = require('fs-extra');
const path = require('path');
const groupFilesByType = require('../groupFilesByType');
/**
* Copies each file from an array of assets provided to targetPath directory
*
* For now, the only types of files that are handled are:
* - Fonts (otf, ttf) - copied to targetPath/fonts under original name
*/
module.exports = function copyAssetsAndroid(files, targetPath) {
const assets = groupFilesByType(files);
(assets.font || []).forEach(asset =>
fs.copySync(asset, path.join(targetPath, 'fonts', path.basename(asset)))
);
};
+8
View File
@@ -0,0 +1,8 @@
const fs = require('fs-extra');
exports.readFile = (file) =>
() => fs.readFileSync(file, 'utf8');
exports.writeFile = (file, content) => content ?
fs.writeFileSync(file, content, 'utf8') :
(c) => fs.writeFileSync(file, c, 'utf8');
@@ -0,0 +1,16 @@
const semver = require('semver');
const versions = ['0.20', '0.18', '0.17'];
module.exports = function getPrefix(rnVersion) {
const version = rnVersion.replace(/-.*/, '');
var prefix = 'patches/0.20';
versions.forEach((item, i) => {
const nextVersion = versions[i + 1];
if (semver.lt(version, item + '.0') && nextVersion) {
prefix = `patches/${nextVersion}`;
}
});
return prefix;
};
@@ -0,0 +1,8 @@
const fs = require('fs');
const makeBuildPatch = require(`./patches/makeBuildPatch`);
module.exports = function isInstalled(config, name) {
return fs
.readFileSync(config.buildGradlePath)
.indexOf(makeBuildPatch(name).patch) > -1;
};
@@ -0,0 +1,6 @@
module.exports = function makeImportPatch(packageImportPath) {
return {
pattern: 'import android.app.Activity;',
patch: '\n' + packageImportPath,
};
};
@@ -0,0 +1,10 @@
const applyParams = require('../applyParams');
module.exports = function makePackagePatch(packageInstance, params, prefix) {
const processedInstance = applyParams(packageInstance, params, prefix);
return {
pattern: '.addPackage(new MainReactPackage())',
patch: `\n .addPackage(${processedInstance})`,
};
};
@@ -0,0 +1,6 @@
module.exports = function makeImportPatch(packageImportPath) {
return {
pattern: 'import com.facebook.react.ReactActivity;',
patch: '\n' + packageImportPath,
};
};
@@ -0,0 +1,10 @@
const applyParams = require('../applyParams');
module.exports = function makePackagePatch(packageInstance, params, prefix) {
const processedInstance = applyParams(packageInstance, params, prefix);
return {
pattern: 'new MainReactPackage()',
patch: ',\n ' + processedInstance,
};
};
@@ -0,0 +1,6 @@
module.exports = function makeImportPatch(packageImportPath) {
return {
pattern: 'import com.facebook.react.ReactActivity;',
patch: '\n' + packageImportPath,
};
};
@@ -0,0 +1,10 @@
const applyParams = require('../applyParams');
module.exports = function makePackagePatch(packageInstance, params, prefix) {
const processedInstance = applyParams(packageInstance, params, prefix);
return {
pattern: 'new MainReactPackage()',
patch: ',\n ' + processedInstance,
};
};
@@ -0,0 +1,14 @@
const toCamelCase = require('to-camel-case');
module.exports = function applyParams(str, params, prefix) {
return str.replace(
/\$\{(\w+)\}/g,
(pattern, param) => {
const name = toCamelCase(prefix) + '_' + param;
return params[param]
? `this.getResources().getString(R.strings.${name})`
: null;
}
);
};
@@ -0,0 +1,8 @@
const fs = require('fs');
module.exports = function applyPatch(file, patch) {
fs.writeFileSync(file, fs
.readFileSync(file, 'utf8')
.replace(patch.pattern, match => `${match}${patch.patch}`)
);
};
@@ -0,0 +1,6 @@
module.exports = function makeBuildPatch(name) {
return {
pattern: /[^ \t]dependencies {\n/,
patch: ` compile project(':${name}')\n`,
};
};
@@ -0,0 +1,26 @@
const path = require('path');
const isWin = process.platform === 'win32';
module.exports = function makeSettingsPatch(name, androidConfig, projectConfig) {
var projectDir = path.relative(
path.dirname(projectConfig.settingsGradlePath),
androidConfig.sourceDir
);
/*
* Fix for Windows
* Backslashes is the escape character and will result in
* an invalid path in settings.gradle
* https://github.com/rnpm/rnpm/issues/113
*/
if (isWin) {
projectDir = projectDir.replace(/\\/g, '/');
}
return {
pattern: 'include \':app\'\n',
patch: `include ':${name}'\n` +
`project(':${name}').projectDir = ` +
`new File(rootProject.projectDir, '${projectDir}')\n`,
};
};
@@ -0,0 +1,14 @@
const toCamelCase = require('to-camel-case');
module.exports = function makeStringsPatch(params, prefix) {
const patch = Object.keys(params).map(param => {
const name = toCamelCase(prefix) + '_' + param;
return ' ' +
`<string moduleConfig="true" name="${name}">${params[param]}</string>`;
}).join('\n') + '\n';
return {
pattern: '<resources>\n',
patch: patch,
};
};
@@ -0,0 +1,8 @@
const fs = require('fs');
module.exports = function revokePatch(file, patch) {
fs.writeFileSync(file, fs
.readFileSync(file, 'utf8')
.replace(patch.patch, '')
);
};
@@ -0,0 +1,38 @@
const fs = require('fs');
const getReactVersion = require('../getReactNativeVersion');
const getPrefix = require('./getPrefix');
const applyPatch = require('./patches/applyPatch');
const makeStringsPatch = require('./patches/makeStringsPatch');
const makeSettingsPatch = require(`./patches/makeSettingsPatch`);
const makeBuildPatch = require(`./patches/makeBuildPatch`);
module.exports = function registerNativeAndroidModule(
name,
androidConfig,
params,
projectConfig
) {
const buildPatch = makeBuildPatch(name);
const prefix = getPrefix(getReactVersion(projectConfig.folder));
const makeImportPatch = require(`./${prefix}/makeImportPatch`);
const makePackagePatch = require(`./${prefix}/makePackagePatch`);
applyPatch(
projectConfig.settingsGradlePath,
makeSettingsPatch(name, androidConfig, projectConfig)
);
applyPatch(projectConfig.buildGradlePath, buildPatch);
applyPatch(projectConfig.stringsPath, makeStringsPatch(params, name));
applyPatch(
projectConfig.mainActivityPath,
makePackagePatch(androidConfig.packageInstance, params, name)
);
applyPatch(
projectConfig.mainActivityPath,
makeImportPatch(androidConfig.packageImportPath)
);
};
@@ -0,0 +1,20 @@
const fs = require('fs-extra');
const path = require('path');
const groupFilesByType = require('../groupFilesByType');
/**
* Copies each file from an array of assets provided to targetPath directory
*
* For now, the only types of files that are handled are:
* - Fonts (otf, ttf) - copied to targetPath/fonts under original name
*/
module.exports = function unlinkAssetsAndroid(files, targetPath) {
const grouped = groupFilesByType(files);
grouped.font.forEach((file) => {
const filename = path.basename(file);
if (fs.existsSync(filename)) {
fs.unlinkSync(path.join(targetPath, 'fonts', filename));
}
});
};
@@ -0,0 +1,47 @@
const fs = require('fs');
const getReactVersion = require('../getReactNativeVersion');
const getPrefix = require('./getPrefix');
const toCamelCase = require('to-camel-case');
const revokePatch = require('./patches/revokePatch');
const makeSettingsPatch = require('./patches/makeSettingsPatch');
const makeBuildPatch = require('./patches/makeBuildPatch');
const makeStringsPatch = require('./patches/makeStringsPatch');
module.exports = function unregisterNativeAndroidModule(
name,
androidConfig,
projectConfig
) {
const buildPatch = makeBuildPatch(name);
const prefix = getPrefix(getReactVersion(projectConfig.folder));
const makeImportPatch = require(`./${prefix}/makeImportPatch`);
const makePackagePatch = require(`./${prefix}/makePackagePatch`);
const strings = fs.readFileSync(projectConfig.stringsPath, 'utf8');
var params = {};
strings.replace(
/moduleConfig="true" name="(\w+)">(.*)</g,
(_, param, value) => {
params[param.slice(toCamelCase(name).length + 1)] = value;
}
);
revokePatch(
projectConfig.settingsGradlePath,
makeSettingsPatch(name, androidConfig, projectConfig)
);
revokePatch(projectConfig.buildGradlePath, buildPatch);
revokePatch(projectConfig.stringsPath, makeStringsPatch(params, name));
revokePatch(
projectConfig.mainActivityPath,
makePackagePatch(androidConfig.packageInstance, params, name)
);
revokePatch(
projectConfig.mainActivityPath,
makeImportPatch(androidConfig.packageImportPath)
);
};
@@ -0,0 +1,17 @@
/**
* Given an array of dependencies - it returns their RNPM config
* if they were valid.
*/
module.exports = function getDependencyConfig(config, deps) {
return deps.reduce((acc, name) => {
try {
return acc.concat({
config: config.getDependencyConfig(name),
name,
});
} catch (err) {
console.log(err);
return acc;
}
}, []);
};
@@ -0,0 +1,9 @@
const path = require('path');
/**
* Returns an array of dependencies that should be linked/checked.
*/
module.exports = function getProjectDependencies() {
const pjson = require(path.join(process.cwd(), './package.json'));
return Object.keys(pjson.dependencies || {}).filter(name => name !== 'react-native');
};
@@ -0,0 +1,5 @@
const path = require('path');
module.exports = (folder) => require(
path.join(folder, 'node_modules', 'react-native', 'package.json')
).version;
@@ -0,0 +1,27 @@
const groupBy = require('lodash').groupBy;
const mime = require('mime');
/**
* Since there are no officialy registered MIME types
* for ttf/otf yet http://www.iana.org/assignments/media-types/media-types.xhtml,
* we define two non-standard ones for the sake of parsing
*/
mime.define({
'font/opentype': ['otf'],
'font/truetype': ['ttf'],
});
/**
* Given an array of files, it groups it by it's type.
* Type of the file is inferred from it's mimetype based on the extension
* file ends up with. The returned value is an object with properties that
* correspond to the first part of the mimetype, e.g. images will be grouped
* under `image` key since the mimetype for them is `image/jpg` etc.
*
* Example:
* Given an array ['fonts/a.ttf', 'images/b.jpg'],
* the returned object will be: {font: ['fonts/a.ttf'], image: ['images/b.jpg']}
*/
module.exports = function groupFilesByType(assets) {
return groupBy(assets, type => mime.lookup(type).split('/')[0]);
};
@@ -0,0 +1,14 @@
const PbxFile = require('xcode/lib/pbxFile');
/**
* Given xcodeproj and filePath, it creates new file
* from path provided, adds it to the project
* and returns newly created instance of a file
*/
module.exports = function addFileToProject(project, filePath) {
const file = new PbxFile(filePath);
file.uuid = project.generateUuid();
file.fileRef = project.generateUuid();
project.addToPbxFileReferenceSection(file);
return file;
};
@@ -0,0 +1,13 @@
/**
* Given an array of xcodeproj libraries and pbxFile,
* it appends it to that group
*
* Important: That function mutates `libraries` and it's not pure.
* It's mainly due to limitations of `xcode` library.
*/
module.exports = function addProjectToLibraries(libraries, file) {
return libraries.children.push({
value: file.fileRef,
comment: file.basename,
});
};
@@ -0,0 +1,3 @@
module.exports = function addSharedLibraries(project, libraries) {
};
@@ -0,0 +1,5 @@
const mapHeaderSearchPaths = require('./mapHeaderSearchPaths');
module.exports = function addToHeaderSearchPaths(project, path) {
mapHeaderSearchPaths(project, searchPaths => searchPaths.concat(path));
};
+57
View File
@@ -0,0 +1,57 @@
const fs = require('fs-extra');
const path = require('path');
const xcode = require('xcode');
const log = require('npmlog');
const plistParser = require('plist');
const groupFilesByType = require('../groupFilesByType');
const createGroup = require('./createGroup');
const getPlist = require('./getPlist');
const getPlistPath = require('./getPlistPath');
/**
* This function works in a similar manner to its Android version,
* except it does not copy fonts but creates XCode Group references
*/
module.exports = function linkAssetsIOS(files, projectConfig) {
const project = xcode.project(projectConfig.pbxprojPath).parseSync();
const assets = groupFilesByType(files);
const plist = getPlist(project, projectConfig.sourceDir);
if (!plist) {
return log.error(
'ERRPLIST',
`Could not locate Info.plist. Check if your project has 'INFOPLIST_FILE' set properly`
);
}
if (!project.pbxGroupByName('Resources')) {
createGroup(project, 'Resources');
log.warn(
'ERRGROUP',
`Group 'Resources' does not exist in your XCode project. We have created it automatically for you.`
);
}
const fonts = (assets.font || [])
.map(asset =>
project.addResourceFile(
path.relative(projectConfig.sourceDir, asset),
{ target: project.getFirstTarget().uuid }
)
)
.filter(file => file) // xcode returns false if file is already there
.map(file => file.basename);
plist.UIAppFonts = (plist.UIAppFonts || []).concat(fonts);
fs.writeFileSync(
projectConfig.pbxprojPath,
project.writeSync()
);
fs.writeFileSync(
getPlistPath(project, projectConfig.sourceDir),
plistParser.build(plist)
);
};
@@ -0,0 +1,27 @@
const getGroup = require('./getGroup');
const hasGroup = (pbxGroup, name) => pbxGroup.children.find(group => group.comment === name);
/**
* Given project and path of the group, it deeply creates a given group
* making all outer groups if neccessary
*
* Returns newly created group
*/
module.exports = function createGroup(project, path) {
return path.split('/').reduce(
(group, name) => {
if (!hasGroup(group, name)) {
const uuid = project.pbxCreateGroup(name, '""');
group.children.push({
value: uuid,
comment: name,
});
}
return project.pbxGroupByName(name);
},
getGroup(project)
);
};
@@ -0,0 +1,18 @@
/**
* Gets build property from the main target build section
*
* It differs from the project.getBuildProperty exposed by xcode in the way that:
* - it only checks for build property in the main target `Debug` section
* - `xcode` library iterates over all build sections and because it misses
* an early return when property is found, it will return undefined/wrong value
* when there's another build section typically after the one you want to access
* without the property defined (e.g. CocoaPods sections appended to project
* miss INFOPLIST_FILE), see: https://github.com/alunny/node-xcode/blob/master/lib/pbxProject.js#L1765
*/
module.exports = function getBuildProperty(project, prop) {
const target = project.getFirstTarget().firstTarget;
const config = project.pbxXCConfigurationList()[target.buildConfigurationList];
const buildSection = project.pbxXCBuildConfigurationSection()[config.buildConfigurations[0].value];
return buildSection.buildSettings[prop];
};
+34
View File
@@ -0,0 +1,34 @@
const getFirstProject = (project) => project.getFirstProject().firstProject;
const findGroup = (group, name) => group.children.find(group => group.comment === name);
/**
* Returns group from .xcodeproj if one exists, null otherwise
*
* Unlike node-xcode `pbxGroupByName` - it does not return `first-matching`
* group if multiple groups with the same name exist
*
* If path is not provided, it returns top-level group
*/
module.exports = function getGroup(project, path) {
const firstProject = getFirstProject(project);
var group = project.getPBXGroupByKey(firstProject.mainGroup);
if (!path) {
return group;
}
for (var name of path.split('/')) {
var foundGroup = findGroup(group, name);
if (foundGroup) {
group = project.getPBXGroupByKey(foundGroup.value);
} else {
group = null;
break;
}
}
return group;
};
@@ -0,0 +1,52 @@
const path = require('path');
const union = require('lodash').union;
const last = require('lodash').last;
/**
* Given an array of directories, it returns the one that contains
* all the other directories in a given array inside it.
*
* Example:
* Given an array of directories: ['/Users/Kureev/a', '/Users/Kureev/b']
* the returned folder is `/Users/Kureev`
*
* Check `getHeaderSearchPath.spec.js` for more use-cases.
*/
const getOuterDirectory = (directories) =>
directories.reduce((topDir, currentDir) => {
const currentFolders = currentDir.split(path.sep);
const topMostFolders = topDir.split(path.sep);
if (currentFolders.length === topMostFolders.length
&& last(currentFolders) !== last(topMostFolders)) {
return currentFolders.slice(0, -1).join(path.sep);
}
return currentFolders.length < topMostFolders.length
? currentDir
: topDir;
});
/**
* Given an array of headers it returns search path so Xcode can resolve
* headers when referenced like below:
* ```
* #import "CodePush.h"
* ```
* If all files are located in one directory (directories.length === 1),
* we simply return a relative path to that location.
*
* Otherwise, we loop through them all to find the outer one that contains
* all the headers inside. That location is then returned with /** appended at
* the end so Xcode marks that location as `recursive` and will look inside
* every folder of it to locate correct headers.
*/
module.exports = function getHeaderSearchPath(sourceDir, headers) {
const directories = union(
headers.map(path.dirname)
);
return directories.length === 1
? `"$(SRCROOT)${path.sep}${path.relative(sourceDir, directories[0])}"`
: `"$(SRCROOT)${path.sep}${path.relative(sourceDir, getOuterDirectory(directories))}/**"`;
};
@@ -0,0 +1,18 @@
const glob = require('glob');
const path = require('path');
const GLOB_EXCLUDE_PATTERN = ['node_modules/**', 'Pods/**', 'Examples/**', 'examples/**'];
/**
* Given folder, it returns an array of all header files
* inside it, ignoring node_modules and examples
*/
module.exports = function getHeadersInFolder(folder) {
return glob
.sync('**/*.h', {
cwd: folder,
nodir: true,
ignore: GLOB_EXCLUDE_PATTERN,
})
.map(file => path.join(folder, file));
};
+20
View File
@@ -0,0 +1,20 @@
const plistParser = require('plist');
const getPlistPath = require('./getPlistPath');
const fs = require('fs');
/**
* Returns Info.plist located in the iOS project
*
* Returns `null` if INFOPLIST_FILE is not specified.
*/
module.exports = function getPlist(project, sourceDir) {
const plistPath = getPlistPath(project, sourceDir);
if (!plistPath || !fs.existsSync(plistPath)) {
return null;
}
return plistParser.parse(
fs.readFileSync(plistPath, 'utf-8')
);
};
@@ -0,0 +1,15 @@
const path = require('path');
const getBuildProperty = require('./getBuildProperty');
module.exports = function getPlistPath(project, sourceDir) {
const plistFile = getBuildProperty(project, 'INFOPLIST_FILE');
if (!plistFile) {
return null;
}
return path.join(
sourceDir,
plistFile.replace(/"/g, '').replace('$(SRCROOT)', '')
);
};
@@ -0,0 +1,12 @@
/**
* Given xcodeproj it returns list of products ending with
* .a extension, so that we know what elements add to target
* project static library
*/
module.exports = function getProducts(project) {
return project
.pbxGroupByName('Products')
.children
.map(c => c.comment)
.filter(c => c.indexOf('.a') > -1);
};
@@ -0,0 +1,10 @@
/**
* Given an array of libraries already imported and packageName that will be
* added, returns true or false depending on whether the library is already linked
* or not
*/
module.exports = function hasLibraryImported(libraries, packageName) {
return libraries.children
.filter(library => library.comment === packageName)
.length > 0;
};
@@ -0,0 +1,18 @@
const xcode = require('xcode');
const getGroup = require('./getGroup');
const hasLibraryImported = require('./hasLibraryImported');
/**
* Returns true if `xcodeproj` specified by dependencyConfig is present
* in a top level `libraryFolder`
*/
module.exports = function isInstalled(projectConfig, dependencyConfig) {
const project = xcode.project(projectConfig.pbxprojPath).parseSync();
const libraries = getGroup(project, projectConfig.libraryFolder);
if (!libraries) {
return false;
}
return hasLibraryImported(libraries, dependencyConfig.projectName);
};
@@ -0,0 +1,36 @@
/**
* Given Xcode project and path, iterate over all build configurations
* and execute func with HEADER_SEARCH_PATHS from current section
*
* We cannot use builtin addToHeaderSearchPaths method since react-native init does not
* use $(TARGET_NAME) for PRODUCT_NAME, but sets it manually so that method will skip
* that target.
*
* To workaround that issue and make it more bullet-proof for different names,
* we iterate over all configurations and look if React is already there. If it is,
* we assume we want to modify that section either
*
* Important: That function mutates `buildSettings` and it's not pure thus you should
* not rely on its return value
*/
module.exports = function headerSearchPathIter(project, func) {
const config = project.pbxXCBuildConfigurationSection();
Object
.keys(config)
.filter(ref => ref.indexOf('_comment') === -1)
.forEach(ref => {
const buildSettings = config[ref].buildSettings;
const shouldVisitBuildSettings = (
Array.isArray(buildSettings.HEADER_SEARCH_PATHS) ?
buildSettings.HEADER_SEARCH_PATHS :
[]
)
.filter(path => path.indexOf('react-native/React/**'))
.length > 0;
if (shouldVisitBuildSettings) {
buildSettings.HEADER_SEARCH_PATHS = func(buildSettings.HEADER_SEARCH_PATHS);
}
});
};
@@ -0,0 +1,67 @@
const xcode = require('xcode');
const fs = require('fs');
const path = require('path');
const log = require('npmlog');
const addToHeaderSearchPaths = require('./addToHeaderSearchPaths');
const getHeadersInFolder = require('./getHeadersInFolder');
const getHeaderSearchPath = require('./getHeaderSearchPath');
const getProducts = require('./getProducts');
const createGroup = require('./createGroup');
const hasLibraryImported = require('./hasLibraryImported');
const addFileToProject = require('./addFileToProject');
const addProjectToLibraries = require('./addProjectToLibraries');
const addSharedLibraries = require('./addSharedLibraries');
const isEmpty = require('lodash').isEmpty;
const getGroup = require('./getGroup');
/**
* Register native module IOS adds given dependency to project by adding
* its xcodeproj to project libraries as well as attaching static library
* to the first target (the main one)
*
* If library is already linked, this action is a no-op.
*/
module.exports = function registerNativeModuleIOS(dependencyConfig, projectConfig) {
const project = xcode.project(projectConfig.pbxprojPath).parseSync();
const dependencyProject = xcode.project(dependencyConfig.pbxprojPath).parseSync();
var libraries = getGroup(project, projectConfig.libraryFolder);
if (!libraries) {
libraries = createGroup(project, projectConfig.libraryFolder);
log.warn(
'ERRGROUP',
`Group ${projectConfig.libraryFolder} does not exist in your XCode project. We have created it automatically for you.`
);
}
const file = addFileToProject(
project,
path.relative(projectConfig.sourceDir, dependencyConfig.projectPath)
);
addProjectToLibraries(libraries, file);
getProducts(dependencyProject).forEach(product => {
project.addStaticLibrary(product, {
target: project.getFirstTarget().uuid,
});
});
addSharedLibraries(project, dependencyConfig.sharedLibraries);
const headers = getHeadersInFolder(dependencyConfig.folder);
if (!isEmpty(headers)) {
addToHeaderSearchPaths(
project,
getHeaderSearchPath(projectConfig.sourceDir, headers)
);
}
fs.writeFileSync(
projectConfig.pbxprojPath,
project.writeSync()
);
};
@@ -0,0 +1,10 @@
const mapHeaderSearchPaths = require('./mapHeaderSearchPaths');
/**
* Given Xcode project and absolute path, it makes sure there are no headers referring to it
*/
module.exports = function addToHeaderSearchPaths(project, path) {
mapHeaderSearchPaths(project,
searchPaths => searchPaths.filter(searchPath => searchPath !== path)
);
};
@@ -0,0 +1,16 @@
/**
* For all files that are created and referenced from another `.xcodeproj` -
* a new PBXItemContainerProxy is created that contains `containerPortal` value
* which equals to xcodeproj file.uuid from PBXFileReference section.
*/
module.exports = function removeFromPbxItemContainerProxySection(project, file) {
const section = project.hash.project.objects.PBXContainerItemProxy;
for (var key of Object.keys(section)) {
if (section[key].containerPortal === file.uuid) {
delete section[key];
}
}
return;
};
@@ -0,0 +1,15 @@
/**
* Every file added to the project from another project is attached to
* `PBXItemContainerProxy` through `PBXReferenceProxy`.
*/
module.exports = function removeFromPbxReferenceProxySection(project, file) {
const section = project.hash.project.objects.PBXReferenceProxy;
for (var key of Object.keys(section)) {
if (section[key].path === file.basename) {
delete section[key];
}
}
return;
};
@@ -0,0 +1,26 @@
/**
* For each file (.xcodeproj), there's an entry in `projectReferences` created
* that has two entries - `ProjectRef` - reference to a file.uuid and
* `ProductGroup` - uuid of a Products group.
*
* When projectReference is found - it's deleted and the removed value is returned
* so that ProductGroup in PBXGroup section can be removed as well.
*
* Otherwise returns null
*/
module.exports = function removeFromProjectReferences(project, file) {
const firstProject = project.getFirstProject().firstProject;
const projectRef = firstProject.projectReferences.find(item => item.ProjectRef === file.uuid);
if (!projectRef) {
return null;
}
firstProject.projectReferences.splice(
firstProject.projectReferences.indexOf(projectRef),
1
);
return projectRef;
};
@@ -0,0 +1,21 @@
const PbxFile = require('xcode/lib/pbxFile');
const removeFromPbxReferenceProxySection = require('./removeFromPbxReferenceProxySection');
/**
* Removes file from static libraries
*
* Similar to `node-xcode` addStaticLibrary
*/
module.exports = function removeFromStaticLibraries(project, path, opts) {
const file = new PbxFile(path);
file.target = opts ? opts.target : undefined;
project.removeFromPbxFileReferenceSection(file);
project.removeFromPbxBuildFileSection(file);
project.removeFromPbxFrameworksBuildPhase(file);
project.removeFromLibrarySearchPaths(file);
removeFromPbxReferenceProxySection(project, file);
return file;
};
@@ -0,0 +1,11 @@
module.exports = function removeProductGroup(project, productGroupId) {
const section = project.hash.project.objects.PBXGroup;
for (var key of Object.keys(section)) {
if (key === productGroupId) {
delete section[key];
}
}
return;
};
@@ -0,0 +1,12 @@
/**
* Given an array of xcodeproj libraries and pbxFile,
* it removes it from that group by comparing basenames
*
* Important: That function mutates `libraries` and it's not pure.
* It's mainly due to limitations of `xcode` library.
*/
module.exports = function removeProjectFromLibraries(libraries, file) {
libraries.children = libraries.children.filter(library =>
library.comment !== file.basename
);
};
@@ -0,0 +1,26 @@
const PbxFile = require('xcode/lib/pbxFile');
const removeFromPbxItemContainerProxySection = require('./removeFromPbxItemContainerProxySection');
const removeFromProjectReferences = require('./removeFromProjectReferences');
const removeProductGroup = require('./removeProductGroup');
/**
* Given xcodeproj and filePath, it creates new file
* from path provided and removes it. That operation is required since
* underlying method requires PbxFile instance to be passed (it does not
* have to have uuid or fileRef defined since it will do equality check
* by path)
*
* Returns removed file (that one will have UUID)
*/
module.exports = function removeProjectFromProject(project, filePath) {
const file = project.removeFromPbxFileReferenceSection(new PbxFile(filePath));
const projectRef = removeFromProjectReferences(project, file);
if (projectRef) {
removeProductGroup(project, projectRef.ProductGroup);
}
removeFromPbxItemContainerProxySection(project, file);
return file;
};
@@ -0,0 +1,3 @@
module.exports = function removeSharedLibraries(project, libraries) {
};
@@ -0,0 +1,54 @@
const fs = require('fs-extra');
const path = require('path');
const xcode = require('xcode');
const log = require('npmlog');
const plistParser = require('plist');
const groupFilesByType = require('../groupFilesByType');
const getPlist = require('./getPlist');
const getPlistPath = require('./getPlistPath');
const difference = require('lodash').difference;
/**
* Unlinks assets from iOS project. Removes references for fonts from `Info.plist`
* fonts provided by application and from `Resources` group
*/
module.exports = function unlinkAssetsIOS(files, projectConfig) {
const project = xcode.project(projectConfig.pbxprojPath).parseSync();
const assets = groupFilesByType(files);
const plist = getPlist(project, projectConfig.sourceDir);
if (!plist) {
return log.error(
'ERRPLIST',
`Could not locate Info.plist file. Check if your project has 'INFOPLIST_FILE' set properly`
);
}
if (!project.pbxGroupByName('Resources')) {
return log.error(
'ERRGROUP',
`Group 'Resources' does not exist in your XCode project. There is nothing to unlink.`
);
}
const fonts = (assets.font || [])
.map(asset =>
project.removeResourceFile(
path.relative(projectConfig.sourceDir, asset),
{ target: project.getFirstTarget().uuid }
)
)
.map(file => file.basename);
plist.UIAppFonts = difference(plist.UIAppFonts || [], fonts);
fs.writeFileSync(
projectConfig.pbxprojPath,
project.writeSync()
);
fs.writeFileSync(
getPlistPath(project, projectConfig.sourceDir),
plistParser.build(plist)
);
};
@@ -0,0 +1,54 @@
const xcode = require('xcode');
const path = require('path');
const fs = require('fs');
const getProducts = require('./getProducts');
const getHeadersInFolder = require('./getHeadersInFolder');
const isEmpty = require('lodash').isEmpty;
const getHeaderSearchPath = require('./getHeaderSearchPath');
const removeProjectFromProject = require('./removeProjectFromProject');
const removeProjectFromLibraries = require('./removeProjectFromLibraries');
const removeFromStaticLibraries = require('./removeFromStaticLibraries');
const removeFromHeaderSearchPaths = require('./removeFromHeaderSearchPaths');
const removeSharedLibraries = require('./addSharedLibraries');
const getGroup = require('./getGroup');
/**
* Unregister native module IOS
*
* If library is already unlinked, this action is a no-op.
*/
module.exports = function unregisterNativeModule(dependencyConfig, projectConfig) {
const project = xcode.project(projectConfig.pbxprojPath).parseSync();
const dependencyProject = xcode.project(dependencyConfig.pbxprojPath).parseSync();
const libraries = getGroup(project, projectConfig.libraryFolder);
const file = removeProjectFromProject(
project,
path.relative(projectConfig.sourceDir, dependencyConfig.projectPath)
);
removeProjectFromLibraries(libraries, file);
getProducts(dependencyProject).forEach(product => {
removeFromStaticLibraries(project, product, {
target: project.getFirstTarget().uuid,
});
});
removeSharedLibraries(project, dependencyConfig.sharedLibraries);
const headers = getHeadersInFolder(dependencyConfig.folder);
if (!isEmpty(headers)) {
removeFromHeaderSearchPaths(
project,
getHeaderSearchPath(projectConfig.sourceDir, headers)
);
}
fs.writeFileSync(
projectConfig.pbxprojPath,
project.writeSync()
);
};
+136
View File
@@ -0,0 +1,136 @@
const log = require('npmlog');
const path = require('path');
const uniq = require('lodash').uniq;
const flatten = require('lodash').flatten;
const pkg = require('../package.json');
const isEmpty = require('lodash').isEmpty;
const promiseWaterfall = require('./promiseWaterfall');
const registerDependencyAndroid = require('./android/registerNativeModule');
const registerDependencyIOS = require('./ios/registerNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const copyAssetsAndroid = require('./android/copyAssets');
const copyAssetsIOS = require('./ios/copyAssets');
const getProjectDependencies = require('./getProjectDependencies');
const getDependencyConfig = require('./getDependencyConfig');
const pollParams = require('./pollParams');
log.heading = 'rnpm-link';
const commandStub = (cb) => cb();
const dedupeAssets = (assets) => uniq(assets, asset => path.basename(asset));
const promisify = (func) => new Promise((resolve, reject) =>
func((err, res) => err ? reject(err) : resolve(res))
);
const linkDependencyAndroid = (androidProject, dependency) => {
if (!androidProject || !dependency.config.android) {
return null;
}
const isInstalled = isInstalledAndroid(androidProject, dependency.name);
if (isInstalled) {
log.info(`Android module ${dependency.name} is already linked`);
return null;
}
return pollParams(dependency.config.params).then(params => {
log.info(`Linking ${dependency.name} android dependency`);
registerDependencyAndroid(
dependency.name,
dependency.config.android,
params,
androidProject
);
log.info(`Android module ${dependency.name} has been successfully linked`);
});
};
const linkDependencyIOS = (iOSProject, dependency) => {
if (!iOSProject || !dependency.config.ios) {
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios);
if (isInstalled) {
log.info(`iOS module ${dependency.name} is already linked`);
return;
}
log.info(`Linking ${dependency.name} ios dependency`);
registerDependencyIOS(dependency.config.ios, iOSProject);
log.info(`iOS module ${dependency.name} has been successfully linked`);
};
const linkAssets = (project, assets) => {
if (isEmpty(assets)) {
return;
}
if (project.ios) {
log.info('Linking assets to ios project');
copyAssetsIOS(assets, project.ios);
}
if (project.android) {
log.info('Linking assets to android project');
copyAssetsAndroid(assets, project.android.assetsPath);
}
log.info(`Assets has been successfully linked to your project`);
};
/**
* Updates project and linkes all dependencies to it
*
* If optional argument [packageName] is provided, it's the only one that's checked
*/
module.exports = function link(config, args) {
var project;
try {
project = config.getProjectConfig();
} catch (err) {
log.error(
'ERRPACKAGEJSON',
'No package found. Are you sure it\'s a React Native project?'
);
return Promise.reject(err);
}
const packageName = args[0];
const dependencies = getDependencyConfig(
config,
packageName ? [packageName] : getProjectDependencies()
);
const assets = dedupeAssets(dependencies.reduce(
(assets, dependency) => assets.concat(dependency.config.assets),
project.assets
));
const tasks = flatten(dependencies.map(dependency => [
() => promisify(dependency.config.commands.prelink || commandStub),
() => linkDependencyAndroid(project.android, dependency),
() => linkDependencyIOS(project.ios, dependency),
() => promisify(dependency.config.commands.postlink || commandStub),
]));
tasks.push(() => linkAssets(project, assets));
return promiseWaterfall(tasks).catch(err => {
log.error(
`It seems something went wrong while linking. Error: ${err.message} \n`
+ `Please file an issue here: ${pkg.bugs.url}`
);
throw err;
});
};
+9
View File
@@ -0,0 +1,9 @@
var inquirer = require('inquirer');
module.exports = (questions) => new Promise((resolve, reject) => {
if (!questions) {
return resolve({});
}
inquirer.prompt(questions, resolve);
});
@@ -0,0 +1,14 @@
/**
* Given an array of promise creators, executes them in a sequence.
*
* If any of the promises in the chain fails, all subsequent promises
* will be skipped
*
* Returns the value last promise from a sequence resolved
*/
module.exports = function promiseWaterfall(tasks) {
return tasks.reduce(
(prevTaskPromise, task) => prevTaskPromise.then(task),
Promise.resolve()
);
};
+117
View File
@@ -0,0 +1,117 @@
const path = require('path');
const log = require('npmlog');
const getProjectDependencies = require('./getProjectDependencies');
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const unlinkAssetsAndroid = require('./android/unlinkAssets');
const unlinkAssetsIOS = require('./ios/unlinkAssets');
const getDependencyConfig = require('./getDependencyConfig');
const difference = require('lodash').difference;
const isEmpty = require('lodash').isEmpty;
const flatten = require('lodash').flatten;
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 unlinkDependencyIOS = (iOSProject, dependency, packageName) => {
if (!iOSProject || !dependency.ios) {
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.ios);
if (!isInstalled) {
log.info(`iOS module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} ios dependency`);
unregisterDependencyIOS(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
*/
module.exports = function unlink(config, args) {
const packageName = args[0];
var project;
var dependency;
try {
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);
}
unlinkDependencyAndroid(project.android, dependency, packageName);
unlinkDependencyIOS(project.ios, dependency, packageName);
const allDependencies = getDependencyConfig(config, getProjectDependencies());
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`
);
return Promise.resolve();
};
@@ -0,0 +1,28 @@
const chai = require('chai');
const expect = chai.expect;
const mock = require('mock-fs');
const fs = require('fs');
const path = require('path');
const isInstalled = require('../../src/android/isInstalled');
const projectConfig = {
buildGradlePath: 'build.gradle',
};
describe('android::isInstalled', () => {
before(() => mock({
'build.gradle': fs.readFileSync(
path.join(__dirname, '../fixtures/android/patchedBuild.gradle')
),
}));
it('should return true when project is already in build.gradle', () =>
expect(isInstalled(projectConfig, 'test')).to.be.true
);
it('should return false when project is not in build.gradle', () =>
expect(isInstalled(projectConfig, 'test2')).to.be.false
);
after(mock.restore);
});

Some files were not shown because too many files have changed in this diff Show More