diff --git a/docs/generated/packages/react-native/executors/build-ios.json b/docs/generated/packages/react-native/executors/build-ios.json index 9f1d67083d6010..e685f222251555 100644 --- a/docs/generated/packages/react-native/executors/build-ios.json +++ b/docs/generated/packages/react-native/executors/build-ios.json @@ -11,11 +11,7 @@ "presets": [ { "name": "Build iOS for a simulator", "keys": ["simulator"] }, { "name": "Build iOS for a device", "keys": ["device"] }, - { "name": "Build iOS for a device with udid", "keys": ["udid"] }, - { - "name": "Run `pod install` before building iOS app", - "keys": ["install"] - } + { "name": "Build iOS for a device with udid", "keys": ["udid"] } ], "properties": { "simulator": { diff --git a/package.json b/package.json index 5f9b164411611f..0bbe6e425774c7 100644 --- a/package.json +++ b/package.json @@ -204,7 +204,8 @@ "magic-string": "~0.26.2", "markdown-factory": "^0.0.6", "memfs": "^3.0.1", - "metro-resolver": "^0.74.1", + "metro-config": "^0.76.5", + "metro-resolver": "^0.76.5", "mini-css-extract-plugin": "~2.4.7", "minimatch": "3.0.5", "next-sitemap": "^3.1.10", @@ -349,4 +350,3 @@ ] } } - diff --git a/packages/expo/package.json b/packages/expo/package.json index 50ac01e23aace7..14c7bb73128e7e 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -29,7 +29,6 @@ "chalk": "^4.1.0", "enhanced-resolve": "^5.8.3", "fs-extra": "^11.1.0", - "metro-resolver": "^0.74.1", "node-fetch": "^2.6.7", "tar-fs": "^2.1.1", "tsconfig-paths": "^4.1.2", diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 0231e5e88d8ef3..55bc14f604dfd1 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -30,7 +30,6 @@ "fs-extra": "^11.1.0", "glob": "7.1.4", "ignore": "^5.0.4", - "metro-resolver": "^0.74.1", "minimatch": "3.0.5", "node-fetch": "^2.6.7", "tsconfig-paths": "^4.1.2", @@ -42,9 +41,6 @@ "@nx/react": "file:../react", "@nx/workspace": "file:../workspace" }, - "peerDependencies": { - "react-native": ">= 0.71.0" - }, "builders": "./executors.json", "ng-update": { "requirements": {}, diff --git a/packages/react-native/plugins/with-nx-metro.ts b/packages/react-native/plugins/with-nx-metro.ts index 6bf66f767af324..28496245bdbeaf 100644 --- a/packages/react-native/plugins/with-nx-metro.ts +++ b/packages/react-native/plugins/with-nx-metro.ts @@ -1,4 +1,6 @@ import { workspaceLayout, workspaceRoot } from '@nx/devkit'; +import { MetroConfig, mergeConfig } from 'metro-config'; +const { getDefaultConfig } = require('metro-config'); // use require for typing issues of getDefaultConfig import { join } from 'path'; import { existsSync } from 'fs-extra'; @@ -11,32 +13,35 @@ interface WithNxOptions { watchFolders?: string[]; } -export function withNxMetro(config: any, opts: WithNxOptions = {}) { +export async function withNxMetro( + userConfig: MetroConfig, + opts: WithNxOptions = {} +) { + const defaultConfig = await getDefaultConfig( + opts.projectRoot || workspaceRoot + ); const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json']; if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true'; if (opts.extensions) extensions.push(...opts.extensions); - config.projectRoot = opts.projectRoot || workspaceRoot; - - // Add support for paths specified by tsconfig - config.resolver = { - ...config.resolver, - resolveRequest: getResolveRequest(extensions), - }; - - let watchFolders = config.watchFolders || []; - watchFolders = watchFolders.concat([ + let watchFolders = [ join(workspaceRoot, 'node_modules'), join(workspaceRoot, workspaceLayout().libsDir), join(workspaceRoot, 'packages'), join(workspaceRoot, '.storybook'), - ]); + ]; if (opts.watchFolders?.length) { watchFolders = watchFolders.concat(opts.watchFolders); } watchFolders = watchFolders.filter((folder) => existsSync(folder)); - config.watchFolders = watchFolders; - return config; + const nxConfig: MetroConfig = { + resolver: { + resolveRequest: getResolveRequest(extensions), + }, + watchFolders, + }; + + return mergeConfig(defaultConfig, userConfig, nxConfig); } diff --git a/packages/react-native/src/executors/build-android/build-android.impl.ts b/packages/react-native/src/executors/build-android/build-android.impl.ts index e6082d2d64f537..a1158f7a77e62e 100644 --- a/packages/react-native/src/executors/build-android/build-android.impl.ts +++ b/packages/react-native/src/executors/build-android/build-android.impl.ts @@ -1,62 +1,36 @@ -import { ExecutorContext, names } from '@nx/devkit'; -import { join } from 'path'; -import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; +import { ExecutorContext, runExecutor } from '@nx/devkit'; +import { join, resolve as pathResolve } from 'path'; import { ChildProcess, fork } from 'child_process'; import { ReactNativeBuildAndroidOptions } from './schema'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; -import { runCliStart } from '../start/start.impl'; -import { - displayNewlyAddedDepsMessage, - syncDeps, -} from '../sync-deps/sync-deps.impl'; +import startExecutor, { runCliStart } from '../start/start.impl'; import { getCliOptions } from '../../utils/get-cli-options'; export interface ReactNativeBuildOutput { success: boolean; } -let childProcess: ChildProcess; - export default async function* buildAndroidExecutor( options: ReactNativeBuildAndroidOptions, context: ExecutorContext ): AsyncGenerator { const projectRoot = context.projectsConfigurations.projects[context.projectName].root; - ensureNodeModulesSymlink(context.root, projectRoot); - if (options.sync) { - displayNewlyAddedDepsMessage( - context.projectName, - await syncDeps( - context.projectName, - projectRoot, - context.root, - context.projectGraph - ) - ); - } - chmodAndroidGradlewFiles(join(projectRoot, 'android')); - try { - const tasks = [runCliBuild(context.root, projectRoot, options)]; - if (options.packager && options.mode !== 'release') { - tasks.push( - runCliStart(context.root, projectRoot, { - port: options.port, - resetCache: options.resetCache, - interactive: options.interactive, - }) - ); - } - - await Promise.all(tasks); - yield { success: true }; - } finally { - if (childProcess) { - childProcess.kill(); + runCliBuild(context.root, projectRoot, options); + if (options.packager && options.mode !== 'release') { + const startResults = startExecutor(options, context); + for await (const result of startResults) { + if (!result.success) { + return result; + } + yield { + success: true, + }; } } + yield { success: true }; } function runCliBuild( @@ -64,30 +38,37 @@ function runCliBuild( projectRoot: string, options: ReactNativeBuildAndroidOptions ) { - return new Promise((resolve, reject) => { + return new Promise((res, reject) => { /** * Call the react native cli with option `--no-packager` * Not passing '--packager' due to cli will launch start command from the project root */ - childProcess = fork( - join(workspaceRoot, './node_modules/react-native/cli.js'), + const childProcess = fork( + require.resolve('react-native/cli.js'), ['build-android', ...createBuildAndroidOptions(options), '--no-packager'], { - cwd: join(workspaceRoot, projectRoot), + stdio: 'inherit', + cwd: pathResolve(workspaceRoot, projectRoot), env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, } ); - // Ensure the child process is killed when the parent exits - process.on('exit', () => childProcess.kill()); - process.on('SIGTERM', () => childProcess.kill()); + /** + * Ensure the child process is killed when the parent exits + */ + const processExitListener = (signal?: number | NodeJS.Signals) => () => + childProcess.kill(signal); + process.on('exit', processExitListener); + process.on('SIGTERM', processExitListener); + process.on('SIGINT', processExitListener); + process.on('SIGQUIT', processExitListener); childProcess.on('error', (err) => { reject(err); }); childProcess.on('exit', (code) => { if (code === 0) { - resolve(code); + res(childProcess); } else { reject(code); } diff --git a/packages/react-native/src/executors/build-android/schema.d.ts b/packages/react-native/src/executors/build-android/schema.d.ts index 24c8d29ce7401f..83b279d2f09adc 100644 --- a/packages/react-native/src/executors/build-android/schema.d.ts +++ b/packages/react-native/src/executors/build-android/schema.d.ts @@ -20,10 +20,9 @@ export interface ReactNativeBuildAndroidOptions // react native options mode: string; // default is debug activeArchOnly: boolean; // default is false - port: number; // default is 8081 tasks?: Array; extraParams?: Array; - interactive?: boolean; + interactive: boolean; // nx options packager: boolean; // default is true diff --git a/packages/react-native/src/executors/build-ios/build-ios.impl.ts b/packages/react-native/src/executors/build-ios/build-ios.impl.ts index 7b58775717f8b9..305a7c5c9c6893 100644 --- a/packages/react-native/src/executors/build-ios/build-ios.impl.ts +++ b/packages/react-native/src/executors/build-ios/build-ios.impl.ts @@ -1,14 +1,8 @@ import { ExecutorContext } from '@nx/devkit'; -import { join } from 'path'; +import { resolve } from 'path'; import { ChildProcess, fork } from 'child_process'; import { platform } from 'os'; -import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; -import { - displayNewlyAddedDepsMessage, - syncDeps, -} from '../sync-deps/sync-deps.impl'; -import { podInstall } from '../../utils/pod-install-task'; import { ReactNativeBuildIosOptions } from './schema'; import { runCliStart } from '../start/start.impl'; import { getCliOptions } from '../../utils/get-cli-options'; @@ -17,9 +11,7 @@ export interface ReactNativeBuildIosOutput { success: boolean; } -let childProcess: ChildProcess; - -export default async function* runIosExecutor( +export default async function* buildIosExecutor( options: ReactNativeBuildIosOptions, context: ExecutorContext ): AsyncGenerator { @@ -28,77 +20,57 @@ export default async function* runIosExecutor( } const projectRoot = context.projectsConfigurations.projects[context.projectName].root; - ensureNodeModulesSymlink(context.root, projectRoot); - if (options.sync) { - displayNewlyAddedDepsMessage( - context.projectName, - await syncDeps( - context.projectName, - projectRoot, - context.root, - context.projectGraph - ) - ); - } - if (options.install) { - await podInstall( - join(context.root, projectRoot, 'ios'), - options.buildFolder + const tasks = [runCliBuildIOS(context.root, projectRoot, options)]; + if (options.packager && options.mode !== 'Release') { + tasks.push( + runCliStart(context.root, projectRoot, { + port: options.port, + resetCache: options.resetCache, + interactive: options.interactive, + }) ); } - try { - const tasks = [runCliBuildIOS(context.root, projectRoot, options)]; - if (options.packager && options.mode !== 'Release') { - tasks.push( - runCliStart(context.root, projectRoot, { - port: options.port, - resetCache: options.resetCache, - interactive: options.interactive, - }) - ); - } - - await Promise.all(tasks); + await Promise.all(tasks); - yield { success: true }; - } finally { - if (childProcess) { - childProcess.kill(); - } - } + yield { success: true }; } function runCliBuildIOS( workspaceRoot: string, projectRoot: string, options: ReactNativeBuildIosOptions -) { - return new Promise((resolve, reject) => { +): Promise { + return new Promise((res, reject) => { /** * Call the react native cli with option `--no-packager` * Not passing '--packager' due to cli will launch start command from the project root */ - childProcess = fork( - join(workspaceRoot, './node_modules/react-native/cli.js'), + const childProcess = fork( + require.resolve('react-native/cli.js'), ['build-ios', ...createBuildIOSOptions(options)], { - cwd: join(workspaceRoot, projectRoot), + cwd: resolve(workspaceRoot, projectRoot), env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, } ); - // Ensure the child process is killed when the parent exits - process.on('exit', () => childProcess.kill()); - process.on('SIGTERM', () => childProcess.kill()); - + /** + * Ensure the child process is killed when the parent exits + */ + const processExitListener = (signal?: number | NodeJS.Signals) => () => + childProcess.kill(signal); + process.on('exit', processExitListener); + process.on('SIGTERM', processExitListener); + process.on('SIGINT', processExitListener); + process.on('SIGQUIT', processExitListener); childProcess.on('error', (err) => { reject(err); }); childProcess.on('exit', (code) => { if (code === 0) { - resolve(code); + res(childProcess); } else { reject(code); } diff --git a/packages/react-native/src/executors/build-ios/schema.d.ts b/packages/react-native/src/executors/build-ios/schema.d.ts index df4fac0ecc331a..436e20bf29b224 100644 --- a/packages/react-native/src/executors/build-ios/schema.d.ts +++ b/packages/react-native/src/executors/build-ios/schema.d.ts @@ -17,6 +17,8 @@ export interface ReactNativeBuildIosOptions extends ReactNativeStartOptions { // nx options packager: boolean; // default is true + // @deprecated, add to pod-install to dependOn install: boolean; // default is true + // @deprecated, add to sync-deps to dependOn sync: boolean; // default is true } diff --git a/packages/react-native/src/executors/build-ios/schema.json b/packages/react-native/src/executors/build-ios/schema.json index a23017767b4113..f0c26b631f8428 100644 --- a/packages/react-native/src/executors/build-ios/schema.json +++ b/packages/react-native/src/executors/build-ios/schema.json @@ -17,10 +17,6 @@ { "name": "Build iOS for a device with udid", "keys": ["udid"] - }, - { - "name": "Run `pod install` before building iOS app", - "keys": ["install"] } ], "properties": { diff --git a/packages/react-native/src/executors/bundle/bundle.impl.ts b/packages/react-native/src/executors/bundle/bundle.impl.ts index 42f4a9004465f3..2a4bf0dd406512 100644 --- a/packages/react-native/src/executors/bundle/bundle.impl.ts +++ b/packages/react-native/src/executors/bundle/bundle.impl.ts @@ -1,18 +1,14 @@ import { createDirectory } from '@nx/workspace/src/utilities/fileutils'; import { names, ExecutorContext } from '@nx/devkit'; -import { dirname, join } from 'path'; +import { dirname, join, resolve } from 'path'; import { ChildProcess, fork } from 'child_process'; -import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; - import { ReactNativeBundleOptions } from './schema'; export interface ReactNativeBundleOutput { success: boolean; } -let childProcess: ChildProcess; - export default async function* bundleExecutor( options: ReactNativeBundleOptions, context: ExecutorContext @@ -23,41 +19,44 @@ export default async function* bundleExecutor( options.bundleOutput = join(context.root, options.bundleOutput); createDirectory(dirname(options.bundleOutput)); - ensureNodeModulesSymlink(context.root, projectRoot); - try { - await runCliBuild(context.root, projectRoot, options); - yield { success: true }; - } finally { - if (childProcess) { - childProcess.kill(); - } - } + await runCliBuild(context.root, projectRoot, options); + yield { success: true }; } function runCliBuild( workspaceRoot: string, projectRoot: string, options: ReactNativeBundleOptions -) { - return new Promise((resolve, reject) => { +): Promise { + return new Promise((res, reject) => { const cliOptions = createBundleOptions(options); - childProcess = fork( - join(workspaceRoot, './node_modules/react-native/cli.js'), + const childProcess = fork( + require.resolve('react-native/cli.js'), ['bundle', ...cliOptions], - { cwd: join(workspaceRoot, projectRoot), env: process.env } + { + stdio: 'inherit', + cwd: resolve(workspaceRoot, projectRoot), + env: process.env, + } ); - // Ensure the child process is killed when the parent exits - process.on('exit', () => childProcess.kill()); - process.on('SIGTERM', () => childProcess.kill()); + /** + * Ensure the child process is killed when the parent exits + */ + const processExitListener = (signal?: number | NodeJS.Signals) => () => + childProcess.kill(signal); + process.on('exit', processExitListener); + process.on('SIGTERM', processExitListener); + process.on('SIGINT', processExitListener); + process.on('SIGQUIT', processExitListener); childProcess.on('error', (err) => { reject(err); }); childProcess.on('exit', (code) => { if (code === 0) { - resolve(code); + res(childProcess); } else { reject(code); } diff --git a/packages/react-native/src/executors/run-android/run-android.impl.ts b/packages/react-native/src/executors/run-android/run-android.impl.ts index a73b3c197bbb95..ccb9bcea36face 100644 --- a/packages/react-native/src/executors/run-android/run-android.impl.ts +++ b/packages/react-native/src/executors/run-android/run-android.impl.ts @@ -1,14 +1,9 @@ import { ExecutorContext } from '@nx/devkit'; -import { join } from 'path'; +import { join, resolve as pathResolve } from 'path'; import { ChildProcess, fork } from 'child_process'; -import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; -import { - displayNewlyAddedDepsMessage, - syncDeps, -} from '../sync-deps/sync-deps.impl'; import { ReactNativeRunAndroidOptions } from './schema'; -import { runCliStart } from '../start/start.impl'; +import startExecutor, { runCliStart } from '../start/start.impl'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; import { getCliOptions } from '../../utils/get-cli-options'; @@ -24,72 +19,54 @@ export default async function* runAndroidExecutor( ): AsyncGenerator { const projectRoot = context.projectsConfigurations.projects[context.projectName].root; - ensureNodeModulesSymlink(context.root, projectRoot); chmodAndroidGradlewFiles(join(projectRoot, 'android')); - if (options.sync) { - displayNewlyAddedDepsMessage( - context.projectName, - await syncDeps( - context.projectName, - projectRoot, - context.root, - context.projectGraph - ) - ); + await runCliRunAndroid(context.root, projectRoot, options); + if (options.packager) { + return startExecutor(options, context); } - try { - const tasks = [runCliRunAndroid(context.root, projectRoot, options)]; - if (options.packager) { - tasks.push( - runCliStart(context.root, projectRoot, { - port: options.port, - resetCache: options.resetCache, - interactive: options.interactive, - }) - ); - } - - await Promise.all(tasks); + await Promise.all(tasks); - yield { success: true }; - } finally { - if (childProcess) { - childProcess.kill(); - } - } + yield { success: true }; } function runCliRunAndroid( workspaceRoot: string, projectRoot: string, options: ReactNativeRunAndroidOptions -) { - return new Promise((resolve, reject) => { +): Promise { + return new Promise((resolve, reject) => { /** * Call the react native cli with option `--no-packager` * Not passing '--packager' due to cli will launch start command from the project root */ childProcess = fork( - join(workspaceRoot, './node_modules/react-native/cli.js'), + require.resolve('react-native/cli.js'), ['run-android', ...createRunAndroidOptions(options), '--no-packager'], { - cwd: join(workspaceRoot, projectRoot), + stdio: 'inherit', + cwd: pathResolve(workspaceRoot, projectRoot), env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, } ); - // Ensure the child process is killed when the parent exits - process.on('exit', () => childProcess.kill()); - process.on('SIGTERM', () => childProcess.kill()); + /** + * Ensure the child process is killed when the parent exits + */ + const processExitListener = (signal?: number | NodeJS.Signals) => () => + childProcess.kill(signal); + process.on('exit', processExitListener); + process.on('SIGTERM', processExitListener); + process.on('SIGINT', processExitListener); + process.on('SIGQUIT', processExitListener); childProcess.on('error', (err) => { reject(err); }); childProcess.on('exit', (code) => { if (code === 0) { - resolve(code); + resolve(childProcess); } else { reject(code); } diff --git a/packages/react-native/src/executors/run-ios/run-ios.impl.ts b/packages/react-native/src/executors/run-ios/run-ios.impl.ts index 62b4581e7ef755..cf45d3c3866291 100644 --- a/packages/react-native/src/executors/run-ios/run-ios.impl.ts +++ b/packages/react-native/src/executors/run-ios/run-ios.impl.ts @@ -1,24 +1,16 @@ import { ExecutorContext } from '@nx/devkit'; -import { join } from 'path'; +import { resolve as pathResolve } from 'path'; import { ChildProcess, fork } from 'child_process'; import { platform } from 'os'; -import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; -import { - displayNewlyAddedDepsMessage, - syncDeps, -} from '../sync-deps/sync-deps.impl'; -import { podInstall } from '../../utils/pod-install-task'; -import { ReactNativeRunIosOptions } from './schema'; -import { runCliStart } from '../start/start.impl'; +import startExecutor from '../start/start.impl'; import { getCliOptions } from '../../utils/get-cli-options'; +import { ReactNativeRunIosOptions } from './schema'; export interface ReactNativeRunIosOutput { success: boolean; } -let childProcess: ChildProcess; - export default async function* runIosExecutor( options: ReactNativeRunIosOptions, context: ExecutorContext @@ -28,77 +20,51 @@ export default async function* runIosExecutor( } const projectRoot = context.projectsConfigurations.projects[context.projectName].root; - ensureNodeModulesSymlink(context.root, projectRoot); - if (options.sync) { - displayNewlyAddedDepsMessage( - context.projectName, - await syncDeps( - context.projectName, - projectRoot, - context.root, - context.projectGraph - ) - ); - } - if (options.install) { - await podInstall( - join(context.root, projectRoot, 'ios'), - options.buildFolder - ); + await runCliRunIOS(context.root, projectRoot, options); + if (options.packager && options.mode !== 'Release') { + return startExecutor(options, context); } - try { - const tasks = [runCliRunIOS(context.root, projectRoot, options)]; - if (options.packager && options.mode !== 'Release') { - tasks.push( - runCliStart(context.root, projectRoot, { - port: options.port, - resetCache: options.resetCache, - interactive: options.interactive, - }) - ); - } - - await Promise.all(tasks); - - yield { success: true }; - } finally { - if (childProcess) { - childProcess.kill(); - } - } + yield { success: true }; } function runCliRunIOS( workspaceRoot: string, projectRoot: string, options: ReactNativeRunIosOptions -) { - return new Promise((resolve, reject) => { +): Promise { + return new Promise((resolve, reject) => { /** * Call the react native cli with option `--no-packager` * Not passing '--packager' due to cli will launch start command from the project root */ - childProcess = fork( - join(workspaceRoot, './node_modules/react-native/cli.js'), + const childProcess = fork( + require.resolve('react-native/cli.js'), ['run-ios', ...createRunIOSOptions(options), '--no-packager'], { - cwd: join(workspaceRoot, projectRoot), + stdio: 'inherit', + cwd: pathResolve(workspaceRoot, projectRoot), env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, } ); - // Ensure the child process is killed when the parent exits - process.on('exit', () => childProcess.kill()); - process.on('SIGTERM', () => childProcess.kill()); + /** + * Ensure the child process is killed when the parent exits + */ + const processExitListener = (signal?: number | NodeJS.Signals) => () => + childProcess.kill(signal); + process.on('exit', processExitListener); + process.on('SIGTERM', processExitListener); + process.on('SIGINT', processExitListener); + process.on('SIGQUIT', processExitListener); childProcess.on('error', (err) => { reject(err); }); childProcess.on('exit', (code) => { if (code === 0) { - resolve(code); + resolve(childProcess); } else { reject(code); } diff --git a/packages/react-native/src/executors/start/start.impl.ts b/packages/react-native/src/executors/start/start.impl.ts index 8155da595bdeb4..c312a3572099b9 100644 --- a/packages/react-native/src/executors/start/start.impl.ts +++ b/packages/react-native/src/executors/start/start.impl.ts @@ -1,7 +1,7 @@ import * as chalk from 'chalk'; import { ExecutorContext, logger } from '@nx/devkit'; import { ChildProcess, fork } from 'child_process'; -import { join } from 'path'; +import { resolve as pathResolve } from 'path'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import { isPackagerRunning } from './lib/is-packager-running'; import { ReactNativeStartOptions } from './schema'; @@ -11,8 +11,6 @@ export interface ReactNativeStartOutput { success: boolean; } -let childProcess: ChildProcess; - export default async function* startExecutor( options: ReactNativeStartOptions, context: ExecutorContext @@ -21,27 +19,32 @@ export default async function* startExecutor( context.projectsConfigurations.projects[context.projectName].root; ensureNodeModulesSymlink(context.root, projectRoot); - try { - const baseUrl = `http://localhost:${options.port}`; - const appName = context.projectName; - logger.info(chalk.cyan(`Packager is ready at ${baseUrl}`)); - logger.info( - `Use ${chalk.bold(`nx run-android ${appName}`)} or ${chalk.bold( - `nx run-ios ${appName}` - )} to run the native app.` - ); + const baseUrl = `http://localhost:${options.port}`; + const appName = context.projectName; + logger.info(chalk.cyan(`Packager is ready at ${baseUrl}`)); + logger.info( + `Use ${chalk.bold(`nx run-android ${appName}`)} or ${chalk.bold( + `nx run-ios ${appName}` + )} to run the native app.` + ); + + const startProcess = await runCliStart(context.root, projectRoot, options); - await runCliStart(context.root, projectRoot, options); + yield { + baseUrl, + success: true, + }; - yield { - baseUrl, - success: true, + await new Promise((resolve) => { + const processExitListener = (signal?: number | NodeJS.Signals) => () => { + startProcess.kill(signal); + resolve(); }; - } finally { - if (childProcess) { - childProcess.kill(); - } - } + process.on('exit', processExitListener); + process.on('SIGTERM', processExitListener); + process.on('SIGINT', processExitListener); + process.on('SIGQUIT', processExitListener); + }); } /* @@ -52,7 +55,7 @@ export async function runCliStart( workspaceRoot: string, projectRoot: string, options: ReactNativeStartOptions -): Promise { +): Promise { const result = await isPackagerRunning(options.port); if (result === 'running') { logger.info('JS server already running.'); @@ -63,7 +66,7 @@ export async function runCliStart( logger.info('Starting JS server...'); try { - await startAsync(workspaceRoot, projectRoot, options); + return await startAsync(workspaceRoot, projectRoot, options); } catch (error) { logger.error( `Failed to start the packager server. Error details: ${error.message}` @@ -77,24 +80,34 @@ function startAsync( workspaceRoot: string, projectRoot: string, options: ReactNativeStartOptions -): Promise { - return new Promise((resolve, reject) => { - childProcess = fork( - join(workspaceRoot, './node_modules/react-native/cli.js'), +): Promise { + return new Promise((resolve, reject) => { + const childProcess = fork( + require.resolve('react-native/cli.js'), ['start', ...createStartOptions(options)], - { cwd: join(workspaceRoot, projectRoot), env: process.env } + { + cwd: pathResolve(workspaceRoot, projectRoot), + env: process.env, + stdio: 'pipe', + } ); - // Ensure the child process is killed when the parent exits - process.on('exit', () => childProcess.kill()); - process.on('SIGTERM', () => childProcess.kill()); + childProcess.stdout.on('data', (data) => { + process.stdout.write(data); + if (data.toString().includes('Packager is ready')) { + resolve(childProcess); + } + }); + childProcess.stderr.on('data', (data) => { + process.stderr.write(data); + }); childProcess.on('error', (err) => { reject(err); }); childProcess.on('exit', (code) => { if (code === 0) { - resolve(code); + resolve(childProcess); } else { reject(code); } diff --git a/packages/react-native/src/generators/application/files/app/.ruby-version b/packages/react-native/src/generators/application/files/app/.ruby-version deleted file mode 100644 index 49cdd668e1c82b..00000000000000 --- a/packages/react-native/src/generators/application/files/app/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.6 diff --git a/packages/react-native/src/generators/application/files/app/Gemfile.template b/packages/react-native/src/generators/application/files/app/Gemfile.template index 567e59805c4a73..1fa2c2e1abdee6 100644 --- a/packages/react-native/src/generators/application/files/app/Gemfile.template +++ b/packages/react-native/src/generators/application/files/app/Gemfile.template @@ -1,6 +1,6 @@ source 'https://rubygems.org' # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby File.read(File.join(__dir__, '.ruby-version')).strip +ruby ">= 2.6.10" -gem 'cocoapods', '~> 1.11', '>= 1.11.3' +gem 'cocoapods', '~> 1.12' diff --git a/packages/react-native/src/generators/application/files/app/android/app/build.gradle.template b/packages/react-native/src/generators/application/files/app/android/app/build.gradle.template index 8ce7b1ab639de5..1fc5a2aeb56438 100644 --- a/packages/react-native/src/generators/application/files/app/android/app/build.gradle.template +++ b/packages/react-native/src/generators/application/files/app/android/app/build.gradle.template @@ -1,8 +1,6 @@ apply plugin: "com.android.application" apply plugin: "com.facebook.react" -import com.android.build.OutputFile - /** * This is the configuration block to customize your React Native Android app. * By default you don't need to apply any configuration, just uncomment the lines you need. @@ -13,8 +11,8 @@ react { // root = file("../") // The folder where the react-native NPM package is. Default is ../node_modules/react-native // reactNativeDir = file("../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen - // codegenDir = file("../node_modules/react-native-codegen") + // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen + // codegenDir = file("../node_modules/@react-native/codegen") // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js // cliFile = file("../node_modules/react-native/cli.js") @@ -53,14 +51,6 @@ react { entryFile = file("../../<%= entryFile %>") } -/** - * Set this to true to create four separate APKs instead of one, - * one for each native architecture. This is useful if you don't - * use App Bundles (https://developer.android.com/guide/app-bundle/) - * and want to have separate APKs to upload to the Play Store. - */ -def enableSeparateBuildPerCPUArchitecture = false - /** * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ @@ -79,16 +69,6 @@ def enableProguardInReleaseBuilds = false */ def jscFlavor = 'org.webkit:android-jsc:+' -/** - * Private function to get the list of Native Architectures you want to build. - * This reads the value from reactNativeArchitectures in your gradle.properties - * file and works together with the --active-arch-only flag of react-native run-android. - */ -def reactNativeArchitectures() { - def value = project.getProperties().get("reactNativeArchitectures") - return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] -} - android { ndkVersion rootProject.ext.ndkVersion @@ -106,15 +86,6 @@ android { testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' <% } %> } - - splits { - abi { - reset() - enable enableSeparateBuildPerCPUArchitecture - universalApk false // If true, also generate a universal APK - include (*reactNativeArchitectures()) - } - } signingConfigs { debug { storeFile file('debug.keystore') @@ -138,22 +109,6 @@ android { <% } %> } } - - // applicationVariants are e.g. debug, release - applicationVariants.all { variant -> - variant.outputs.each { output -> - // For each separate APK per architecture, set a unique version code as described here: - // https://developer.android.com/studio/build/configure-apk-splits.html - // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. - def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] - def abi = output.getFilter(OutputFile.ABI) - if (abi != null) { // null for the universal-debug, universal-release variants - output.versionCodeOverride = - defaultConfig.versionCode * 1000 + versionCodes.get(abi) - } - - } - } } dependencies { @@ -164,8 +119,6 @@ dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") - implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") - debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group:'com.squareup.okhttp3', module:'okhttp' diff --git a/packages/react-native/src/generators/application/files/app/android/app/src/main/java/com/__lowerCaseName__/MainActivity.java.template b/packages/react-native/src/generators/application/files/app/android/app/src/main/java/com/__lowerCaseName__/MainActivity.java.template index 629bff52ba19fc..981e6233377b76 100644 --- a/packages/react-native/src/generators/application/files/app/android/app/src/main/java/com/__lowerCaseName__/MainActivity.java.template +++ b/packages/react-native/src/generators/application/files/app/android/app/src/main/java/com/__lowerCaseName__/MainActivity.java.template @@ -1,4 +1,4 @@ -package com.<%= lowerCaseName %>; +package com.<%= className %>; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; @@ -27,9 +27,6 @@ public class MainActivity extends ReactActivity { this, getMainComponentName(), // If you opted-in for the New Architecture, we enable the Fabric Renderer. - DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled - // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18). - DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled - ); + DefaultNewArchitectureEntryPoint.getFabricEnabled()); } } diff --git a/packages/react-native/src/generators/application/files/app/android/app/src/main/res/drawable/rn_edit_text_material.xml b/packages/react-native/src/generators/application/files/app/android/app/src/main/res/drawable/rn_edit_text_material.xml index f35d9962026a85..73b37e4d9963e2 100644 --- a/packages/react-native/src/generators/application/files/app/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ b/packages/react-native/src/generators/application/files/app/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -20,7 +20,7 @@ android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"> -