Eject CLI command to re-create native folders
Summary: The iOS and Android native folders of a React Native app can be difficult to maintain. This introduces a new workflow for creating and maintaining the native code of your app. Now it will be possible to: 1. Remove the native iOS or Android folders 2. Create an `app.json` for your app, with at least a `name` and `displayName` 3. Run `react-native eject`, and the native code for your app will be generated Then, as usual, you can run `react-native run-ios` and `react-native run-android`, to build and launch your app For apps that don't have any native code, it will be possible to ignore the `ios` and `android` folders from version control. Eject step tested in RN app by deleting native folders. mkonicek, what is the best way to test `react-native init`? As follow-up items, we can enable the following: - Configuring app icon and launch screen from the `app.json` - Automatically run `react-native link` for native libraries - A Closes https://github.com/facebook/react-native/pull/12162 Differential Revision: D4509138 Pulled By: ericvicenti fbshipit-source-id: 0ee213e68f0a3d44bfce337e3ec43e5024bacc66
This commit is contained in:
parent
0a71f48b13
commit
bc74dd1e14
|
@ -42,6 +42,7 @@ const documentedCommands = [
|
|||
require('./library/library'),
|
||||
require('./bundle/bundle'),
|
||||
require('./bundle/unbundle'),
|
||||
require('./eject/eject'),
|
||||
require('./link/link'),
|
||||
require('./link/unlink'),
|
||||
require('./install/install'),
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* 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 copyProjectTemplateAndReplace = require('../generator/copyProjectTemplateAndReplace');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* The eject command re-creates the `android` and `ios` native folders. Because native code can be
|
||||
* difficult to maintain, this new script allows an `app.json` to be defined for the project, which
|
||||
* is used to configure the native app.
|
||||
*
|
||||
* The `app.json` config may contain the following keys:
|
||||
*
|
||||
* - `name` - The short name used for the project, should be TitleCase
|
||||
* - `displayName` - The app's name on the home screen
|
||||
*/
|
||||
|
||||
function eject() {
|
||||
|
||||
const doesIOSExist = fs.existsSync(path.resolve('ios'));
|
||||
const doesAndroidExist = fs.existsSync(path.resolve('android'));
|
||||
if (doesIOSExist && doesAndroidExist) {
|
||||
console.error(
|
||||
'Both the iOS and Android folders already exist! Please delete `ios` and/or `android` ' +
|
||||
'before ejecting.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let appConfig = null;
|
||||
try {
|
||||
appConfig = require(path.resolve('app.json'));
|
||||
} catch(e) {
|
||||
console.error(
|
||||
`Eject requires an \`app.json\` config file to be located at ` +
|
||||
`${path.resolve('app.json')}, and it must at least specify a \`name\` for the project ` +
|
||||
`name, and a \`displayName\` for the app's home screen label.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const appName = appConfig.name;
|
||||
if (!appName) {
|
||||
console.error(
|
||||
`App \`name\` must be defined in the \`app.json\` config file to define the project name. `+
|
||||
`It must not contain any spaces or dashes.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
const displayName = appConfig.displayName;
|
||||
if (!displayName) {
|
||||
console.error(
|
||||
`App \`displayName\` must be defined in the \`app.json\` config file, to define the label ` +
|
||||
`of the app on the home screen.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const templateOptions = { displayName };
|
||||
|
||||
if (!doesIOSExist) {
|
||||
console.log('Generating the iOS folder.');
|
||||
copyProjectTemplateAndReplace(
|
||||
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld', 'ios'),
|
||||
path.resolve('ios'),
|
||||
appName,
|
||||
templateOptions
|
||||
);
|
||||
}
|
||||
|
||||
if (!doesAndroidExist) {
|
||||
console.log('Generating the Android folder.');
|
||||
copyProjectTemplateAndReplace(
|
||||
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld', 'android'),
|
||||
path.resolve('android'),
|
||||
appName,
|
||||
templateOptions
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'eject',
|
||||
description: 'Re-create the iOS and Android folders and native code',
|
||||
func: eject,
|
||||
options: [],
|
||||
};
|
|
@ -27,10 +27,12 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
|
|||
if (!destPath) { throw new Error('Need a path to copy to'); }
|
||||
if (!newProjectName) { throw new Error('Need a project name'); }
|
||||
|
||||
options = options || {};
|
||||
|
||||
walk(srcPath).forEach(absoluteSrcFilePath => {
|
||||
|
||||
// 'react-native upgrade'
|
||||
if (options && options.upgrade) {
|
||||
if (options.upgrade) {
|
||||
// Don't upgrade these files
|
||||
const fileName = path.basename(absoluteSrcFilePath);
|
||||
// This also includes __tests__/index.*.js
|
||||
|
@ -44,7 +46,7 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
|
|||
.replace(/helloworld/g, newProjectName.toLowerCase());
|
||||
|
||||
let contentChangedCallback = null;
|
||||
if (options && options.upgrade && (!options.force)) {
|
||||
if (options.upgrade && (!options.force)) {
|
||||
contentChangedCallback = (_, contentChanged) => {
|
||||
return upgradeFileContentChangedCallback(
|
||||
absoluteSrcFilePath,
|
||||
|
@ -57,6 +59,7 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
|
|||
absoluteSrcFilePath,
|
||||
path.resolve(destPath, relativeRenamedPath),
|
||||
{
|
||||
'Hello App Display Name': options.displayName || newProjectName,
|
||||
'HelloWorld': newProjectName,
|
||||
'helloworld': newProjectName.toLowerCase(),
|
||||
},
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">HelloWorld</string>
|
||||
<string name="app_name">Hello App Display Name</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "HelloWorld",
|
||||
"displayName": "HelloWorld"
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Hello App Display Name</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
|
Loading…
Reference in New Issue