Compare commits

...
Author SHA1 Message Date
Héctor Ramos 96ac277cca [Website] Load React Native Web Player from MaxCDN. 2016-09-23 15:43:58 -07:00
Evgueni NavernioukandJames Ide 7a32f77f73 Minor doc improvements in Debugging.md (#9276)
Minor clarity and styling improvements for Debugging.md for the "Stetho" section
2016-08-07 23:43:10 -07:00
Steven BapagaandJames Ide 9864c3a0df Change getDefaultProps to ES7 property initialiazers for default props (#9241)
and add PropType validation

* getDefaultProps is not available in ES6 class style declaration
2016-08-07 21:42:05 -07:00
Joel MarceyandGitHub e3c81fa5ee Merge pull request #9238 from hramos/0.31-stable
Cherry-pick #9149 into 0.31-stable branch
2016-08-05 07:58:31 -07:00
Héctor Ramos cadc727854 Update Edit on GitHub links to point to master
Summary:
We've been getting a lot of documentation PRs opened against `0.29-stable`, `0.30-stable`, and so on, instead of `master`. This is because our doc site is also based on RN release cuts, so clicking on the "Edit on GitHub" links on a document will take you to the markdown source for that release branch instead of the latest doc on `master`.

See #9095 for an example of such a PR.

In this PR we edit the link to say View on GitHub. Though it may not prevent PRs from being opened against a release branch, removing the "Edit" CTA may help in this regard.
Closes https://github.com/facebook/react-native/pull/9149

Differential Revision: D3664368

Pulled By: vjeux

fbshipit-source-id: 395c0813f736bfbe1be4b4fb1182f9060169365d
2016-08-05 07:18:52 -07:00
Mike Grabowski a738b5157e [0.31.0] Bump version numbers 2016-08-05 11:34:03 +03:00
Mike Grabowski 34294a1c7c Fix corrupted options() call for link and other commands
Summary:
The issue here is that sometimes `this.options()` is not `[options]` but contains different stuff, esp. if your command accepts arguments.

This commit reverts its original behaviour and passes custom `examples` property that we use instead.

Tested, can be shipped right away.

Differential Revision: D3662179

fbshipit-source-id: 4b2af3487464d46e2007388230e675ce3575f797
2016-08-05 11:08:25 +03:00
Mike Grabowski 15238fb139 Fixes #9168
Summary:
Imports missing commit from the rnpm master.
Closes https://github.com/facebook/react-native/pull/9179

Differential Revision: D3661909

fbshipit-source-id: 23ebd3b96f236ab140f91eb4ed9f456d7c925027
2016-08-05 11:08:10 +03:00
Tom HaydenandMike Grabowski f9b78d111a reset-cache flag no longer needs boolean
Summary:
When compiling 0.31-rc1 was having issues with xcode running the package script and failing (ie not running and returning the --help info) Was due to this - simply changing the flag to --reset-cache instead of --reset-cache true worked miracles.

**Test plan (required)**

Ran packager without and runs but doesn't build the package, runs with small change and works now. Can copy paste output if you want but it's pretty super verbose for this small change.
Closes https://github.com/facebook/react-native/pull/9177

Differential Revision: D3661831

fbshipit-source-id: 3cebc543806b8fe3e413f83c59c9fb74e5e078f4
2016-08-05 11:07:55 +03:00
Eric RozellandMike Grabowski 73b24acf4c Fixes command validity check for local-cli.
Summary:
Prior to the RNPM integration, command names would match the second argument passed to the react-native-cli. Now the command names contain some usage information (such as non-flag CLI arguments). This commit splits the name of the commands and checks that against the second command line argument.
Closes https://github.com/facebook/react-native/pull/9171

Differential Revision: D3661799

fbshipit-source-id: dc2aa9cd9e69dde3c5b877aeb77a17c4f6427075
2016-08-05 11:07:42 +03:00
Eric RozellandMike Grabowski 1e5c46df87 Flattens RNPM user commands from plugin modules
Summary:
RNPM plugins may have ship multiple commands as extensions (e.g., https://github.com/rnpm/rnpm-plugin-link/blob/master/index.js#L1)

The consumer of the user commands (https://github.com/facebook/react-native/blob/master/local-cli/commands.js#L67) expects a flat list of commands, which used to be flattened here https://github.com/rnpm/rnpm/blob/master/bin/cli#L35 in RNPM.

This commit simply flattens the (possible) set of commands coming out of a user plugin.
Closes https://github.com/facebook/react-native/pull/9170

Differential Revision: D3661809

fbshipit-source-id: 72107745e53bf63b47a297eae546ed4a69c57008
2016-08-05 11:07:27 +03:00
Daniel BraunandMike Grabowski b957ea4d17 --reset-cache no longer accept a boolean parameter.
Summary:
The gradle :app:bundleReleaseJsAndAssets task is currently broken on master.
Gradle attaches a boolean parameter to --reset-cache, while it no longer accepts it.

Related:
* https://github.com/facebook/react-native/blob/master/local-cli/bundle/bundleCommandLineArgs.js#L52
* #9134
* https://github.com/facebook/react-native/blob/master/local-cli/bundle/bundleCommandLineArgs.js#L52
Closes https://github.com/facebook/react-native/pull/9163

Differential Revision: D3659300

fbshipit-source-id: 798f211407ee279e9adc98948e94b212da84555a
2016-08-05 11:07:21 +03:00
Kureev AlexeyandMike Grabowski 4a0b201334 Fix scoped modules
Summary:
see https://github.com/facebook/react-native/issues/8623

**Background:**
Back in a days, one buggy patch sneaked into rnpm master. It was intended to fix `react-native-fbsdk` installation for version 0.1.0 (not required in version 0.3.0). Nowadays, we see that this patch break scoped modules `like/this`, so this PR fixes it (basically, reverting the patch).

**Test plan (required)**
- [x] Try to link unscoped react-native package
- [x] Try to link scoped react-native package
**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests).
Closes https://github.com/facebook/react-native/pull/9118

Differential Revision: D3648488

fbshipit-source-id: 1891086ac79a89c888631592c966a8b33fd9c9fd
2016-08-05 11:07:13 +03:00
Mike Grabowski 5c6c7a2b40 [0.31.0-rc.1] Bump version numbers 2016-08-01 07:24:48 +02:00
Mike Grabowski 86cbaedb7a Merge rnpm cli into react-native
Summary:
This is an initial step of rewriting the CLI interface to use `rnpm` one (commander, plugins etc.).

It's scope is to move all existing commands to use rnpm CLI interface, so that we get plugins, flags and our existing ecosystem working out of the box.

<s>This is still WIP and some of the commands are left commented out.</s>

For the `config` of `rnpm` (functions get info about project and dependency), <s>I am thinking we can merge them with</s> we decided to merge it with [`default.config.js`](https://github.com/grabbou/react-native/blob/e57683e420210749a5a6b802b4e70adb69675786/local-cli/default.config.js#L33), so they are available on the `new Config()` [instance](https://github.com/grabbou/react-native/blob/e57683e420210749a5a6b802b4e70adb69675786/local-cli/cliEntry.js#L59) (which means we don't have to change anything and current plugins, like runIOS and runAndroid can just start using it [w/o depending on any extra argument](https://github.com/grabbou/react-native/blob/e57683e420210749a5a6b802b4e
Closes https://github.com/facebook/react-native/pull/7899

Differential Revision: D3613193

Pulled By: bestander

fbshipit-source-id: 09a072f3b21e5239dfcd8da88a205bd28dc5d037
2016-08-01 07:13:49 +02:00
Marc ShillingandMike Grabowski f5bdd52ddc Make getCurrentActivity public on ReactContext
Summary:
Addresses #8661
Closes https://github.com/facebook/react-native/pull/9071

Differential Revision: D3641285

Pulled By: foghina

fbshipit-source-id: dede86743efddc33b6ead053e805770fc213685c
2016-08-01 07:11:27 +02:00
Nathan AzariaandMike Grabowski 07a970195d Fix RCTImageLoader's priority
Summary:
There is a conflict between RCTImageLoader and RCTAssetsLibraryRequestHandler on handling images, which was caused by making RCTPhotoLibraryImageLoader able to handle assets library requests as well. This gives more priority to RCTImageLoader instead.
Fixes #9031.

Reviewed By: mmmulani

Differential Revision: D3627451

fbshipit-source-id: 7ffd2c66f43ce1479c9a117768fb2d29f9d0dc08
2016-08-01 07:11:19 +02:00
SokovikovandMike Grabowski c5d1ae0144 change lineBreakMode to ellipsizeMode
Summary:
lineBreakMode only in rc so I think we can replace property without any deprecation warnings. satya164
Closes https://github.com/facebook/react-native/pull/9008

Differential Revision: D3614901

fbshipit-source-id: 724227c0a89192825a24850b930b80884571a51f
2016-08-01 07:11:03 +02:00
James IdeandMike Grabowski a6d0f27ecf Create a CSSLayout subspec that preserves the header directories
Summary:
RN imports CSSLayout files like so: `#import <CSSLayout/CSSLayout.h>`, so we need to tell CocoaPods to preserve the directory structure for CSSLayout files. This is done with the header_mappings_dir, but we want it to apply just to CSSLayout's files, hence the subspec.
Closes https://github.com/facebook/react-native/pull/9015

Differential Revision: D3621727

fbshipit-source-id: fb3603a0820ca3d6502813204ff215cf160b1955
2016-08-01 07:10:52 +02:00
Mike Grabowski 9958afbcec [0.31.0-rc.0] Bump version numbers 2016-07-21 09:51:56 +02:00
152 changed files with 1330 additions and 1502 deletions
-2
View File
@@ -2,8 +2,6 @@ Thanks for submitting a pull request! Please provide enough information so that
> **Unless you are a React Native release maintainer and cherry-picking an *existing* commit into a current release, ensure your pull request is targeting the `master` React Native branch.**
(You can skip this if you're fixing a typo or adding an app to the Showcase.)
Explain the **motivation** for making this change. What existing problem does the pull request solve?
Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.
+1 -1
View File
@@ -21,7 +21,7 @@ The core team will be monitoring for pull requests. When we get one, we'll run s
1. Fork the repo and create your branch from `master`.
2. **Describe your test plan in your commit.** If you've added code that should be tested, add tests!
3. If you've changed APIs, update the documentation.
4. If you've updated the docs, verify the website locally and submit screenshots if applicable
4. If you've updated the docs, verify the website locally and submit screenshots if applicable.
```
$ cd website
@@ -400,14 +400,14 @@ var TextExample = React.createClass({
Demo text shadow
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="Line break mode">
<UIExplorerBlock title="Ellipsize mode">
<Text numberOfLines={1}>
This very long text should be truncated with dots in the end.
</Text>
<Text lineBreakMode="middle" numberOfLines={1}>
<Text ellipsizeMode="middle" numberOfLines={1}>
This very long text should be truncated with dots in the middle.
</Text>
<Text lineBreakMode="head" numberOfLines={1}>
<Text ellipsizeMode="head" numberOfLines={1}>
This very long text should be truncated with dots in the beginning.
</Text>
</UIExplorerBlock>
+4 -4
View File
@@ -445,20 +445,20 @@ exports.examples = [
);
},
}, {
title: 'Line break mode',
title: 'Ellipsize mode',
render: function() {
return (
<View>
<Text numberOfLines={1}>
This very long text should be truncated with dots in the end.
</Text>
<Text lineBreakMode="middle" numberOfLines={1}>
<Text ellipsizeMode="middle" numberOfLines={1}>
This very long text should be truncated with dots in the middle.
</Text>
<Text lineBreakMode="head" numberOfLines={1}>
<Text ellipsizeMode="head" numberOfLines={1}>
This very long text should be truncated with dots in the beginning.
</Text>
<Text lineBreakMode="clip" numberOfLines={1}>
<Text ellipsizeMode="clip" numberOfLines={1}>
This very looooooooooooooooooooooooooooong text should be clipped.
</Text>
</View>
+5
View File
@@ -86,6 +86,11 @@ RCT_EXPORT_MODULE()
[[NSNotificationCenter defaultCenter] removeObserver:_decodedImageCache];
}
- (float)handlerPriority
{
return 1;
}
- (id<RCTImageURLLoader>)imageURLLoaderForURL:(NSURL *)URL
{
if (!_maxConcurrentLoadingTasks) {
+1 -1
View File
@@ -24,7 +24,7 @@ extern NSString *const RCTReactTagAttributeName;
@property (nonatomic, assign) CGFloat letterSpacing;
@property (nonatomic, assign) CGFloat lineHeight;
@property (nonatomic, assign) NSUInteger numberOfLines;
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;
@property (nonatomic, assign) NSLineBreakMode ellipsizeMode;
@property (nonatomic, assign) CGSize shadowOffset;
@property (nonatomic, assign) NSTextAlignment textAlign;
@property (nonatomic, assign) NSWritingDirection writingDirection;
+2 -2
View File
@@ -190,7 +190,7 @@ static CSSSize RCTMeasure(void *context, float width, CSSMeasureMode widthMode,
textContainer.lineFragmentPadding = 0.0;
if (_numberOfLines > 0) {
textContainer.lineBreakMode = _lineBreakMode;
textContainer.lineBreakMode = _ellipsizeMode;
} else {
textContainer.lineBreakMode = NSLineBreakByClipping;
}
@@ -470,7 +470,7 @@ RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL)
RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat)
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat)
RCT_TEXT_PROPERTY(NumberOfLines, _numberOfLines, NSUInteger)
RCT_TEXT_PROPERTY(LineBreakMode, _lineBreakMode, NSLineBreakMode)
RCT_TEXT_PROPERTY(EllipsizeMode, _ellipsizeMode, NSLineBreakMode)
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment)
RCT_TEXT_PROPERTY(TextDecorationColor, _textDecorationColor, UIColor *);
RCT_TEXT_PROPERTY(TextDecorationLine, _textDecorationLine, RCTTextDecorationLineType);
+1 -1
View File
@@ -64,7 +64,7 @@ RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(numberOfLines, NSUInteger)
RCT_EXPORT_SHADOW_PROPERTY(lineBreakMode, NSLineBreakMode)
RCT_EXPORT_SHADOW_PROPERTY(ellipsizeMode, NSLineBreakMode)
RCT_EXPORT_SHADOW_PROPERTY(textAlign, NSTextAlignment)
RCT_EXPORT_SHADOW_PROPERTY(textDecorationStyle, NSUnderlineStyle)
RCT_EXPORT_SHADOW_PROPERTY(textDecorationColor, UIColor)
+5 -5
View File
@@ -29,7 +29,7 @@ const viewConfig = {
validAttributes: merge(ReactNativeViewAttributes.UIView, {
isHighlighted: true,
numberOfLines: true,
lineBreakMode: true,
ellipsizeMode: true,
allowFontScaling: true,
selectable: true,
}),
@@ -90,7 +90,7 @@ const viewConfig = {
const Text = React.createClass({
propTypes: {
/**
* Line Break mode. This can be one of the following values:
* This can be one of the following values:
*
* - `head` - The line is displayed so that the end fits in the container and the missing text
* at the beginning of the line is indicated by an ellipsis glyph. e.g., "...wxyz"
@@ -106,13 +106,13 @@ const Text = React.createClass({
*
* > `clip` is working only for iOS
*/
lineBreakMode: React.PropTypes.oneOf(['head', 'middle', 'tail', 'clip']),
ellipsizeMode: React.PropTypes.oneOf(['head', 'middle', 'tail', 'clip']),
/**
* Used to truncate the text with an ellipsis after computing the text
* layout, including line wrapping, such that the total number of lines
* does not exceed this number.
*
* This prop is commonly used with `lineBreakMode`.
* This prop is commonly used with `ellipsizeMode`.
*/
numberOfLines: React.PropTypes.number,
/**
@@ -172,7 +172,7 @@ const Text = React.createClass({
return {
accessible: true,
allowFontScaling: true,
lineBreakMode: 'tail',
ellipsizeMode: 'tail',
};
},
getInitialState: function(): Object {
+8 -2
View File
@@ -4,7 +4,7 @@ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
Pod::Spec.new do |s|
s.name = "React"
s.version = package['version']
s.version = "0.31.0"
s.summary = package['description']
s.description = <<-DESC
React Native apps are built using the React JS
@@ -29,8 +29,9 @@ Pod::Spec.new do |s|
s.preserve_paths = "cli.js", "Libraries/**/*.js", "lint", "linter.js", "node_modules", "package.json", "packager", "PATENTS", "react-native-cli"
s.subspec 'Core' do |ss|
ss.dependency 'React/CSSLayout'
ss.source_files = "React/**/*.{c,h,m,mm,S}"
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*"
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*", "React/CSSLayout/*"
ss.frameworks = "JavaScriptCore"
ss.libraries = "stdc++"
ss.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
@@ -42,6 +43,11 @@ Pod::Spec.new do |s|
ss.preserve_paths = "Libraries/ART/**/*.js"
end
s.subspec 'CSSLayout' do |ss|
ss.source_files = "React/CSSLayout/**/*.{c,h}"
ss.header_mappings_dir = "React"
end
s.subspec 'RCTActionSheet' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.31.0
GROUP=com.facebook.react
POM_NAME=ReactNative
@@ -283,7 +283,7 @@ public class ReactContext extends ContextWrapper {
* DO NOT HOLD LONG-LIVED REFERENCES TO THE OBJECT RETURNED BY THIS METHOD, AS THIS WILL CAUSE
* MEMORY LEAKS.
*/
/* package */ @Nullable Activity getCurrentActivity() {
public @Nullable Activity getCurrentActivity() {
if (mCurrentActivity == null) {
return null;
}
@@ -72,7 +72,7 @@ public class ViewProps {
public static final String LINE_HEIGHT = "lineHeight";
public static final String NEEDS_OFFSCREEN_ALPHA_COMPOSITING = "needsOffscreenAlphaCompositing";
public static final String NUMBER_OF_LINES = "numberOfLines";
public static final String LINE_BREAK_MODE = "lineBreakMode";
public static final String LINE_BREAK_MODE = "ellipsizeMode";
public static final String ON = "on";
public static final String RESIZE_MODE = "resizeMode";
public static final String TEXT_ALIGN = "textAlign";
@@ -74,16 +74,16 @@ public class ReactTextViewManager extends BaseViewManager<ReactTextView, ReactTe
}
@ReactProp(name = ViewProps.LINE_BREAK_MODE)
public void setLineBreakMode(ReactTextView view, @Nullable String lineBreakMode) {
if(lineBreakMode == null) {
public void setLineBreakMode(ReactTextView view, @Nullable String ellipsizeMode) {
if(ellipsizeMode == null) {
return;
}
if (lineBreakMode.equals("head")) {
if (ellipsizeMode.equals("head")) {
view.setEllipsize(TextUtils.TruncateAt.START);
} else if (lineBreakMode.equals("middle")) {
} else if (ellipsizeMode.equals("middle")) {
view.setEllipsize(TextUtils.TruncateAt.MIDDLE);
} else if (lineBreakMode.equals("tail")) {
} else if (ellipsizeMode.equals("tail")) {
view.setEllipsize(TextUtils.TruncateAt.END);
}
}
+2 -2
View File
@@ -94,14 +94,14 @@ The debugger will receive a list of all project roots, separated by a space. For
### Debugging with [Stetho](http://facebook.github.io/stetho/) on Android
1. In ```android/app/build.gradle``` , add
1. In ```android/app/build.gradle```, add these lines in the `dependencies` section:
```gradle
compile 'com.facebook.stetho:stetho:1.3.1'
compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'
```
2. In ```android/app/src/main/java/com/{yourAppName}/MainApplication.java```, add the following imports :
2. In ```android/app/src/main/java/com/{yourAppName}/MainApplication.java```, add the following imports:
```java
import com.facebook.react.modules.network.ReactCookieJarContainer;
+6 -5
View File
@@ -31,11 +31,12 @@ import React, { Component } from 'react';
import { View, Text } from 'react-native';
export default class MyScene extends Component {
getDefaultProps() {
return {
title: 'MyScene'
};
}
static propTypes = {
title: React.PropTypes.string,
};
static defaultProps = {
title: 'MyScene',
};
render() {
return (
+2
View File
@@ -19,4 +19,6 @@ global.__fbBatchedBridgeConfig = {
global.Promise = require('promise');
global.regeneratorRuntime = require.requireActual('regenerator-runtime/runtime');
jest.mock('npmlog');
jest.setMock('ErrorUtils', require('ErrorUtils'));
+44 -57
View File
@@ -21,69 +21,56 @@ function saveBundle(output, bundle, args) {
}
function buildBundle(args, config, output = outputBundle, packagerInstance) {
return new Promise((resolve, reject) => {
// This is used by a bazillion of npm modules we don't control so we don't
// have other choice than defining it as an env variable here.
process.env.NODE_ENV = args.dev ? 'development' : 'production';
// This is used by a bazillion of npm modules we don't control so we don't
// have other choice than defining it as an env variable here.
if (!process.env.NODE_ENV) {
// If you're inlining environment variables, you can use babel to remove
// this line:
// https://www.npmjs.com/package/babel-remove-process-env-assignment
process.env.NODE_ENV = args.dev ? 'development' : 'production';
}
const options = {
projectRoots: config.getProjectRoots(),
assetRoots: config.getAssetRoots(),
blacklistRE: config.getBlacklistRE(args.platform),
getTransformOptionsModulePath: config.getTransformOptionsModulePath,
transformModulePath: args.transformer,
extraNodeModules: config.extraNodeModules,
nonPersistent: true,
resetCache: args.resetCache,
};
const transformModulePath =
args.transformer ? path.resolve(args.transformer) :
typeof config.getTransformModulePath === 'function' ? config.getTransformModulePath() :
undefined;
const requestOpts = {
entryFile: args.entryFile,
sourceMapUrl: args.sourcemapOutput,
dev: args.dev,
minify: !args.dev,
platform: args.platform,
};
const options = {
projectRoots: config.getProjectRoots(),
assetRoots: config.getAssetRoots(),
blacklistRE: config.getBlacklistRE(args.platform),
getTransformOptionsModulePath: config.getTransformOptionsModulePath,
transformModulePath: transformModulePath,
extraNodeModules: config.extraNodeModules,
nonPersistent: true,
resetCache: args['reset-cache'],
};
// If a packager instance was not provided, then just create one for this
// bundle command and close it down afterwards.
var shouldClosePackager = false;
if (!packagerInstance) {
packagerInstance = new Server(options);
shouldClosePackager = true;
}
const requestOpts = {
entryFile: args['entry-file'],
sourceMapUrl: args['sourcemap-output'],
dev: args.dev,
minify: !args.dev,
platform: args.platform,
};
const bundlePromise = output.build(packagerInstance, requestOpts)
.then(bundle => {
if (shouldClosePackager) {
packagerInstance.end();
}
return saveBundle(output, bundle, args);
});
// If a packager instance was not provided, then just create one for this
// bundle command and close it down afterwards.
var shouldClosePackager = false;
if (!packagerInstance) {
packagerInstance = new Server(options);
shouldClosePackager = true;
}
// Save the assets of the bundle
const assets = bundlePromise
.then(bundle => bundle.getAssets())
.then(outputAssets => saveAssets(
outputAssets,
args.platform,
args.assetsDest,
));
const bundlePromise = output.build(packagerInstance, requestOpts)
.then(bundle => {
if (shouldClosePackager) {
packagerInstance.end();
}
return saveBundle(output, bundle, args);
});
// Save the assets of the bundle
const assets = bundlePromise
.then(bundle => bundle.getAssets())
.then(outputAssets => saveAssets(
outputAssets,
args.platform,
args['assets-dest']
));
// When we're done saving bundle output and the assets, we're done.
resolve(assets);
});
// When we're done saving bundle output and the assets, we're done.
return assets;
}
module.exports = buildBundle;
+13 -9
View File
@@ -8,26 +8,30 @@
*/
const buildBundle = require('./buildBundle');
const bundleCommandLineArgs = require('./bundleCommandLineArgs');
const parseCommandLine = require('../util/parseCommandLine');
const outputBundle = require('./output/bundle');
const outputPrepack = require('./output/prepack');
const bundleCommandLineArgs = require('./bundleCommandLineArgs');
/**
* Builds the bundle starting to look for dependencies at the given entry path.
*/
function bundleWithOutput(argv, config, output, packagerInstance) {
const args = parseCommandLine(bundleCommandLineArgs, argv);
function bundleWithOutput(argv, config, args, output, packagerInstance) {
if (!output) {
output = args.prepack ? outputPrepack : outputBundle;
}
return buildBundle(args, config, output, packagerInstance);
}
function bundle(argv, config, packagerInstance) {
return bundleWithOutput(argv, config, undefined, packagerInstance);
function bundle(argv, config, args, packagerInstance) {
return bundleWithOutput(argv, config, args, undefined, packagerInstance);
}
module.exports = bundle;
module.exports.withOutput = bundleWithOutput;
module.exports = {
name: 'bundle',
description: 'builds the javascript bundle for offline use',
func: bundle,
options: bundleCommandLineArgs,
// not used by the CLI itself
withOutput: bundleWithOutput,
};
+19 -28
View File
@@ -10,56 +10,47 @@
module.exports = [
{
command: 'entry-file',
command: '--entry-file <path>',
description: 'Path to the root JS file, either absolute or relative to JS root',
type: 'string',
required: true,
}, {
command: 'platform',
command: '--platform [string]',
description: 'Either "ios" or "android"',
type: 'string',
default: 'ios',
}, {
command: 'transformer',
description: 'Specify a custom transformer to be used',
type: 'string',
default: null,
command: '--transformer [string]',
description: 'Specify a custom transformer to be used (absolute path)',
default: require.resolve('../../packager/transformer'),
}, {
command: 'dev',
command: '--dev [boolean]',
description: 'If false, warnings are disabled and the bundle is minified',
parse: (val) => val === 'false' ? false : true,
default: true,
}, {
command: 'prepack',
description: 'If true, the output bundle will use the Prepack format.',
default: false
command: '--prepack',
description: 'When passed, the output bundle will use the Prepack format.',
}, {
command: 'bridge-config',
command: '--bridge-config [string]',
description: 'File name of a a JSON export of __fbBatchedBridgeConfig. Used by Prepack. Ex. ./bridgeconfig.json',
type: 'string'
}, {
command: 'bundle-output',
command: '--bundle-output <string>',
description: 'File name where to store the resulting bundle, ex. /tmp/groups.bundle',
type: 'string',
required: true,
}, {
command: 'bundle-encoding',
command: '--bundle-encoding [string]',
description: 'Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer).',
type: 'string',
default: 'utf8',
}, {
command: 'sourcemap-output',
command: '--sourcemap-output [string]',
description: 'File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map',
type: 'string',
}, {
command: 'assets-dest',
command: '--assets-dest [string]',
description: 'Directory name where to store assets referenced in the bundle',
type: 'string',
}, {
command: 'verbose',
command: '--verbose',
description: 'Enables logging',
default: false,
}, {
command: 'reset-cache',
command: '--reset-cache',
description: 'Removes cached files',
default: false
}
default: false,
},
];
+3 -3
View File
@@ -28,10 +28,10 @@ function createCodeWithMap(bundle, dev) {
function saveBundleAndMap(bundle, options, log) {
const {
'bundle-output': bundleOutput,
'bundle-encoding': encoding,
bundleOutput,
bundleEncoding: encoding,
dev,
'sourcemap-output': sourcemapOutput,
sourcemapOutput
} = options;
log('start');
+2 -2
View File
@@ -16,8 +16,8 @@ function buildPrepackBundle(packagerClient, requestOptions) {
function savePrepackBundle(bundle, options, log) {
const {
'bundle-output': bundleOutput,
'bridge-config': bridgeConfig,
bundleOutput,
bridgeConfig,
} = options;
const result = bundle.build({
@@ -30,9 +30,9 @@ const MODULES_DIR = 'js-modules';
*/
function saveAsAssets(bundle, options, log) {
const {
'bundle-output': bundleOutput,
'bundle-encoding': encoding,
'sourcemap-output': sourcemapOutput,
bundleOutput,
bundleEncoding: encoding,
sourcemapOutput
} = options;
log('start');
@@ -26,9 +26,9 @@ const SIZEOF_UINT32 = 4;
*/
function saveAsIndexedFile(bundle, options, log) {
const {
'bundle-output': bundleOutput,
'bundle-encoding': encoding,
'sourcemap-output': sourcemapOutput,
bundleOutput,
bundleEncoding: encoding,
sourcemapOutput
} = options;
log('start');
+9 -3
View File
@@ -8,13 +8,19 @@
*/
const bundleWithOutput = require('./bundle').withOutput;
const bundleCommandLineArgs = require('./bundleCommandLineArgs');
const outputUnbundle = require('./output/unbundle');
/**
* Builds the bundle starting to look for dependencies at the given entry path.
*/
function unbundle(argv, config, packagerInstance) {
return bundleWithOutput(argv, config, outputUnbundle, packagerInstance);
function unbundle(argv, config, args, packagerInstance) {
return bundleWithOutput(argv, config, args, outputUnbundle, packagerInstance);
}
module.exports = unbundle;
module.exports = {
name: 'unbundle',
description: 'builds javascript as "unbundle" for offline use',
func: unbundle,
options: bundleCommandLineArgs,
};
+115 -137
View File
@@ -5,171 +5,149 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/
'use strict';
const bundle = require('./bundle/bundle');
const childProcess = require('child_process');
const Config = require('./util/Config');
const defaultConfig = require('./default.config');
const dependencies = require('./dependencies/dependencies');
const generate = require('./generate/generate');
const library = require('./library/library');
const link = require('./rnpm/link/src/link');
const path = require('path');
const Promise = require('promise');
const runAndroid = require('./runAndroid/runAndroid');
const logAndroid = require('./logAndroid/logAndroid');
const runIOS = require('./runIOS/runIOS');
const logIOS = require('./logIOS/logIOS');
const server = require('./server/server');
const TerminalAdapter = require('yeoman-environment/lib/adapter.js');
const yeoman = require('yeoman-environment');
const unbundle = require('./bundle/unbundle');
const upgrade = require('./upgrade/upgrade');
const version = require('./version/version');
const commander = require('commander');
const Config = require('./util/Config');
const childProcess = require('child_process');
const Promise = require('promise');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const gracefulFs = require('graceful-fs');
// Just a helper to proxy 'react-native link' to rnpm
const linkWrapper = (args, config) => {
const rnpmConfig = require('./rnpm/core/src/config');
return new Promise((resolve, reject) => {
link(rnpmConfig, args.slice(1)).then(resolve, reject);
});
}
const init = require('./init/init');
const commands = require('./commands');
const assertRequiredOptions = require('./util/assertRequiredOptions');
const pkg = require('../package.json');
const defaultConfig = require('./default.config');
import type { Command } from './commands';
// graceful-fs helps on getting an error when we run out of file
// descriptors. When that happens it will enqueue the operation and retry it.
gracefulFs.gracefulify(fs);
const documentedCommands = {
'start': [server, 'starts the webserver'],
'bundle': [bundle, 'builds the javascript bundle for offline use'],
'unbundle': [unbundle, 'builds javascript as "unbundle" for offline use'],
'new-library': [library, 'generates a native library bridge'],
'android': [generateWrapper, 'generates an Android project for your app'],
'run-android': [runAndroid, 'builds your app and starts it on a connected Android emulator or device'],
'log-android': [logAndroid, 'print Android logs'],
'run-ios': [runIOS, 'builds your app and starts it on iOS simulator'],
'log-ios': [logIOS, 'print iOS logs'],
'upgrade': [upgrade, 'upgrade your app\'s template files to the latest version; run this after ' +
'updating the react-native version in your package.json and running npm install'],
'link': [linkWrapper, 'link a library'],
commander.version(pkg.version);
const defaultOptParser = (val) => val;
const handleError = (err) => {
console.error();
console.error(err.message || err);
console.error();
process.exit(1);
};
const exportedCommands = {dependencies: dependencies};
Object.keys(documentedCommands).forEach(function(command) {
exportedCommands[command] = documentedCommands[command][0];
});
const undocumentedCommands = {
'--version': [version, ''],
'init': [printInitWarning, ''],
};
const commands = Object.assign({}, documentedCommands, undocumentedCommands);
/**
* Parses the command line and runs a command of the CLI.
*/
function run() {
const args = process.argv.slice(2);
if (args.length === 0) {
printUsage();
// Custom printHelpInformation command inspired by internal Commander.js
// one modified to suit our needs
function printHelpInformation() {
let cmdName = this._name;
if (this._alias) {
cmdName = cmdName + '|' + this._alias;
}
let output = [
'',
chalk.bold(chalk.cyan((` react-native ${cmdName} ${this.usage()}`))),
` ${this._description}`,
'',
` ${chalk.bold('Options:')}`,
'',
this.optionHelp().replace(/^/gm, ' '),
'',
];
if (this.examples && this.examples.length > 0) {
const formattedUsage = this.examples.map(
example => ` ${example.desc}: \n ${chalk.cyan(example.cmd)}`,
).join('\n\n');
output = output.concat([
chalk.bold(' Example usage:'),
'',
formattedUsage,
]);
}
return output.concat([
'',
'',
]).join('\n');
}
function printUnknownCommand(cmdName) {
console.log([
'',
cmdName
? chalk.red(` Unrecognized command '${cmdName}'`)
: chalk.red(' You didn\'t pass any command'),
` Run ${chalk.cyan('react-native --help')} to see list of all available commands`,
'',
].join('\n'));
}
const addCommand = (command: Command, config: Config) => {
const options = command.options || [];
const cmd = commander
.command(command.name, undefined, {
noHelp: !command.description,
})
.description(command.description)
.action(function runAction() {
const passedOptions = this.opts();
const argv: Array<string> = Array.from(arguments).slice(0, -1);
Promise.resolve()
.then(() => {
assertRequiredOptions(options, passedOptions);
return command.func(argv, config, passedOptions);
})
.catch(handleError);
});
cmd.helpInformation = printHelpInformation.bind(cmd);
cmd.examples = command.examples;
options
.forEach(opt => cmd.option(
opt.command,
opt.description,
opt.parse || defaultOptParser,
typeof opt.default === 'function' ? opt.default(config) : opt.default,
));
};
function run() {
const config = Config.get(__dirname, defaultConfig);
const setupEnvScript = /^win/.test(process.platform)
? 'setup_env.bat'
: 'setup_env.sh';
childProcess.execFileSync(path.join(__dirname, setupEnvScript));
const command = commands[args[0]];
if (!command) {
console.error('Command `%s` unrecognized', args[0]);
printUsage();
commands.forEach(cmd => addCommand(cmd, config));
commander.parse(process.argv);
const isValidCommand = commands.find(cmd => cmd.name.split(' ')[0] === process.argv[2]);
if (!isValidCommand) {
printUnknownCommand(process.argv[2]);
return;
}
command[0](args, Config.get(__dirname, defaultConfig)).done();
}
function generateWrapper(args, config) {
return generate([
'--platform', 'android',
'--project-path', process.cwd(),
'--project-name', JSON.parse(
fs.readFileSync('package.json', 'utf8')
).name
], config);
}
function printUsage() {
console.log([
'Usage: react-native <command>',
'',
'Commands:'
].concat(Object.keys(documentedCommands).map(function(name) {
return ' - ' + name + ': ' + documentedCommands[name][1];
})).join('\n'));
process.exit(1);
}
// The user should never get here because projects are inited by
// using `react-native-cli` from outside a project directory.
function printInitWarning() {
return Promise.resolve().then(function() {
console.log([
'Looks like React Native project already exists in the current',
'folder. Run this command from a different folder or remove node_modules/react-native'
].join('\n'));
process.exit(1);
});
}
class CreateSuppressingTerminalAdapter extends TerminalAdapter {
constructor() {
super();
// suppress 'create' output generated by yeoman
this.log.create = function() {};
if (!commander.args.length) {
commander.help();
}
}
/**
* Creates the template for a React Native project given the provided
* parameters:
* - projectDir: templates will be copied here.
* - argsOrName: project name or full list of custom arguments to pass to the
* generator.
*/
function init(projectDir, argsOrName) {
console.log('Setting up new React Native app in ' + projectDir);
const env = yeoman.createEnv(
undefined,
undefined,
new CreateSuppressingTerminalAdapter()
);
env.register(
require.resolve(path.join(__dirname, 'generator')),
'react:app'
);
// argv is for instance
// ['node', 'react-native', 'init', 'AwesomeApp', '--verbose']
// args should be ['AwesomeApp', '--verbose']
const args = Array.isArray(argsOrName)
? argsOrName
: [argsOrName].concat(process.argv.slice(4));
const generator = env.create('react:app', {args: args});
generator.destinationRoot(projectDir);
generator.run();
}
module.exports = {
run: run,
init: init,
commands: exportedCommands
};
+70
View File
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/
'use strict';
const Config = require('./util/Config');
const getUserCommands = require('./rnpm/core/src/getCommands');
export type Command = {
name: string,
description?: string,
usage?: string,
func: (argv: Array<string>, config: Config, args: Object) => ?Promise<void>,
options?: Array<{
command: string,
description?: string,
parse?: (val: string) => any,
default?: (config: Config) => any | any,
}>,
examples?: Array<{
desc: string,
cmd: string,
}>,
};
const documentedCommands = [
require('./server/server'),
require('./runIOS/runIOS'),
require('./runAndroid/runAndroid'),
require('./library/library'),
require('./bundle/bundle'),
require('./bundle/unbundle'),
require('./rnpm/link/link'),
require('./rnpm/link/unlink'),
require('./rnpm/install/install'),
require('./rnpm/install/uninstall'),
require('./upgrade/upgrade'),
require('./logAndroid/logAndroid'),
require('./logIOS/logIOS'),
require('./dependencies/dependencies'),
];
// The user should never get here because projects are inited by
// using `react-native-cli` from outside a project directory.
const undocumentedCommands = [
{
name: 'init',
func: () => {
console.log([
'Looks like React Native project already exists in the current',
'folder. Run this command from a different folder or remove node_modules/react-native'
].join('\n'));
},
},
];
const commands: Array<Command> = [
...documentedCommands,
...undocumentedCommands,
...getUserCommands(),
];
module.exports = commands;
+4
View File
@@ -2,6 +2,7 @@
var blacklist = require('../packager/blacklist');
var path = require('path');
var rnpmConfig = require('./rnpm/core/src/config');
/**
* Default configuration for the CLI.
@@ -15,6 +16,9 @@ var config = {
return getRoots();
},
getProjectConfig: rnpmConfig.getProjectConfig,
getDependencyConfig: rnpmConfig.getDependencyConfig,
/**
* Specify where to look for assets that are referenced using
* `image!<image_name>`. Asset directories for images referenced using
+28 -41
View File
@@ -8,50 +8,14 @@
*/
const fs = require('fs');
const parseCommandLine = require('../util/parseCommandLine');
const path = require('path');
const Promise = require('promise');
const ReactPackager = require('../../packager/react-packager');
/**
* Returns the dependencies an entry path has.
*/
function dependencies(argv, config, packagerInstance) {
return new Promise((resolve, reject) => {
_dependencies(argv, config, resolve, reject, packagerInstance);
});
}
function _dependencies(argv, config, resolve, reject, packagerInstance) {
const args = parseCommandLine([
{
command: 'entry-file',
description: 'Absolute path to the root JS file',
type: 'string',
required: true,
}, {
command: 'output',
description: 'File name where to store the output, ex. /tmp/dependencies.txt',
type: 'string',
}, {
command: 'platform',
description: 'The platform extension used for selecting modules',
type: 'string',
}, {
command: 'transformer',
type: 'string',
default: null,
description: 'Specify a custom transformer to be used'
}, {
command: 'verbose',
description: 'Enables logging',
default: false,
}
], argv);
const rootModuleAbsolutePath = args['entry-file'];
function dependencies(argv, config, args, packagerInstance) {
const rootModuleAbsolutePath = args.entryFile;
if (!fs.existsSync(rootModuleAbsolutePath)) {
reject(`File ${rootModuleAbsolutePath} does not exist`);
return Promise.reject(`File ${rootModuleAbsolutePath} does not exist`);
}
const transformModulePath =
@@ -86,7 +50,7 @@ function _dependencies(argv, config, resolve, reject, packagerInstance) {
? fs.createWriteStream(args.output)
: process.stdout;
resolve((packagerInstance ?
return Promise.resolve((packagerInstance ?
packagerInstance.getOrderedDependencyPaths(options) :
ReactPackager.getOrderedDependencyPaths(packageOpts, options)).then(
deps => {
@@ -110,4 +74,27 @@ function _dependencies(argv, config, resolve, reject, packagerInstance) {
));
}
module.exports = dependencies;
module.exports = {
name: 'dependencies',
func: dependencies,
options: [
{
command: '--entry-file <path>',
description: 'Absolute path to the root JS file',
}, {
command: '--output [path]',
description: 'File name where to store the output, ex. /tmp/dependencies.txt',
}, {
command: '--platform [extension]',
description: 'The platform extension used for selecting modules',
}, {
command: '--transformer [path]',
default: null,
description: 'Specify a custom transformer to be used'
}, {
command: '--verbose',
description: 'Enables logging',
default: false,
},
],
};
+55
View File
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const path = require('path');
const TerminalAdapter = require('yeoman-environment/lib/adapter.js');
const yeoman = require('yeoman-environment');
class CreateSuppressingTerminalAdapter extends TerminalAdapter {
constructor() {
super();
// suppress 'create' output generated by yeoman
this.log.create = function() {};
}
}
/**
* Creates the template for a React Native project given the provided
* parameters:
* - projectDir: templates will be copied here.
* - argsOrName: project name or full list of custom arguments to pass to the
* generator.
*/
function init(projectDir, argsOrName) {
console.log('Setting up new React Native app in ' + projectDir);
const env = yeoman.createEnv(
undefined,
undefined,
new CreateSuppressingTerminalAdapter()
);
env.register(
require.resolve(path.join(__dirname, '../generator')),
'react:app'
);
// argv is for instance
// ['node', 'react-native', 'init', 'AwesomeApp', '--verbose']
// args should be ['AwesomeApp', '--verbose']
const args = Array.isArray(argsOrName)
? argsOrName
: [argsOrName].concat(process.argv.slice(4));
const generator = env.create('react:app', {args: args});
generator.destinationRoot(projectDir);
generator.run();
}
module.exports = init;
+13 -19
View File
@@ -11,7 +11,6 @@
const copyAndReplace = require('../util/copyAndReplace');
const fs = require('fs');
const isValidPackageName = require('../util/isValidPackageName');
const parseCommandLine = require('../util/parseCommandLine');
const path = require('path');
const Promise = require('promise');
const walk = require('../util/walk');
@@ -19,22 +18,9 @@ const walk = require('../util/walk');
/**
* Creates a new native library with the given name
*/
function library(argv, config) {
return new Promise((resolve, reject) => {
_library(argv, config, resolve, reject);
});
}
function _library(argv, config, resolve, reject) {
const args = parseCommandLine([{
command: 'name',
description: 'Library name',
type: 'string',
required: true,
}], argv);
function library(argv, config, args) {
if (!isValidPackageName(args.name)) {
reject(
return Promise.reject(
args.name + ' is not a valid name for a project. Please use a valid ' +
'identifier name (alphanumeric).'
);
@@ -50,7 +36,7 @@ function _library(argv, config, resolve, reject) {
}
if (fs.existsSync(libraryDest)) {
reject('Library already exists in', libraryDest);
return Promise.reject(`Library already exists in ${libraryDest}`);
}
walk(source).forEach(f => {
@@ -71,7 +57,15 @@ function _library(argv, config, resolve, reject) {
console.log('Next Steps:');
console.log(' Link your library in Xcode:');
console.log(' https://facebook.github.io/react-native/docs/linking-libraries-ios.html#content\n');
resolve();
}
module.exports = library;
module.exports = {
name: 'new-library',
func: library,
description: 'generates a native library bridge',
options: [{
command: '--name <string>',
description: 'name of the library to generate',
default: null,
}],
};
+7 -4
View File
@@ -21,7 +21,7 @@ function logAndroid() {
});
}
function _logAndroid(resolve, reject) {
function _logAndroid() {
try {
const adbPath = process.env.ANDROID_HOME
? process.env.ANDROID_HOME + '/platform-tools/adb'
@@ -43,9 +43,12 @@ function _logAndroid(resolve, reject) {
console.log(chalk.red(
'adb invocation failed. Do you have adb in your PATH?'
));
reject();
return;
return Promise.reject();
}
}
module.exports = logAndroid;
module.exports = {
name: 'log-android',
description: 'starts adb logcat',
func: logAndroid,
};
+11 -8
View File
@@ -15,7 +15,7 @@ function logIOS() {
});
}
function _logIOS(resolve, reject) {
function _logIOS() {
let rawDevices;
try {
@@ -26,8 +26,7 @@ function _logIOS(resolve, reject) {
console.log(chalk.red(
'xcrun invocation failed. Please check that Xcode is installed.'
));
reject(e);
return;
return Promise.reject(e);
}
const { devices } = JSON.parse(rawDevices);
@@ -37,10 +36,10 @@ function _logIOS(resolve, reject) {
console.log(chalk.red(
'No active iOS device found'
));
reject();
return Promise.reject();
}
tailDeviceLogs(device.udid, reject);
return tailDeviceLogs(device.udid);
}
function _findAvailableDevice(devices) {
@@ -53,7 +52,7 @@ function _findAvailableDevice(devices) {
}
}
function tailDeviceLogs(udid, reject) {
function tailDeviceLogs(udid) {
const logDir = path.join(
os.homedir(),
'Library',
@@ -70,8 +69,12 @@ function tailDeviceLogs(udid, reject) {
console.log(chalk.red(
'syslog invocation failed.'
));
reject(log.error);
return Promise.reject(log.error);
}
}
module.exports = logIOS;
module.exports = {
name: 'log-ios',
description: 'starts iOS device syslog tail',
func: logIOS,
};
@@ -2,7 +2,7 @@ jest.autoMockOff();
const findAndroidAppFolder = require('../../src/config/android/findAndroidAppFolder');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
const mocks = require('../../__fixtures__/android');
describe('android::findAndroidAppFolder', () => {
beforeAll(() => mockFs({
@@ -2,7 +2,7 @@ jest.autoMockOff();
const findManifest = require('../../src/config/android/findManifest');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
const mocks = require('../../__fixtures__/android');
describe('android::findManifest', () => {
@@ -2,7 +2,7 @@ jest.autoMockOff();
const findPackageClassName = require('../../src/config/android/findPackageClassName');
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
const mocks = require('../../__fixtures__/android');
describe('android::findPackageClassName', () => {
@@ -2,7 +2,7 @@ jest.autoMockOff();
const getDependencyConfig = require('../../src/config/android').dependencyConfig;
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
const mocks = require('../../__fixtures__/android');
const userConfig = {};
describe('android::getDependencyConfig', () => {
@@ -2,7 +2,7 @@ jest.autoMockOff();
const getProjectConfig = require('../../src/config/android').projectConfig;
const mockFs = require('mock-fs');
const mocks = require('../fixtures/android');
const mocks = require('../../__fixtures__/android');
describe('android::getProjectConfig', () => {
beforeAll(() => mockFs({
@@ -3,7 +3,7 @@ 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');
const mocks = require('../../__fixtures__/android');
describe('android::readManifest', () => {
@@ -2,7 +2,7 @@ jest.autoMockOff();
const findAssets = require('../src/config/findAssets');
const mockFs = require('mock-fs');
const dependencies = require('./fixtures/dependencies');
const dependencies = require('../__fixtures__/dependencies');
const isArray = (arg) =>
Object.prototype.toString.call(arg) === '[object Array]';
@@ -2,8 +2,8 @@ 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 projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
const userConfig = {};
describe('ios::findProject', () => {
@@ -2,7 +2,7 @@ jest.autoMockOff();
const getProjectConfig = require('../../src/config/ios').projectConfig;
const mockFs = require('mock-fs');
const projects = require('../fixtures/projects');
const projects = require('../../__fixtures__/projects');
describe('ios::getProjectConfig', () => {
const userConfig = {};
@@ -22,5 +22,15 @@ describe('ios::getProjectConfig', () => {
expect(getProjectConfig(folder, userConfig)).toBe(null);
});
it('should return normalized shared library names', () => {
const projectConfig = getProjectConfig('testDir/nested', {
sharedLibraries: ['libc++', 'libz.tbd', 'HealthKit', 'HomeKit.framework'],
});
expect(projectConfig.sharedLibraries).toEqual(
['libc++.tbd', 'libz.tbd', 'HealthKit.framework', 'HomeKit.framework']
);
});
afterEach(mockFs.restore);
});
+1 -1
View File
@@ -31,7 +31,7 @@ exports.getProjectConfig = function getProjectConfig() {
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])
path.join(process.cwd(), 'node_modules', packageName)
);
return Object.assign({}, rnpm, {
@@ -1,6 +1,19 @@
const path = require('path');
const findProject = require('./findProject');
/**
* For libraries specified without an extension, add '.tbd' for those that
* start with 'lib' and '.framework' to the rest.
*/
const mapSharedLibaries = (libraries) => {
return libraries.map(name => {
if (path.extname(name)) {
return name;
}
return name + (name.indexOf('lib') === 0 ? '.tbd' : '.framework');
});
};
/**
* Returns project config by analyzing given folder and applying some user defaults
* when constructing final object
@@ -24,6 +37,7 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
projectPath: projectPath,
projectName: path.basename(projectPath),
libraryFolder: userConfig.libraryFolder || 'Libraries',
sharedLibraries: mapSharedLibaries(userConfig.sharedLibraries || []),
plist: userConfig.plist || [],
};
};
+37 -7
View File
@@ -1,5 +1,4 @@
const path = require('path');
const fs = require('fs');
const union = require('lodash').union;
const uniq = require('lodash').uniq;
const flatten = require('lodash').flatten;
@@ -9,13 +8,29 @@ const flatten = require('lodash').flatten;
* @param {String} dependency Name of the dependency
* @return {Boolean} If dependency is a rnpm plugin
*/
const isPlugin = (dependency) => !!~dependency.indexOf('rnpm-plugin-');
const isRNPMPlugin = (dependency) => dependency.indexOf('rnpm-plugin-') === 0;
const isReactNativePlugin = (dependency) => dependency.indexOf('react-native-') === 0;
const readPackage = (folder) => {
try {
return require(path.join(folder, 'package.json'));
} catch (e) {
return null;
}
};
const findPluginsInReactNativePackage = (pjson) => {
if (!pjson.rnpm || !pjson.rnpm.plugin) {
return [];
}
return path.join(pjson.name, pjson.rnpm.plugin);
};
const findPluginInFolder = (folder) => {
var pjson;
try {
pjson = require(path.join(folder, 'package.json'));
} catch (e) {
const pjson = readPackage(folder);
if (!pjson) {
return [];
}
@@ -24,7 +39,22 @@ const findPluginInFolder = (folder) => {
Object.keys(pjson.devDependencies || {})
);
return deps.filter(isPlugin);
return deps.reduce(
(acc, pkg) => {
if (isRNPMPlugin(pkg)) {
return acc.concat(pkg);
}
if (isReactNativePlugin(pkg)) {
const pkgJson = readPackage(path.join(folder, 'node_modules', pkg));
if (!pkgJson) {
return acc;
}
return acc.concat(findPluginsInReactNativePackage(pkgJson));
}
return acc;
},
[]
);
};
/**
+3 -11
View File
@@ -1,20 +1,12 @@
const path = require('path');
const fs = require('fs');
const uniq = require('lodash').uniq;
const flattenDeep = require('lodash').flattenDeep;
const findPlugins = require('./findPlugins');
const flatten = require('lodash').flatten;
/**
* @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');
const plugins = findPlugins([appRoot]).map(name => require(path.join(appRoot, 'node_modules', name)));
return flatten(plugins);
};
@@ -1,171 +0,0 @@
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('should 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);
});
});
});
-11
View File
@@ -1,11 +0,0 @@
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]',
},
];
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
func: require('./src/install'),
description: 'install and link native dependencies',
name: 'install <packageName>',
};
+1 -1
View File
@@ -7,7 +7,7 @@ const spawnOpts = {
log.heading = 'rnpm-install';
module.exports = function install(config, args, callback) {
module.exports = function install(args, config) {
const name = args[0];
var res = spawnSync('npm', ['install', name, '--save'], spawnOpts);
+1 -1
View File
@@ -7,7 +7,7 @@ const spawnOpts = {
log.heading = 'rnpm-install';
module.exports = function install(config, args, callback) {
module.exports = function install(args, config) {
const name = args[0];
var res = spawnSync('rnpm', ['unlink', name], spawnOpts);
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
func: require('./src/uninstall'),
description: 'uninstall and unlink native dependencies',
name: 'uninstall <packageName>',
};
@@ -1,17 +1,19 @@
const chai = require('chai');
const expect = chai.expect;
const applyParams = require('../../../src/android/patches/applyParams');
'use strict';
jest.autoMockOff();
const applyParams = require('../../src/android/patches/applyParams');
describe('applyParams', () => {
it('apply params to the string', () => {
expect(
applyParams('${foo}', {foo: 'foo'}, 'react-native')
).to.be.equal('this.getResources().getString(R.strings.reactNative_foo)');
).toEqual('this.getResources().getString(R.strings.reactNative_foo)');
});
it('use null if no params provided', () => {
expect(
applyParams('${foo}', {}, 'react-native')
).to.be.equal('null');
).toEqual('null');
});
});
@@ -0,0 +1,20 @@
'use strict';
jest.autoMockOff();
const path = require('path');
const isInstalled = require('../../src/android/isInstalled');
const projectConfig = {
buildGradlePath: path.join(__dirname, '../../__fixtures__/android/patchedBuild.gradle'),
};
describe('android::isInstalled', () => {
it('should return true when project is already in build.gradle', () =>
expect(isInstalled(projectConfig, 'test')).toBeTruthy()
);
it('should return false when project is not in build.gradle', () =>
expect(isInstalled(projectConfig, 'test2')).toBeFalsy()
);
});
@@ -0,0 +1,18 @@
'use strict';
jest.autoMockOff();
const makeBuildPatch = require('../../src/android/patches/makeBuildPatch');
const name = 'test';
describe('makeBuildPatch', () => {
it('should build a patch function', () => {
expect(Object.prototype.toString(makeBuildPatch(name)))
.toBe('[object Object]');
});
it('should make a correct patch', () => {
const {patch} = makeBuildPatch(name);
expect(patch).toBe(` compile project(':${name}')\n`);
});
});
@@ -0,0 +1,20 @@
'use strict';
jest.autoMockOff();
const makeImportPatch = require('../../src/android/patches/makeImportPatch');
const packageImportPath = 'import some.example.project';
describe('makeImportPatch', () => {
it('should build a patch', () => {
expect(Object.prototype.toString(makeImportPatch(packageImportPath)))
.toBe('[object Object]');
});
it('MainActivity contains a correct import patch', () => {
const {patch} = makeImportPatch(packageImportPath);
expect(patch).toBe('\n' + packageImportPath);
});
});
@@ -0,0 +1,28 @@
'use strict';
jest.autoMockOff();
const makePackagePatch = require('../../src/android/patches/makePackagePatch');
const applyParams = require('../../src/android/patches/applyParams');
const packageInstance = 'new SomeLibrary(${foo}, ${bar}, \'something\')';
const name = 'some-library';
const params = {
foo: 'foo',
bar: 'bar',
};
describe('makePackagePatch@0.20', () => {
it('should build a patch', () => {
const packagePatch = makePackagePatch(packageInstance, params, name);
expect(Object.prototype.toString(packagePatch))
.toBe('[object Object]');
});
it('MainActivity contains a correct 0.20 import patch', () => {
const {patch} = makePackagePatch(packageInstance, params, name);
const processedInstance = applyParams(packageInstance, params, name);
expect(patch).toBe(',\n ' + processedInstance);
});
});
@@ -1,8 +1,9 @@
const fs = require('fs');
'use strict';
jest.autoMockOff();
const path = require('path');
const chai = require('chai');
const expect = chai.expect;
const makeSettingsPatch = require('../../../src/android/patches/makeSettingsPatch');
const makeSettingsPatch = require('../../src/android/patches/makeSettingsPatch');
const name = 'test';
const projectConfig = {
@@ -15,9 +16,9 @@ const dependencyConfig = {
describe('makeSettingsPatch', () => {
it('should build a patch function', () => {
expect(
makeSettingsPatch(name, dependencyConfig, {}, projectConfig)
).to.be.an('object');
expect(Object.prototype.toString(
makeSettingsPatch(name, dependencyConfig, projectConfig)
)).toBe('[object Object]');
});
it('should make a correct patch', () => {
@@ -26,8 +27,10 @@ describe('makeSettingsPatch', () => {
dependencyConfig.sourceDir
);
expect(makeSettingsPatch(name, dependencyConfig, projectConfig).patch)
.to.be.equal(
const {patch} = makeSettingsPatch(name, dependencyConfig, projectConfig);
expect(patch)
.toBe(
`include ':${name}'\n` +
`project(':${name}').projectDir = ` +
`new File(rootProject.projectDir, '${projectDir}')\n`
@@ -1,5 +1,7 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const getDependencyConfig = require('../src/getDependencyConfig');
const sinon = require('sinon');
@@ -9,8 +11,8 @@ describe('getDependencyConfig', () => {
getDependencyConfig: sinon.stub(),
};
expect(getDependencyConfig(config, ['abcd'])).to.be.an.array;
expect(config.getDependencyConfig.callCount).to.equals(1);
expect(Array.isArray(getDependencyConfig(config, ['abcd']))).toBeTruthy();
expect(config.getDependencyConfig.callCount).toEqual(1);
});
it('should filter out invalid react-native projects', () => {
@@ -18,6 +20,6 @@ describe('getDependencyConfig', () => {
getDependencyConfig: sinon.stub().throws(new Error('Cannot require')),
};
expect(getDependencyConfig(config, ['abcd'])).to.deep.equal([]);
expect(getDependencyConfig(config, ['abcd'])).toEqual([]);
});
});
@@ -1,27 +1,23 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const getProjectDependencies = require('../src/getProjectDependencies');
const mock = require('mock-require');
const path = require('path');
describe('getProjectDependencies', () => {
it('should return an array of project dependencies', () => {
mock(
jest.setMock(
path.join(process.cwd(), './package.json'),
{ dependencies: { lodash: '^6.0.0', 'react-native': '^16.0.0' } }
{ dependencies: { lodash: '^6.0.0', 'react-native': '^16.0.0' }}
);
expect(getProjectDependencies()).to.deep.equals(['lodash']);
expect(getProjectDependencies()).toEqual(['lodash']);
});
it('should return an empty array when no dependencies set', () => {
mock(path.join(process.cwd(), './package.json'), {});
expect(getProjectDependencies()).to.deep.equals([]);
jest.setMock(path.join(process.cwd(), './package.json'), {});
expect(getProjectDependencies()).toEqual([]);
});
afterEach(() => {
mock.stopAll();
});
});
@@ -1,5 +1,7 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const groupFilesByType = require('../src/groupFilesByType');
describe('groupFilesByType', () => {
@@ -16,8 +18,8 @@ describe('groupFilesByType', () => {
const groupedFiles = groupFilesByType(fonts.concat(images));
expect(groupedFiles.font).to.deep.equal(fonts);
expect(groupedFiles.image).to.deep.equal(images);
expect(groupedFiles.font).toEqual(fonts);
expect(groupedFiles.image).toEqual(images);
});
});
@@ -0,0 +1,27 @@
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const path = require('path');
const addFileToProject = require('../../src/ios/addFileToProject');
const _ = require('lodash');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::addFileToProject', () => {
beforeEach(() => {
project.parseSync();
});
xit('should add file to a project', () => {
expect(
_.includes(
Object.keys(project.pbxFileReferenceSection()),
addFileToProject(project, '../../__fixtures__/linearGradient.pbxproj').fileRef
)
).toBeTruthy();
});
});
@@ -1,14 +1,18 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const path = require('path');
const PbxFile = require('xcode/lib/pbxFile');
const addProjectToLibraries = require('../../src/ios/addProjectToLibraries');
const last = require('lodash').last;
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::addProjectToLibraries', () => {
beforeEach(() => {
project.parseSync();
});
@@ -21,8 +25,7 @@ describe('ios::addProjectToLibraries', () => {
const child = last(libraries.children);
expect(child).to.have.keys(['value', 'comment']);
expect(child.comment).to.equals(file.basename);
expect((['value', 'comment']), child).toBeTruthy();
expect(child.comment).toBe(file.basename);
});
});
@@ -0,0 +1,45 @@
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const path = require('path');
const addSharedLibraries = require('../../src/ios/addSharedLibraries');
const getGroup = require('../../src/ios/getGroup');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::addSharedLibraries', () => {
beforeEach(() => {
project.parseSync();
});
it('should automatically create Frameworks group', () => {
expect(getGroup(project, 'Frameworks')).toBeNull();
addSharedLibraries(project, ['libz.tbd']);
expect(getGroup(project, 'Frameworks')).not.toBeNull();
});
it('should add shared libraries to project', () => {
addSharedLibraries(project, ['libz.tbd']);
const frameworksGroup = getGroup(project, 'Frameworks');
expect(frameworksGroup.children.length).toEqual(1);
expect(frameworksGroup.children[0].comment).toEqual('libz.tbd');
addSharedLibraries(project, ['MessageUI.framework']);
expect(frameworksGroup.children.length).toEqual(2);
});
it('should not add duplicate libraries to project', () => {
addSharedLibraries(project, ['libz.tbd']);
addSharedLibraries(project, ['libz.tbd']);
const frameworksGroup = getGroup(project, 'Frameworks');
expect(frameworksGroup.children.length).toEqual(1);
});
});
@@ -1,21 +1,25 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const path = require('path');
const createGroup = require('../../src/ios/createGroup');
const getGroup = require('../../src/ios/getGroup');
const last = require('lodash').last;
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::createGroup', () => {
beforeEach(() => {
project.parseSync();
});
it('should create a group with given name', () => {
const createdGroup = createGroup(project, 'Resources');
expect(createdGroup.name).to.equals('Resources');
expect(createdGroup.name).toBe('Resources');
});
it('should attach group to main project group', () => {
@@ -24,7 +28,7 @@ describe('ios::createGroup', () => {
expect(
last(mainGroup.children).comment
).to.equals(createdGroup.name);
).toBe(createdGroup.name);
});
it('should create a nested group with given path', () => {
@@ -33,7 +37,7 @@ describe('ios::createGroup', () => {
expect(
last(outerGroup.children).comment
).to.equals(createdGroup.name);
).toBe(createdGroup.name);
});
it('should-not create already created groups', () => {
@@ -42,8 +46,11 @@ describe('ios::createGroup', () => {
const mainGroup = getGroup(project);
expect(
mainGroup.children.filter(group => group.comment === 'Libraries').length
).to.equals(1);
expect(last(outerGroup.children).comment).to.equals(createdGroup.name);
mainGroup
.children
.filter(group => group.comment === 'Libraries')
.length
).toBe(1);
expect(last(outerGroup.children).comment).toBe(createdGroup.name);
});
});
@@ -1,19 +1,22 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const path = require('path');
const getBuildProperty = require('../../src/ios/getBuildProperty');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::getBuildProperty', () => {
beforeEach(() => {
project.parseSync();
});
it('should return build property from main target', () => {
const plistPath = getBuildProperty(project, 'INFOPLIST_FILE');
expect(plistPath).to.equals('"Basic/Info.plist"');
expect(plistPath).toEqual('"Basic/Info.plist"');
});
});
@@ -1,9 +1,14 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const getGroup = require('../../src/ios/getGroup');
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::getGroup', () => {
beforeEach(() => {
@@ -12,25 +17,25 @@ describe('ios::getGroup', () => {
it('should return a top-level group', () => {
const group = getGroup(project, 'Libraries');
expect(group.children.length > 0).to.be.true; // our test top-level Libraries has children
expect(group.name).to.equals('Libraries');
expect(group.children.length > 0).toBeTruthy();
expect(group.name).toBe('Libraries');
});
it('should return nested group when specified', () => {
const group = getGroup(project, 'NestedGroup/Libraries');
expect(group.children.length).to.equals(0); // our test nested Libraries is empty
expect(group.name).to.equals('Libraries');
expect(group.children.length).toBe(0); // our test nested Libraries is empty
expect(group.name).toBe('Libraries');
});
it('should return null when no group found', () => {
const group = getGroup(project, 'I-Dont-Exist');
expect(group).to.be.null;
expect(group).toBeNull();
});
it('should return top-level group when name not specified', () => {
const mainGroupId = project.getFirstProject().firstProject.mainGroup;
const mainGroup = project.getPBXGroupByKey(mainGroupId);
const group = getGroup(project);
expect(group).to.equals(mainGroup);
expect(group).toEqual(mainGroup);
});
});
@@ -1,12 +1,13 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const getHeaderSearchPath = require('../../src/ios/getHeaderSearchPath');
const path = require('path');
const SRC_DIR = path.join('react-native-project', 'ios');
describe('ios::getHeaderSearchPath', () => {
/**
* See https://github.com/Microsoft/react-native-code-push
*/
@@ -18,7 +19,7 @@ describe('ios::getHeaderSearchPath', () => {
const searchPath = getHeaderSearchPath(SRC_DIR, files);
expect(searchPath).to.equal(
expect(searchPath).toBe(
`"${['$(SRCROOT)', '..', 'node_modules', 'package'].join(path.sep)}"`
);
});
@@ -34,7 +35,7 @@ describe('ios::getHeaderSearchPath', () => {
const searchPath = getHeaderSearchPath(SRC_DIR, files);
expect(searchPath).to.equal(
expect(searchPath).toBe(
`"${['$(SRCROOT)', '..', 'node_modules', 'package', 'src'].join(path.sep)}/**"`
);
});
@@ -51,7 +52,7 @@ describe('ios::getHeaderSearchPath', () => {
const searchPath = getHeaderSearchPath(SRC_DIR, files);
expect(searchPath).to.equal(
expect(searchPath).toBe(
`"${['$(SRCROOT)', '..', 'node_modules', 'package', 'src'].join(path.sep)}/**"`
);
});
@@ -1,27 +1,27 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const getHeadersInFolder = require('../../src/ios/getHeadersInFolder');
const mock = require('mock-fs');
describe('ios::getHeadersInFolder', () => {
it('should return an array of all headers in given folder', () => {
mock({
xit('should return an array of all headers in given folder', () => {
jest.setMock({
'FileA.h': '',
'FileB.h': '',
});
const foundHeaders = getHeadersInFolder(process.cwd());
expect(foundHeaders.length).to.equals(2);
expect(foundHeaders.length).toBe(2);
getHeadersInFolder(process.cwd()).forEach(headerPath => {
expect(headerPath).to.contain(process.cwd());
});
});
it('should ignore all headers in Pods, Examples & node_modules', () => {
mock({
xit('should ignore all headers in Pods, Examples & node_modules', () => {
jest.setMock({
'FileA.h': '',
'FileB.h': '',
Pods: {
@@ -37,9 +37,4 @@ describe('ios::getHeadersInFolder', () => {
expect(getHeadersInFolder(process.cwd()).length).to.equals(2);
});
afterEach(() => {
mock.restore();
});
});
@@ -1,23 +1,24 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const getPlist = require('../../src/ios/getPlist');
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::getPlist', () => {
beforeEach(() => {
project.parseSync();
});
it('should return null when `.plist` file missing', () => {
const plistPath = getPlist(project, process.cwd());
expect(plistPath).to.equals(null);
});
it.skip('should return parsed `plist`', () => {
// @todo mock fs here
expect(plistPath).toBeNull();
});
// @todo - Happy scenario
});
@@ -1,19 +1,22 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const getPlistPath = require('../../src/ios/getPlistPath');
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::getPlistPath', () => {
beforeEach(() => {
project.parseSync();
});
it('should return path without Xcode $(SRCROOT)', () => {
const plistPath = getPlistPath(project, '/');
expect(plistPath).to.equals('/Basic/Info.plist');
expect(plistPath).toBe('/Basic/Info.plist');
});
});
@@ -1,20 +1,23 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const getProducts = require('../../src/ios/getProducts');
const path = require('path');
const project = xcode.project('test/fixtures/linearGradient.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::getProducts', () => {
beforeEach(() => {
project.parseSync();
});
it('should return an array of static libraries project exports', () => {
const products = getProducts(project);
expect(products.length).to.equals(1);
expect(products).to.contains('libBVLinearGradient.a');
expect(products.length).toBe(1);
expect(products).toContain('libRCTActionSheet.a');
});
});
@@ -1,24 +1,27 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const hasLibraryImported = require('../../src/ios/hasLibraryImported');
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::hasLibraryImported', () => {
beforeEach(() => {
project.parseSync();
});
it('should return true if project has been already imported', () => {
const libraries = project.pbxGroupByName('Libraries');
expect(hasLibraryImported(libraries, 'React.xcodeproj')).to.be.true;
expect(hasLibraryImported(libraries, 'React.xcodeproj')).toBeTruthy();
});
it('should return false if project is not imported', () => {
const libraries = project.pbxGroupByName('Libraries');
expect(hasLibraryImported(libraries, 'ACME.xcodeproj')).to.be.false;
expect(hasLibraryImported(libraries, 'ACME.xcodeproj')).toBeFalsy();
});
});
@@ -1,39 +1,29 @@
const chai = require('chai');
const expect = chai.expect;
const mock = require('mock-fs');
const fs = require('fs');
'use strict';
jest.autoMockOff();
const path = require('path');
const isInstalled = require('../../src/ios/isInstalled');
const baseProjectConfig = {
pbxprojPath: 'project.pbxproj',
pbxprojPath: path.join(__dirname, '../../__fixtures__/project.pbxproj'),
libraryFolder: 'Libraries',
};
describe('ios::isInstalled', () => {
before(() => {
mock({
'project.pbxproj': fs.readFileSync(path.join(__dirname, '../fixtures/project.pbxproj')),
});
});
it('should return true when .xcodeproj in Libraries', () => {
const dependencyConfig = { projectName: 'React.xcodeproj' };
expect(isInstalled(baseProjectConfig, dependencyConfig)).to.be.true;
expect(isInstalled(baseProjectConfig, dependencyConfig)).toBeTruthy();
});
it('should return false when .xcodeproj not in Libraries', () => {
const dependencyConfig = { projectName: 'Missing.xcodeproj' };
expect(isInstalled(baseProjectConfig, dependencyConfig)).to.be.false;
expect(isInstalled(baseProjectConfig, dependencyConfig)).toBeFalsy();
});
it('should return false when `LibraryFolder` is missing', () => {
const dependencyConfig = { projectName: 'React.xcodeproj' };
const projectConfig = Object.assign({}, baseProjectConfig, { libraryFolder: 'Missing' });
expect(isInstalled(projectConfig, dependencyConfig)).to.be.false;
expect(isInstalled(projectConfig, dependencyConfig)).toBeFalsy();
});
after(mock.restore);
});
@@ -1,23 +1,24 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const mapHeaderSearchPaths = require('../../src/ios/mapHeaderSearchPaths');
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
const reactPath = '"$(SRCROOT)/../node_modules/react-native/React/**"';
describe('ios::mapHeaderSearchPaths', () => {
beforeEach(() => {
project.parseSync();
});
it('should iterate over headers with `react` added only', () => {
const path = '../../node_modules/path-to-module/**';
mapHeaderSearchPaths(project, paths => {
expect(paths.find(path => path.indexOf(reactPath))).to.be.not.empty;
expect(paths.find(p => p.indexOf(reactPath))).toBeDefined();
});
});
});
@@ -1,15 +1,19 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const PbxFile = require('xcode/lib/pbxFile');
const addProjectToLibraries = require('../../src/ios/addProjectToLibraries');
const removeProjectFromLibraries = require('../../src/ios/removeProjectFromLibraries');
const last = require('lodash').last;
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
describe('ios::removeProjectFromLibraries', () => {
beforeEach(() => {
project.parseSync();
@@ -27,7 +31,6 @@ describe('ios::removeProjectFromLibraries', () => {
const child = last(libraries.children);
expect(child.comment).to.not.equals(file.basename);
expect(child.comment).not.toBe(file.basename);
});
});
@@ -1,32 +1,36 @@
const chai = require('chai');
const expect = chai.expect;
'use strict';
jest.autoMockOff();
const xcode = require('xcode');
const pbxFile = require('xcode/lib/pbxFile');
const addFileToProject = require('../../src/ios/addFileToProject');
const removeProjectFromProject = require('../../src/ios/removeProjectFromProject');
const path = require('path');
const project = xcode.project('test/fixtures/project.pbxproj');
const filePath = '../fixtures/linearGradient.pbxproj';
const project = xcode.project(
path.join(__dirname, '../../__fixtures__/project.pbxproj')
);
const filePath = '../../__fixtures__/linearGradient.pbxproj';
describe('ios::addFileToProject', () => {
beforeEach(() => {
project.parseSync();
addFileToProject(project, filePath);
});
it('should return removed file', () => {
expect(removeProjectFromProject(project, filePath)).to.be.instanceof(pbxFile);
expect(removeProjectFromProject(project, filePath) instanceof pbxFile)
.toBeTruthy();
});
it('should remove file from a project', () => {
const file = removeProjectFromProject(project, filePath);
expect(project.pbxFileReferenceSection()).to.not.include.keys(file.fileRef);
expect(project.pbxFileReferenceSection()[file.fileRef]).not.toBeDefined();
});
it.skip('should remove file from PBXContainerProxy', () => {
xit('should remove file from PBXContainerProxy', () => {
// todo(mike): add in .xcodeproj after Xcode modifications so we can test extra
// removals later.
});
});

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