feat(react-native): upgrade react-native to 0.72 (#17810)

This commit is contained in:
Emily Xiong 2023-07-17 13:07:46 -04:00 committed by GitHub
parent 6529be96cf
commit 699a20ca03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 1652 additions and 777 deletions

View File

@ -50,7 +50,7 @@ nx start my-app
To generate a new library run:
```shell
npx nx g @nx/react-native:lib your-lib-name
npx nx g @nx/expo:lib your-lib-name
```
### Generating Components
@ -58,7 +58,7 @@ npx nx g @nx/react-native:lib your-lib-name
To generate a new component inside library run:
```shell
npx nx g @nx/react-native:component your-component-name --project=your-lib-name --export
npx nx g @nx/expo:component your-component-name --project=your-lib-name --export
```
Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json`
@ -247,6 +247,28 @@ To check the details of your build status, run:
nx build-list <app-name>
```
### Submit an EAS Build
EAS Submit is a hosted service for uploading and submitting your app binaries to the app stores. Since it's a hosted service, you can submit your app to both stores as long as you can run EAS CLI on your machine.
To submit an EAS build:
```shell
nx submit <app-name>
```
### Update an EAS Build
EAS Update is a hosted service that serves updates for projects using the `expo-updates` library.
EAS Update makes fixing small bugs and pushing quick fixes a snap in between app store submissions. It accomplishes this by allowing an end-user's app to swap out the non-native parts of their app (for example, JS, styling, and image changes) with a new update that contains bug fixes and other updates.
To update an EAS build:
```shell
nx update <app-name>
```
### Testing Projects
You can run unit tests with:
@ -269,6 +291,8 @@ Below table is a map between expo commands and Nx commands:
| `expo install` | `nx install <app-name>` |
| `eas build` | `nx build <app-name>` |
| `eas build:list` | `nx build-list <app-name>` |
| `eas update` | `nx update <app-name>` |
| `eas submit` | `nx submit <app-name>` |
## More Documentation

View File

@ -46,7 +46,8 @@
},
"output": {
"type": "string",
"description": "Output path for local build"
"description": "Output path for local build",
"examples": ["../../dist/MyApp.tar.gz", "../../dist"]
},
"wait": {
"type": "boolean",

View File

@ -42,6 +42,7 @@
"type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).",
"default": true,
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"x-priority": "internal"
},
"port": {

View File

@ -88,6 +88,7 @@
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true
}
},

View File

@ -45,7 +45,8 @@
"packager": {
"type": "boolean",
"description": "Launch packager while building",
"default": true
"default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
},
"port": {
"type": "number",
@ -53,8 +54,10 @@
"default": 8081
},
"tasks": {
"type": "array",
"items": { "type": "string" },
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [
"assembleDebug",
@ -71,8 +74,12 @@
"default": false
},
"extraParams": {
"type": "string",
"description": "Custom params passed to gradle build command"
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command",
"examples": ["-x lint -x test"]
},
"interactive": {
"type": "boolean",
@ -81,7 +88,8 @@
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"default": true
"default": true,
"x-deprecated": "Add sync-deps to dependsOn instead"
},
"resetCache": {
"type": "boolean",

View File

@ -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": {
@ -72,17 +68,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build"
},
"extraParams": {
"type": "string",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command."
},
"install": {
"type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.",
"x-deprecated": "Add pod-install to dependsOn in project.json for this target instead",
"default": true
},
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true
},
"resetCache": {
@ -93,7 +94,8 @@
"packager": {
"type": "boolean",
"description": "Launch packager while building",
"default": true
"default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
}
},
"required": [],

View File

@ -12,9 +12,19 @@
"type": "object",
"properties": {
"buildFolder": {
"description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory",
"description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.",
"type": "string",
"default": "./build"
},
"repoUpdate": {
"description": "Force running `pod repo update` before install.",
"type": "boolean",
"default": false
},
"deployment": {
"description": "Disallow any changes to the Podfile or the Podfile.lock during installation.",
"type": "boolean",
"default": false
}
},
"required": ["buildFolder"],

View File

@ -77,8 +77,10 @@
"default": 8081
},
"tasks": {
"type": "array",
"items": { "type": "string" },
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [
"assembleDebug",
@ -95,7 +97,10 @@
"default": false
},
"extraParams": {
"type": "string",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command"
},
"interactive": {
@ -105,6 +110,7 @@
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn for this target in project.json instead",
"default": true
},
"resetCache": {

View File

@ -13,11 +13,7 @@
"presets": [
{ "name": "Run iOS on a simulator", "keys": ["simulator"] },
{ "name": "Run iOS on a device", "keys": ["device"] },
{ "name": "Run iOS on a device with udid", "keys": ["udid"] },
{
"name": "Run `pod install` before building iOS app",
"keys": ["install"]
}
{ "name": "Run iOS on a device with udid", "keys": ["udid"] }
],
"properties": {
"xcodeConfiguration": {
@ -81,17 +77,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build"
},
"extraParams": {
"type": "string",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command."
},
"install": {
"type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.",
"default": true
"default": true,
"x-deprecated": "Add `pod-install` to dependsOn for this target in project.json instead."
},
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add `sync-deps` to dependsOn for this target in project.json instead.",
"default": true
},
"resetCache": {

View File

@ -50,7 +50,7 @@ nx start my-app
To generate a new library run:
```shell
npx nx g @nx/react-native:lib your-lib-name
npx nx g @nx/expo:lib your-lib-name
```
### Generating Components
@ -58,7 +58,7 @@ npx nx g @nx/react-native:lib your-lib-name
To generate a new component inside library run:
```shell
npx nx g @nx/react-native:component your-component-name --project=your-lib-name --export
npx nx g @nx/expo:component your-component-name --project=your-lib-name --export
```
Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json`
@ -247,6 +247,28 @@ To check the details of your build status, run:
nx build-list <app-name>
```
### Submit an EAS Build
EAS Submit is a hosted service for uploading and submitting your app binaries to the app stores. Since it's a hosted service, you can submit your app to both stores as long as you can run EAS CLI on your machine.
To submit an EAS build:
```shell
nx submit <app-name>
```
### Update an EAS Build
EAS Update is a hosted service that serves updates for projects using the `expo-updates` library.
EAS Update makes fixing small bugs and pushing quick fixes a snap in between app store submissions. It accomplishes this by allowing an end-user's app to swap out the non-native parts of their app (for example, JS, styling, and image changes) with a new update that contains bug fixes and other updates.
To update an EAS build:
```shell
nx update <app-name>
```
### Testing Projects
You can run unit tests with:
@ -269,6 +291,8 @@ Below table is a map between expo commands and Nx commands:
| `expo install` | `nx install <app-name>` |
| `eas build` | `nx build <app-name>` |
| `eas build:list` | `nx build-list <app-name>` |
| `eas update` | `nx update <app-name>` |
| `eas submit` | `nx submit <app-name>` |
## More Documentation

View File

@ -92,15 +92,15 @@ describe('expo', () => {
expect(prebuildResult.combinedOutput).toContain('Config synced');
});
// TODO(emily): expo-cli always fetches the latest version of react native
// re-enable it when expo-cli is fixed
// TODO(emily): this test failed due to version conflict with conflict with @config-plugins/detox
// https://github.com/expo/config-plugins/issues/178
xit('should install', async () => {
// run install command
const installResults = await runCLIAsync(
`install ${appName} --no-interactive --check`
`install ${appName} --no-interactive`
);
expect(installResults.combinedOutput).toContain(
'Dependencies are up to date'
'Successfully ran target install'
);
});

View File

@ -208,7 +208,8 @@
"magic-string": "~0.26.2",
"markdown-factory": "^0.0.6",
"memfs": "^3.0.1",
"metro-resolver": "^0.74.1",
"metro-config": "0.76.7",
"metro-resolver": "0.76.7",
"mini-css-extract-plugin": "~2.4.7",
"minimatch": "3.0.5",
"next-sitemap": "^3.1.10",

View File

@ -342,12 +342,6 @@ describe('detox application generator', () => {
'cd ../../../my-dir/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk',
},
'android.eas': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.apk',
build:
'npx nx run my-dir-my-app:download --platform android --distribution simulator --output=../../../my-dir/my-app/dist/',
type: 'android.apk',
},
'android.local': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.apk',
build:
@ -368,12 +362,6 @@ describe('detox application generator', () => {
"cd ../../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app',
},
'ios.eas': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.app',
build:
'npx nx run my-dir-my-app:download --platform ios --distribution simulator --output=../../../my-dir/my-app/dist/',
type: 'ios.app',
},
'ios.local': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.app',
build:

View File

@ -20,11 +20,6 @@
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Release-iphonesimulator/<%= appClassName %>.app"
},
<% if (framework === 'expo') { %>
"ios.eas": {
"type": "ios.app",
"build": "<%= exec %> nx run <%= appFileName %>:download --platform ios --distribution simulator --output=<%= offsetFromRoot %><%= appRoot %>/dist/",
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.app"
},
"ios.local": {
"type": "ios.app",
"build": "<%= exec %> nx run <%= appFileName %>:build --platform ios --profile preview --wait --local --no-interactive --output=<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.tar.gz",
@ -42,11 +37,6 @@
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/android/app/build/outputs/apk/release/app-release.apk"
},
<% if (framework === 'expo') { %>
"android.eas": {
"type": "android.apk",
"build": "<%= exec %> nx run <%= appFileName %>:download --platform android --distribution simulator --output=<%= offsetFromRoot %><%= appRoot %>/dist/",
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.apk"
},
"android.local": {
"type": "android.apk",
"build": "<%= exec %> nx run <%= appFileName %>:build --platform android --profile preview --wait --local --no-interactive --output=<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.apk",
@ -78,10 +68,6 @@
"app": "ios.debug"
},
<% if (framework === 'expo') { %>
"ios.sim.eas": {
"device": "simulator",
"app": "ios.eas"
},
"ios.sim.local": {
"device": "simulator",
"app": "ios.local"
@ -96,10 +82,6 @@
"app": "android.debug"
},
<% if (framework === 'expo') { %>
"android.emu.eas": {
"device": "emulator",
"app": "android.eas"
},
"android.emu.local": {
"device": "emulator",
"app": "android.local"

View File

@ -4,14 +4,10 @@ describe('getTargets', () => {
it('should return ios test target for expo projects', () => {
expect(expoTestTarget('ios.sim', 'test')).toEqual({
options: {
detoxConfiguration: 'ios.sim.eas',
detoxConfiguration: 'ios.sim.local',
buildTarget: 'test:build-ios',
},
configurations: {
local: {
detoxConfiguration: 'ios.sim.local',
buildTarget: 'test:build-ios:local',
},
bare: {
detoxConfiguration: 'ios.sim.debug',
buildTarget: 'test:build-ios:bare',
@ -27,12 +23,9 @@ describe('getTargets', () => {
it('should return ios build target for expo projects', () => {
expect(expoBuildTarget('ios.sim')).toEqual({
options: {
detoxConfiguration: 'ios.sim.eas',
detoxConfiguration: 'ios.sim.local',
},
configurations: {
local: {
detoxConfiguration: 'ios.sim.local',
},
bare: {
detoxConfiguration: 'ios.sim.debug',
},

View File

@ -14,12 +14,9 @@ export function reactNativeBuildTarget(platform: 'ios.sim' | 'android.emu') {
export function expoBuildTarget(platform: 'ios.sim' | 'android.emu') {
return {
options: {
detoxConfiguration: `${platform}.eas`,
detoxConfiguration: `${platform}.local`,
},
configurations: {
local: {
detoxConfiguration: `${platform}.local`,
},
bare: {
detoxConfiguration: `${platform}.debug`,
},
@ -58,14 +55,10 @@ export function expoTestTarget(
return {
options: {
detoxConfiguration: `${platform}.eas`,
detoxConfiguration: `${platform}.local`,
buildTarget: `${e2eName}:build-${buildPlatform}`,
},
configurations: {
local: {
detoxConfiguration: `${platform}.local`,
buildTarget: `${e2eName}:build-${buildPlatform}:local`,
},
bare: {
detoxConfiguration: `${platform}.debug`,
buildTarget: `${e2eName}:build-${buildPlatform}:bare`,

View File

@ -65,6 +65,18 @@
"cli": "nx",
"description": "Add app.json for detox",
"factory": "./src/migrations/update-16-1-4/add-detox-app-json"
},
"update-16-6-0-add-dependsOn": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Add dependsOn like ensure-symlink or sync-deps to targets",
"implementation": "./src/migrations/update-16-6-0/add-depends-on"
},
"update-16-6-0-update-metro-config": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Update metro.config.js to use the new metro config format",
"implementation": "./src/migrations/update-16-6-0/update-metro-config"
}
},
"packageJsonUpdates": {
@ -855,6 +867,67 @@
"alwaysAddToPackageJson": false
}
}
},
"16.6.0": {
"version": "16.6.0-beta.0",
"packages": {
"expo": {
"version": "^49.0.3",
"alwaysAddToPackageJson": false
},
"@expo/metro-config": {
"version": "~0.10.6",
"alwaysAddToPackageJson": false
},
"expo-splash-screen": {
"version": "~0.20.4",
"alwaysAddToPackageJson": false
},
"expo-status-bar": {
"version": "~1.6.0",
"alwaysAddToPackageJson": false
},
"@expo/cli": {
"version": "~0.10.10",
"alwaysAddToPackageJson": false
},
"eas-cli": {
"version": "~3.15.0",
"alwaysAddToPackageJson": false
},
"babel-preset-expo": {
"version": "~9.5.0",
"alwaysAddToPackageJson": false
},
"jest-expo": {
"version": "~49.0.0",
"alwaysAddToPackageJson": false
},
"metro-resolver": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"react-native": {
"version": "0.72.3",
"alwaysAddToPackageJson": false
},
"@types/react-native": {
"version": "0.72.2",
"alwaysAddToPackageJson": false
},
"react-native-web": {
"version": "~0.19.6",
"alwaysAddToPackageJson": false
},
"react-native-svg": {
"version": "13.9.0",
"alwaysAddToPackageJson": false
}
}
}
}
}

View File

@ -29,7 +29,8 @@
"chalk": "^4.1.0",
"enhanced-resolve": "^5.8.3",
"fs-extra": "^11.1.0",
"metro-resolver": "^0.74.1",
"metro-config": "0.76.7",
"metro-resolver": "0.76.7",
"node-fetch": "^2.6.7",
"tar-fs": "^2.1.1",
"tsconfig-paths": "^4.1.2",
@ -43,7 +44,9 @@
"@nx/webpack": "file:../webpack"
},
"peerDependencies": {
"expo": "^48.0.19"
"expo": ">= 49.0.0",
"@expo/cli": ">= 0.10.0",
"eas-cli": ">= 3.15.0"
},
"builders": "./executors.json",
"ng-update": {

View File

@ -1,4 +1,6 @@
import { workspaceLayout, workspaceRoot } from '@nx/devkit';
import { mergeConfig } from 'metro-config';
import type { MetroConfig } from 'metro-config';
import { join } from 'path';
import { existsSync } from 'fs-extra';
@ -7,35 +9,39 @@ import { getResolveRequest } from './metro-resolver';
interface WithNxOptions {
debug?: boolean;
extensions?: string[];
/**
* @deprecated TODO(v17) in the metro.config.js, pass in to the getDefaultConfig instead: getDefaultConfig(__dirname)
*/
projectRoot?: string;
watchFolders?: string[];
}
export function withNxMetro(config: any, opts: WithNxOptions = {}) {
export async function withNxMetro(
userConfig: MetroConfig,
opts: WithNxOptions = {}
) {
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(userConfig, nxConfig);
}

View File

@ -2,7 +2,7 @@ import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { resolve } from 'path';
/**
* @deprecated use bundler: 'metro' instead, will be removed in 16.0.0
* @deprecated TODO(v17) use bundler: 'metro' instead, will be removed in 16.0.0
* This function add additional rules to expo's webpack config to make expo web working
*/
export async function withNxWebpack(config) {

View File

@ -1,5 +1,5 @@
import { ExecutorContext, logger, names } from '@nx/devkit';
import { join } from 'path';
import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
@ -42,10 +42,10 @@ export function runCliBuildList(
): Promise<string> {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'),
require.resolve('eas-cli/bin/run'),
['build:list', ...createBuildListOptions(options)],
{
cwd: join(workspaceRoot, projectRoot),
cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
stdio: ['inherit', 'pipe', 'inherit', 'ipc'], // only stream stdout on child process
}

View File

@ -1,5 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit';
import { join, normalize, sep } from 'path';
import { ExecutorContext, names, output } from '@nx/devkit';
import { normalize, sep, resolve as pathResolve, dirname } from 'path';
import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
@ -20,21 +20,22 @@ export default async function* buildExecutor(
): AsyncGenerator<ReactNativeBuildOutput> {
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
try {
// remove the output app if it already existed
if (options.local && options.output) {
removeSync(options.output);
if (options.output.endsWith('.tar.gz')) {
// remove unzipped app if it already existed
removeSync(options.output.replace('.tar.gz', '.app'));
}
}
await runCliBuild(context.root, projectRoot, options);
// unzip the build if it's a tar.gz
if (options.local && options.output && options.output.endsWith('.tar.gz')) {
const directoryPath = normalize(options.output).split(sep);
directoryPath.pop();
const outputDirectory = directoryPath.join(sep);
const outputDirectory = dirname(options.output);
await unzipBuild(options.output, outputDirectory);
}
yield { success: true };
@ -52,10 +53,10 @@ function runCliBuild(
) {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'),
require.resolve('eas-cli/bin/run'),
['build', ...createBuildOptions(options)],
{
cwd: join(workspaceRoot, projectRoot),
cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
}
);

View File

@ -55,7 +55,8 @@
},
"output": {
"type": "string",
"description": "Output path for local build"
"description": "Output path for local build",
"examples": ["../../dist/MyApp.tar.gz", "../../dist"]
},
"wait": {
"type": "boolean",

View File

@ -28,6 +28,7 @@ export interface ReactNativeDownloadOutput {
const streamPipeline = promisify(pipeline);
/**
* @deprecated TODO(v17) this executor is to be removed in NX 17. It is no longer used.
* This executor downloads the latest EAS build.
* It calls the build list executor to list EAS builds with options passed in.
*/

View File

@ -1,8 +1,7 @@
import { ExecutorContext, names } 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 { ExportExecutorSchema } from './schema';
export interface ExpoExportOutput {
@ -17,7 +16,6 @@ export default async function* exportExecutor(
): AsyncGenerator<ExpoExportOutput> {
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
try {
await exportAsync(context.root, projectRoot, options);
@ -39,13 +37,13 @@ function exportAsync(
): Promise<number> {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'),
require.resolve('@expo/cli/build/bin/cli'),
[
`export${options.bundler === 'webpack' ? ':web' : ''}`,
'.',
...createExportOptions(options),
],
{ cwd: join(workspaceRoot, projectRoot), env: process.env }
{ cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
);
// Ensure the child process is killed when the parent exits

View File

@ -1,6 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit';
import { ChildProcess, fork } from 'child_process';
import { join } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ExpoInstallOptions } from './schema';
@ -38,7 +37,7 @@ export function installAsync(
): Promise<number> {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'),
require.resolve('@expo/cli/build/bin/cli'),
['install', ...createInstallOptions(options)],
{ cwd: workspaceRoot, env: process.env }
);

View File

@ -27,7 +27,7 @@ export default async function* prebuildExecutor(
if (options.install) {
await installAsync(context.root, {});
if (options.platform === 'ios') {
await podInstall(join(context.root, projectRoot, 'ios'));
podInstall(join(context.root, projectRoot, 'ios'));
}
}
@ -48,7 +48,7 @@ export function prebuildAsync(
): Promise<number> {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'),
require.resolve('@expo/cli/build/bin/cli'),
['prebuild', ...createPrebuildOptions(options), '--no-install'],
{ cwd: join(workspaceRoot, projectRoot), env: process.env }
);

View File

@ -1,5 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path';
import { join, resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process';
import { platform } from 'os';
import { existsSync } from 'fs-extra';
@ -29,18 +29,6 @@ export default async function* runExecutor(
}
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 (!existsSync(join(context.root, projectRoot, options.platform))) {
await prebuildAsync(context.root, projectRoot, {
@ -53,7 +41,7 @@ export default async function* runExecutor(
if (options.install) {
await installAsync(context.root, {});
if (options.platform === 'ios') {
await podInstall(join(context.root, projectRoot, 'ios'));
podInstall(join(context.root, projectRoot, 'ios'));
}
}
@ -75,10 +63,11 @@ function runCliRun(
) {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'),
require.resolve('@expo/cli/build/bin/cli'),
['run:' + options.platform, ...createRunOptions(options), '--no-install'], // pass in no-install to prevent node_modules install
{
cwd: projectRoot,
cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
}
);

View File

@ -6,6 +6,9 @@
export interface ExpoRunOptions {
// nx options
platform: 'ios' | 'android';
/**
* @deprecated TODO(v17) Add sync-deps to dependsOn in project.json for this target instead
*/
sync: boolean; // default is true
install?: boolean; // default is true
clean?: boolean; // default is false

View File

@ -39,6 +39,7 @@
"type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).",
"default": true,
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"x-priority": "internal"
},
"port": {

View File

@ -20,6 +20,8 @@ export interface ExpoStartOptions {
tunnel?: boolean;
offline?: boolean;
// nx options
/**
* @deprecated TODO(v17) Add sync-deps to dependsOn in project.json for this target instead
*/
sync?: boolean; // default is true
}

View File

@ -91,6 +91,7 @@
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true
}
},

View File

@ -1,14 +1,9 @@
import * as chalk from 'chalk';
import { ExecutorContext, logger, names } 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 { ExpoStartOptions } from './schema';
import {
displayNewlyAddedDepsMessage,
syncDeps,
} from '../sync-deps/sync-deps.impl';
export interface ExpoStartOutput {
baseUrl?: string;
@ -23,18 +18,6 @@ export default async function* startExecutor(
): AsyncGenerator<ExpoStartOutput> {
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
)
);
}
try {
const baseUrl = `http://localhost:${options.port}`;
@ -60,9 +43,9 @@ function startAsync(
): Promise<number> {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'),
require.resolve('@expo/cli/build/bin/cli'),
['start', ...createStartOptions(options)],
{ cwd: join(workspaceRoot, projectRoot), env: process.env }
{ cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
);
// Ensure the child process is killed when the parent exits

View File

@ -1,9 +1,7 @@
import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path';
import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { SubmitExecutorSchema } from './schema';
export interface ReactNativeSubmitOutput {
@ -18,7 +16,6 @@ export default async function* submitExecutor(
): AsyncGenerator<ReactNativeSubmitOutput> {
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
try {
await runCliSubmit(context.root, projectRoot, options);
@ -38,10 +35,10 @@ function runCliSubmit(
) {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'),
require.resolve('eas-cli/bin/run'),
['submit', ...createSubmitOptions(options)],
{
cwd: join(workspaceRoot, projectRoot),
cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
}
);

View File

@ -1,5 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path';
import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
@ -53,9 +53,9 @@ function runCliUpdate(
) {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'),
require.resolve('eas-cli/bin/run'),
['update', ...createUpdateOptions(options)],
{ cwd: join(workspaceRoot, projectRoot), env: process.env }
{ cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
);
// Ensure the child process is killed when the parent exits

View File

@ -121,12 +121,6 @@ describe('app', () => {
'cd ../../../apps/my-dir/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk',
},
'android.eas': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.apk',
build:
'npx nx run my-app:download --platform android --distribution simulator --output=../../../apps/my-dir/my-app/dist/',
type: 'android.apk',
},
'android.local': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.apk',
build:
@ -147,12 +141,6 @@ describe('app', () => {
"cd ../../../apps/my-dir/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app',
},
'ios.eas': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.app',
build:
'npx nx run my-app:download --platform ios --distribution simulator --output=../../../apps/my-dir/my-app/dist/',
type: 'ios.app',
},
'ios.local': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.app',
build:
@ -196,12 +184,6 @@ describe('app', () => {
'cd ../../apps/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk',
},
'android.eas': {
binaryPath: '../../apps/my-app/dist/MyApp.apk',
build:
'npx nx run my-app:download --platform android --distribution simulator --output=../../apps/my-app/dist/',
type: 'android.apk',
},
'android.local': {
binaryPath: '../../apps/my-app/dist/MyApp.apk',
build:
@ -222,12 +204,6 @@ describe('app', () => {
"cd ../../apps/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app',
},
'ios.eas': {
binaryPath: '../../apps/my-app/dist/MyApp.app',
build:
'npx nx run my-app:download --platform ios --distribution simulator --output=../../apps/my-app/dist/',
type: 'ios.app',
},
'ios.local': {
binaryPath: '../../apps/my-app/dist/MyApp.app',
build:
@ -272,12 +248,6 @@ describe('app', () => {
'cd ../../apps/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk',
},
'android.eas': {
binaryPath: '../../apps/my-app/dist/myappname.apk',
build:
'npx nx run my-app:download --platform android --distribution simulator --output=../../apps/my-app/dist/',
type: 'android.apk',
},
'android.local': {
binaryPath: '../../apps/my-app/dist/myappname.apk',
build:
@ -298,12 +268,6 @@ describe('app', () => {
"cd ../../apps/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app',
},
'ios.eas': {
binaryPath: '../../apps/my-app/dist/myappname.app',
build:
'npx nx run my-app:download --platform ios --distribution simulator --output=../../apps/my-app/dist/',
type: 'ios.app',
},
'ios.local': {
binaryPath: '../../apps/my-app/dist/myappname.app',
build:

View File

@ -1,25 +1,37 @@
const { withNxMetro } = require('@nx/expo');
const { getDefaultConfig } = require('@expo/metro-config');
const { mergeConfig } = require('metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
module.exports = (async () => {
defaultConfig.transformer.babelTransformerPath = require.resolve(
'react-native-svg-transformer'
);
defaultConfig.resolver.assetExts = defaultConfig.resolver.assetExts.filter(
(ext) => ext !== 'svg'
);
defaultConfig.resolver.sourceExts.push('svg');
return withNxMetro(defaultConfig, {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx'
extensions: [],
// the project root to start the metro server
projectRoot: __dirname,
// Specify any additional (to projectRoot) watch folders, this is used to know which files to watch
watchFolders: []
});
})();
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});

View File

@ -5,6 +5,7 @@
"dependencies": {
"@testing-library/jest-native": "*",
"@testing-library/react-native": "*",
"metro-config": "*",
"react-native": "*",
"expo": "*",
"react-native-svg": "*",

View File

@ -29,6 +29,7 @@ function getTargets(options: NormalizedSchema) {
architect.start = {
executor: '@nx/expo:start',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {
port: 8081,
},
@ -43,6 +44,7 @@ function getTargets(options: NormalizedSchema) {
architect['run-ios'] = {
executor: '@nx/expo:run',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {
platform: 'ios',
},
@ -50,6 +52,7 @@ function getTargets(options: NormalizedSchema) {
architect['run-android'] = {
executor: '@nx/expo:run',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {
platform: 'android',
},
@ -70,6 +73,9 @@ function getTargets(options: NormalizedSchema) {
options: {},
};
/**
* @deprecated TODO(v17) this executor is no longer used, to be removed in v17
*/
architect['download'] = {
executor: '@nx/expo:download',
options: {
@ -89,6 +95,7 @@ function getTargets(options: NormalizedSchema) {
architect['prebuild'] = {
executor: '@nx/expo:prebuild',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {},
};
@ -104,6 +111,7 @@ function getTargets(options: NormalizedSchema) {
architect['export'] = {
executor: '@nx/expo:export',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {
platform: 'all',
outputDir: `${offsetFromRoot(options.appProjectRoot)}dist/${

View File

@ -0,0 +1,21 @@
import { Tree, getProjects, updateProjectConfiguration } from '@nx/devkit';
/**
* This migration adds dependsOn to project.json.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [name, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/expo:start') {
config.targets['start'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['run-ios'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['run-android'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['prebuild'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['export'].dependsOn = ['ensure-symlink', 'sync-deps'];
updateProjectConfiguration(tree, name, config);
}
}
}

View File

@ -0,0 +1,73 @@
import { Tree, getProjects } from '@nx/devkit';
import { join } from 'path';
/**
* This migration updates metro.config.js to export config as a default.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [_, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/expo:start') {
if (tree.exists(join(config.root, 'metro.config.js'))) {
const oldConfig = tree
.read(join(config.root, 'metro.config.js'))
.toString();
tree.write(
join(config.root, 'metro-v71.config.js'),
oldConfigComment + oldConfig
);
tree.write(join(config.root, 'metro.config.js'), content);
}
}
}
}
const oldConfigComment = `/**
* Old custom configuration for React Native v0.71.
* From @react-native/metro-config 0.72.1, it is no longer necessary to use a config function to access the complete default config.
* Please port your custom configuration to metro.config.js.
* Please see https://docs.expo.dev/guides/customizing-metro/ to learn about configuration.
*/
`;
const content = `
const { withNxMetro } = require('@nx/expo');
const { getDefaultConfig } = require('@expo/metro-config');
const { mergeConfig } = require('metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});
`;

View File

@ -1,28 +1,27 @@
export const nxVersion = require('../../package.json').version;
export const expoVersion = '48.0.19';
export const expoMetroConfigVersion = '0.7.1';
export const expoSplashScreenVersion = '~0.18.2';
export const expoStatusBarVersion = '~1.4.4';
export const expoUpdatesVersion = '~0.16.4';
export const expoCliVersion = '~0.7.3'; // @expo/cli
export const easCliVersion = '~3.13.3';
export const babelPresetExpoVersion = '~9.3.2';
export const expoVersion = '49.0.3';
export const expoMetroConfigVersion = '~0.10.6';
export const expoSplashScreenVersion = '~0.20.4';
export const expoStatusBarVersion = '~1.6.0';
export const expoCliVersion = '~0.10.10'; // @expo/cli
export const easCliVersion = '~3.15.1';
export const babelPresetExpoVersion = '~9.5.0';
export const reactVersion = '18.2.0';
export const reactDomVersion = '18.2.0';
export const reactTestRendererVersion = '18.2.0';
export const typesReactVersion = '18.0.28';
export const reactNativeVersion = '0.71.8';
export const typesReactNativeVersion = '0.71.7';
export const reactNativeWebVersion = '~0.18.12';
export const reactNativeVersion = '0.72.3';
export const typesReactNativeVersion = '0.72.2';
export const reactNativeWebVersion = '~0.19.6';
export const reactNativeSvgTransformerVersion = '1.0.0';
export const reactNativeSvgVersion = '13.4.0';
export const reactNativeSvgVersion = '13.9.0';
export const metroVersion = '0.74.1';
export const metroVersion = '0.76.7';
export const testingLibraryReactNativeVersion = '12.1.2';
export const testingLibraryJestNativeVersion = '5.4.2';
export const jestExpoVersion = '~48.0.2';
export const jestExpoVersion = '~49.0.0';

View File

@ -89,6 +89,18 @@
"version": "16.1.0-beta.0",
"description": "Upgrade @storybook/react-native to 6.5",
"implementation": "./src/migrations/update-16-1-0/upgrade-storybook-6-5"
},
"update-16-6-0-add-dependsOn": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Add dependsOn like ensure-symlink or sync-deps to targets",
"implementation": "./src/migrations/update-16-6-0/add-depends-on"
},
"update-16-6-0-update-metro-config": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Update metro.config.js to use the new metro config format",
"implementation": "./src/migrations/update-16-6-0/update-metro-config"
}
},
"packageJsonUpdates": {
@ -1348,6 +1360,72 @@
"alwaysAddToPackageJson": false
}
}
},
"16.6.0": {
"version": "16.6.0-beta.0",
"packages": {
"react-native": {
"version": "0.72.3",
"alwaysAddToPackageJson": false
},
"@types/react-native": {
"version": "0.72.2",
"alwaysAddToPackageJson": false
},
"metro": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-resolver": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-config": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-react-native-babel-preset": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-babel-register": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-react-native-babel-transformer": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"@react-native/metro-config": {
"version": "^0.72.9",
"alwaysAddToPackageJson": false,
"addToPackageJson": "devDependencies"
},
"@react-native-community/cli": {
"version": "11.3.5",
"alwaysAddToPackageJson": false
},
"@react-native-community/cli-platform-android": {
"version": "11.3.5",
"alwaysAddToPackageJson": false
},
"@react-native-community/cli-platform-ios": {
"version": "11.3.5",
"alwaysAddToPackageJson": false
},
"@babel/runtime": {
"version": "7.22.6",
"alwaysAddToPackageJson": false
},
"@react-native-async-storage/async-storage": {
"version": "1.19.0",
"alwaysAddToPackageJson": false
},
"react-native-safe-area-context": {
"version": "4.7.1",
"alwaysAddToPackageJson": false
}
}
}
}
}

View File

@ -30,7 +30,8 @@
"fs-extra": "^11.1.0",
"glob": "7.1.4",
"ignore": "^5.0.4",
"metro-resolver": "^0.74.1",
"metro-config": "0.76.7",
"metro-resolver": "0.76.7",
"minimatch": "3.0.5",
"node-fetch": "^2.6.7",
"tsconfig-paths": "^4.1.2",
@ -43,7 +44,7 @@
"@nx/workspace": "file:../workspace"
},
"peerDependencies": {
"react-native": ">= 0.71.0"
"react-native": ">= 0.72.0 < 0.73.0"
},
"builders": "./executors.json",
"ng-update": {

View File

@ -1,4 +1,6 @@
import { workspaceLayout, workspaceRoot } from '@nx/devkit';
import { mergeConfig } from 'metro-config';
import type { MetroConfig } from 'metro-config';
import { join } from 'path';
import { existsSync } from 'fs-extra';
@ -7,36 +9,39 @@ import { getResolveRequest } from './metro-resolver';
interface WithNxOptions {
debug?: boolean;
extensions?: string[];
/**
* @deprecated TODO(v17) in the metro.config.js, pass in to the getDefaultConfig instead: getDefaultConfig(__dirname)
*/
projectRoot?: string;
watchFolders?: string[];
}
export function withNxMetro(config: any, opts: WithNxOptions = {}) {
export async function withNxMetro(
userConfig: MetroConfig,
opts: WithNxOptions = {}
) {
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(userConfig, nxConfig);
}

View File

@ -1,62 +1,24 @@
import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ExecutorContext } 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 { getCliOptions } from '../../utils/get-cli-options';
export interface ReactNativeBuildOutput {
success: boolean;
}
let childProcess: ChildProcess;
export default async function* buildAndroidExecutor(
options: ReactNativeBuildAndroidOptions,
context: ExecutorContext
): AsyncGenerator<ReactNativeBuildOutput> {
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();
}
}
await runCliBuild(context.root, projectRoot, options);
yield { success: true };
}
function runCliBuild(
@ -64,30 +26,39 @@ function runCliBuild(
projectRoot: string,
options: ReactNativeBuildAndroidOptions
) {
return new Promise((resolve, reject) => {
return new Promise<ChildProcess>((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.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => {
reject(err);
});
childProcess.on('exit', (code) => {
if (code === 0) {
resolve(code);
res(childProcess);
} else {
reject(code);
}

View File

@ -1,4 +1,5 @@
// options taken from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/buildAndroid/index.ts
// https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/README.md#build-android
import { ReactNativeStartOptions } from '../start/schema';
@ -20,12 +21,13 @@ export interface ReactNativeBuildAndroidOptions
// react native options
mode: string; // default is debug
activeArchOnly: boolean; // default is false
port: number; // default is 8081
tasks?: Array<string>;
tasks?: string | Array<string>;
extraParams?: Array<string>;
interactive?: boolean;
interactive: boolean;
// nx options
// @deprecated, no longer used
packager: boolean; // default is true
// @deprecated, add to sync-deps to dependsOn
sync: boolean;
}

View File

@ -51,7 +51,8 @@
"packager": {
"type": "boolean",
"description": "Launch packager while building",
"default": true
"default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
},
"port": {
"type": "number",
@ -59,10 +60,10 @@
"default": 8081
},
"tasks": {
"type": "array",
"items": {
"type": "string"
},
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [
"assembleDebug",
@ -79,8 +80,12 @@
"default": false
},
"extraParams": {
"type": "string",
"description": "Custom params passed to gradle build command"
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command",
"examples": ["-x lint -x test"]
},
"interactive": {
"type": "boolean",
@ -89,7 +94,8 @@
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"default": true
"default": true,
"x-deprecated": "Add sync-deps to dependsOn instead"
},
"resetCache": {
"type": "boolean",

View File

@ -1,25 +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 { ReactNativeBuildIosOptions } from './schema';
import { runCliStart } from '../start/start.impl';
import { getCliOptions } from '../../utils/get-cli-options';
export interface ReactNativeBuildIosOutput {
success: boolean;
}
let childProcess: ChildProcess;
export default async function* runIosExecutor(
export default async function* buildIosExecutor(
options: ReactNativeBuildIosOptions,
context: ExecutorContext
): AsyncGenerator<ReactNativeBuildIosOutput> {
@ -28,77 +19,48 @@ 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
);
}
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);
yield { success: true };
} finally {
if (childProcess) {
childProcess.kill();
}
}
await runCliBuildIOS(context.root, projectRoot, options);
return { success: true };
}
function runCliBuildIOS(
workspaceRoot: string,
projectRoot: string,
options: ReactNativeBuildIosOptions
) {
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'),
['build-ios', ...createBuildIOSOptions(options)],
): Promise<ChildProcess> {
return new Promise<ChildProcess>((resolve, reject) => {
const childProcess = fork(
require.resolve('react-native/cli.js'),
[
'build-ios',
...createBuildIOSOptions(options),
...(process.env.NX_VERBOSE_LOGGING === 'true' ? ['--verbose'] : []),
],
{
cwd: join(workspaceRoot, projectRoot),
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.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => {
reject(err);
});
childProcess.on('exit', (code) => {
if (code === 0) {
resolve(code);
resolve(childProcess);
} else {
reject(code);
}

View File

@ -16,7 +16,10 @@ export interface ReactNativeBuildIosOptions extends ReactNativeStartOptions {
extraParams?: string;
// nx options
// @deprecated, no longer used
packager: boolean; // default is true
// @deprecated, add to pod-install to dependsOn
install: boolean; // default is true
// @deprecated, add to sync-deps to dependsOn
sync: boolean; // default is true
}

View File

@ -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": {
@ -78,17 +74,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build"
},
"extraParams": {
"type": "string",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command."
},
"install": {
"type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.",
"x-deprecated": "Add pod-install to dependsOn in project.json for this target instead",
"default": true
},
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true
},
"resetCache": {
@ -99,7 +100,8 @@
"packager": {
"type": "boolean",
"description": "Launch packager while building",
"default": true
"default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
}
},
"required": [],

View File

@ -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 as pathResolve } 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,46 @@ 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<ChildProcess> {
return new Promise<ChildProcess>((resolve, 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: pathResolve(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.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => {
reject(err);
});
childProcess.on('exit', (code) => {
if (code === 0) {
resolve(code);
resolve(childProcess);
} else {
reject(code);
}

View File

@ -15,7 +15,7 @@ export default async function* podInstall(
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;
const iosDirectory = join(context.root, projectRoot, 'ios');
await runPodInstall(iosDirectory, true, options.buildFolder)();
await runPodInstall(iosDirectory, true, options)();
yield { success: true };
}

View File

@ -1,3 +1,5 @@
export interface ReactNativePodInstallOptions {
buildFolder: string;
buildFolder: string; // default is './build'
repoUpdate: boolean; // default is false
deployment: boolean; // default is false
}

View File

@ -9,9 +9,19 @@
"type": "object",
"properties": {
"buildFolder": {
"description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory",
"description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.",
"type": "string",
"default": "./build"
},
"repoUpdate": {
"description": "Force running `pod repo update` before install.",
"type": "boolean",
"default": false
},
"deployment": {
"description": "Disallow any changes to the Podfile or the Podfile.lock during installation.",
"type": "boolean",
"default": false
}
},
"required": ["buildFolder"]

View File

@ -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 from '../start/start.impl';
import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files';
import { getCliOptions } from '../../utils/get-cli-options';
@ -24,72 +19,72 @@ export default async function* runAndroidExecutor(
): AsyncGenerator<ReactNativeRunAndroidOutput> {
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
)
if (options.packager && options.mode !== 'Release') {
const startResults = startExecutor(
{
port: options.port,
resetCache: options.resetCache,
interactive: true,
},
context
);
for await (const result of startResults) {
if (result.success) {
await runCliRunAndroid(context.root, projectRoot, options);
yield {
success: true,
};
} else {
return result;
}
}
} else {
await runCliRunAndroid(context.root, projectRoot, options);
}
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);
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<ChildProcess> {
return new Promise<ChildProcess>((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.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => {
reject(err);
});
childProcess.on('exit', (code) => {
if (code === 0) {
resolve(code);
resolve(childProcess);
} else {
reject(code);
}

View File

@ -5,11 +5,11 @@ import { ReactNativeStartOptions } from '../start/schema';
export interface ReactNativeRunAndroidOptions
extends ReactNativeBuildAndroidOptions {
/**
* @deprecated use mode instead
* @deprecated TODO(v17) use mode instead
*/
variant: string;
/**
* @deprecated no longer supported in react native cli
* @deprecated TODO(v17) no longer supported in react native cli
* https://github.com/react-native-community/cli/commit/7c003f2b1d9d80ec5c167614ba533a004272c685
*/
jetifier: boolean;

View File

@ -77,10 +77,10 @@
"default": 8081
},
"tasks": {
"type": "array",
"items": {
"type": "string"
},
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [
"assembleDebug",
@ -97,7 +97,10 @@
"default": false
},
"extraParams": {
"type": "string",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command"
},
"interactive": {
@ -107,6 +110,7 @@
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn for this target in project.json instead",
"default": true
},
"resetCache": {

View File

@ -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,71 @@ 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.packager && options.mode !== 'Release') {
const startResults = startExecutor(
{
port: options.port,
resetCache: options.resetCache,
interactive: true,
},
context
);
}
if (options.install) {
await podInstall(
join(context.root, projectRoot, 'ios'),
options.buildFolder
);
}
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();
for await (const result of startResults) {
if (result.success) {
await runCliRunIOS(context.root, projectRoot, options);
yield {
success: true,
};
} else {
return result;
}
}
} else {
await runCliRunIOS(context.root, projectRoot, options);
}
yield { success: true };
}
function runCliRunIOS(
workspaceRoot: string,
projectRoot: string,
options: ReactNativeRunIosOptions
) {
return new Promise((resolve, reject) => {
): Promise<ChildProcess> {
return new Promise<ChildProcess>((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.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => {
reject(err);
});
childProcess.on('exit', (code) => {
if (code === 0) {
resolve(code);
resolve(childProcess);
} else {
reject(code);
}

View File

@ -4,7 +4,7 @@ import { ReactNativeStartOptions } from '../start/schema';
// part of options form https://github.com/react-native-community/cli/blob/main/packages/cli-platform-ios/src/commands/runIOS/index.ts
export interface ReactNativeRunIosOptions extends ReactNativeBuildIosOptions {
/**
* @deprecated use mode instead, will be removed in nx 17.
* @deprecated TODO(v17) use mode instead, will be removed in nx 17.
*/
xcodeConfiguration?: string;

View File

@ -19,10 +19,6 @@
{
"name": "Run iOS on a device with udid",
"keys": ["udid"]
},
{
"name": "Run `pod install` before building iOS app",
"keys": ["install"]
}
],
"properties": {
@ -87,17 +83,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build"
},
"extraParams": {
"type": "string",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command."
},
"install": {
"type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.",
"default": true
"default": true,
"x-deprecated": "Add `pod-install` to dependsOn for this target in project.json instead."
},
"sync": {
"type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add `sync-deps` to dependsOn for this target in project.json instead.",
"default": true
},
"resetCache": {

View File

@ -1,18 +1,15 @@
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';
export interface ReactNativeStartOutput {
baseUrl?: string;
port?: number;
success: boolean;
}
let childProcess: ChildProcess;
export default async function* startExecutor(
options: ReactNativeStartOptions,
context: ExecutorContext
@ -21,27 +18,27 @@ 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 startProcess = await runCliStart(context.root, projectRoot, options);
await runCliStart(context.root, projectRoot, options);
yield {
port: options.port,
success: true,
};
yield {
baseUrl,
success: true,
};
} finally {
if (childProcess) {
childProcess.kill();
}
if (!startProcess) {
return;
}
await new Promise<void>((resolve) => {
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
startProcess.kill(signal);
resolve();
process.exit();
};
process.once('exit', (signal) => startProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
});
}
/*
@ -52,10 +49,10 @@ export async function runCliStart(
workspaceRoot: string,
projectRoot: string,
options: ReactNativeStartOptions
): Promise<void> {
): Promise<ChildProcess> {
const result = await isPackagerRunning(options.port);
if (result === 'running') {
logger.info('JS server already running.');
logger.info(`JS server already running on port ${options.port}.`);
} else if (result === 'unrecognized') {
logger.warn('JS server not recognized.');
} else {
@ -63,7 +60,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 +74,34 @@ function startAsync(
workspaceRoot: string,
projectRoot: string,
options: ReactNativeStartOptions
): Promise<number> {
return new Promise((resolve, reject) => {
childProcess = fork(
join(workspaceRoot, './node_modules/react-native/cli.js'),
): Promise<ChildProcess> {
return new Promise<ChildProcess>((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: ['inherit', 'pipe', 'pipe', 'ipc'],
}
);
// 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('reload the app')) {
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);
}

View File

@ -3,7 +3,7 @@ export interface ReactNativeStorybookOptions {
outputFile: string;
pattern: string;
/**
* @deprecated going to be removed in 17
* @deprecated TODO(v17) no longer used going to be removed in 17
*/
silent: boolean;
}

View File

@ -7,7 +7,6 @@ import {
Tree,
} from '@nx/devkit';
import { runPodInstall } from '../../utils/pod-install-task';
import { runSymlink } from '../../utils/symlink-task';
import { addLinting } from '../../utils/add-linting';
import { addJest } from '../../utils/add-jest';
@ -48,10 +47,6 @@ export async function reactNativeApplicationGenerator(
);
const detoxTask = await addDetox(host, options);
const symlinkTask = runSymlink(host.root, options.appProjectRoot);
const podInstallTask = runPodInstall(
joinPathFragments(host.root, options.iosProjectRoot),
options.install
);
const chmodTaskGradlew = chmodAndroidGradlewFilesTask(
joinPathFragments(host.root, options.androidProjectRoot)
);
@ -66,7 +61,6 @@ export async function reactNativeApplicationGenerator(
jestTask,
detoxTask,
symlinkTask,
podInstallTask,
chmodTaskGradlew
);
}

View File

@ -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'

View File

@ -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'

View File

@ -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());
}
}

View File

@ -20,7 +20,7 @@
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
<selector>
<!--
<!--
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'

View File

@ -16,7 +16,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
}

View File

@ -25,7 +25,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.125.0
FLIPPER_VERSION=0.182.0
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using

View File

@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,10 +80,10 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -205,6 +209,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View File

@ -1,5 +1,4 @@
rootProject.name = '<%= className %>'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
includeBuild('../node_modules/@react-native/gradle-plugin')

View File

@ -1,5 +1,9 @@
require_relative '<%= offsetFromRoot %>../node_modules/react-native/scripts/react_native_pods'
require_relative '<%= offsetFromRoot %>../node_modules/@react-native-community/cli-platform-ios/native_modules'
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
platform :ios, min_ios_version_supported
prepare_react_native_project!
@ -30,8 +34,6 @@ target '<%= className %>' do
use_react_native!(
:path => config[:reactNativePath],
# Hermes is now enabled by default. Disable by setting this flag to false.
# Upcoming versions of React Native may rely on get_default_flags(), but
# we make it explicit here to aid in the React Native upgrade process.
:hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled],
# Enables Flipper.
@ -49,10 +51,10 @@ target '<%= className %>' do
end
post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(
installer,
# Set `mac_catalyst_enabled` to `true` in order to apply patches
# necessary for Mac Catalyst builds
config[:reactNativePath],
:mac_catalyst_enabled => false
)
__apply_Xcode_12_5_M1_post_install_workaround(installer)

View File

@ -564,7 +564,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -592,12 +592,14 @@
);
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG",
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
name = Debug;
@ -635,7 +637,7 @@
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -655,12 +657,14 @@
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG",
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};

View File

@ -23,14 +23,4 @@
#endif
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
return true;
}
@end

View File

@ -1,38 +1,35 @@
const { withNxMetro } = require('@nx/react-native');
const { getDefaultConfig } = require('metro-config');
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig();
return withNxMetro(
{
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
},
},
{
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// the project root to start the metro server
projectRoot: __dirname,
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: []
}
);
})();
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});

View File

@ -3,6 +3,7 @@
"version": "0.0.1",
"private": true,
"dependencies": {
"@react-native/metro-config": "*",
"@testing-library/jest-native": "*",
"@testing-library/react-native": "*",
"react": "*",

View File

@ -25,6 +25,7 @@ function getTargets(options: NormalizedSchema) {
architect.start = {
executor: '@nx/react-native:start',
dependsOn: ['ensure-symlink', 'sync-deps', 'pod-install'],
options: {
port: 8081,
},
@ -39,11 +40,13 @@ function getTargets(options: NormalizedSchema) {
architect['run-ios'] = {
executor: '@nx/react-native:run-ios',
dependsOn: ['ensure-symlink', 'sync-deps', 'pod-install'],
options: {},
};
architect['bundle-ios'] = {
executor: '@nx/react-native:bundle',
dependsOn: ['ensure-symlink'],
outputs: ['{options.bundleOutput}'],
options: {
entryFile: options.entryFile,
@ -54,6 +57,7 @@ function getTargets(options: NormalizedSchema) {
architect['run-android'] = {
executor: '@nx/react-native:run-android',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {},
};
@ -63,12 +67,14 @@ function getTargets(options: NormalizedSchema) {
`{projectRoot}/android/app/build/outputs/bundle`,
`{projectRoot}/android/app/build/outputs/apk`,
],
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {},
};
architect['build-ios'] = {
executor: '@nx/react-native:build-ios',
outputs: ['{projectRoot}/ios/build/Build'],
dependsOn: ['ensure-symlink', 'sync-deps', 'pod-install'],
options: {},
};
@ -79,6 +85,7 @@ function getTargets(options: NormalizedSchema) {
architect['bundle-android'] = {
executor: '@nx/react-native:bundle',
dependsOn: ['ensure-symlink'],
outputs: ['{options.bundleOutput}'],
options: {
entryFile: options.entryFile,

View File

@ -24,6 +24,7 @@ import {
reactNativeCommunityCli,
reactNativeCommunityCliAndroid,
reactNativeCommunityCliIos,
reactNativeMetroConfigVersion,
reactNativeSvgTransformerVersion,
reactNativeSvgVersion,
reactNativeVersion,
@ -90,6 +91,7 @@ export function updateDependencies(host: Tree) {
'@types/node': typesNodeVersion,
'@types/react': typesReactVersion,
'@types/react-native': typesReactNativeVersion,
'@react-native/metro-config': reactNativeMetroConfigVersion,
'@react-native-community/cli': reactNativeCommunityCli,
'@react-native-community/cli-platform-android':
reactNativeCommunityCliAndroid,
@ -98,6 +100,7 @@ export function updateDependencies(host: Tree) {
'@testing-library/jest-native': testingLibraryJestNativeVersion,
'jest-react-native': jestReactNativeVersion,
metro: metroVersion,
'metro-config': metroVersion,
'metro-resolver': metroVersion,
'metro-babel-register': metroVersion,
'metro-react-native-babel-preset': metroVersion,
@ -108,7 +111,6 @@ export function updateDependencies(host: Tree) {
'@babel/preset-react': babelPresetReactVersion,
...(isPnpm
? {
'metro-config': metroVersion, // metro-config is used by metro.config.js
'@babel/runtime': babelRuntimeVersion, // @babel/runtime is used by react-native-svg
}
: {}),

View File

@ -170,7 +170,6 @@ function createFiles(host: Tree, options: NormalizedSchema) {
{
...options,
...names(options.name),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.projectRoot),
rootTsConfigPath: getRelativePathToRootTsConfig(
host,

View File

@ -0,0 +1,39 @@
import { Tree, getProjects, updateProjectConfiguration } from '@nx/devkit';
/**
* This migration adds dependsOn to project.json.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [name, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/react-native:start') {
config.targets['start'].dependsOn = [
'ensure-symlink',
'sync-deps',
'pod-install',
];
config.targets['run-ios'].dependsOn = [
'ensure-symlink',
'sync-deps',
'pod-install',
];
config.targets['bundle-ios'].dependsOn = ['ensure-symlink'];
config.targets['run-android'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['build-android'].dependsOn = [
'ensure-symlink',
'sync-deps',
];
config.targets['build-ios'].dependsOn = [
'ensure-symlink',
'sync-deps',
'pod-install',
];
config.targets['pod-install'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['bundle-android'].dependsOn = ['ensure-symlink'];
updateProjectConfiguration(tree, name, config);
}
}
}

View File

@ -0,0 +1,71 @@
import { Tree, getProjects } from '@nx/devkit';
import { join } from 'path';
/**
* This migration updates metro.config.js to export config as a default.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [_, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/react-native:start') {
if (tree.exists(join(config.root, 'metro.config.js'))) {
const oldConfig = tree
.read(join(config.root, 'metro.config.js'))
.toString();
tree.write(
join(config.root, 'metro-v71.config.js'),
oldConfigComment + oldConfig
);
tree.write(join(config.root, 'metro.config.js'), content);
}
}
}
}
const oldConfigComment = `/**
* Old custom configuration for React Native v0.71.
* From @react-native/metro-config 0.72.1, it is no longer necessary to use a config function to access the complete default config.
* Please port your custom configuration to metro.config.js.
* Please see https://reactnative.dev/docs/metro to learn about configuration.
*/
`;
const content = `
const { withNxMetro } = require('@nx/react-native');
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});
`;

View File

@ -18,7 +18,9 @@ export function getCliOptions<T>(
const cliKey = optionKeysInCamelName.includes(optionKey)
? names(optionKey).propertyName
: names(optionKey).fileName; // cli uses kebab case as default
if (typeof optionValue === 'boolean' && optionValue) {
if (Array.isArray(optionValue)) {
acc.push(`--${cliKey}`, optionValue.join(','));
} else if (typeof optionValue === 'boolean' && optionValue) {
// no need to pass in the value when it is true, just the flag name
acc.push(`--${cliKey}`);
} else {

View File

@ -24,7 +24,15 @@ ${chalk.bold('sudo xcode-select --switch /Applications/Xcode.app')}
export function runPodInstall(
iosDirectory: string,
install: boolean = true,
buildFolder?: string
options: {
buildFolder?: string;
repoUpdate?: boolean;
deployment?: boolean;
} = {
buildFolder: './build',
repoUpdate: false,
deployment: false,
}
): GeneratorCallback {
return () => {
if (platform() !== 'darwin') {
@ -39,16 +47,32 @@ export function runPodInstall(
logger.info(`Running \`pod install\` from "${iosDirectory}"`);
return podInstall(iosDirectory, buildFolder);
return podInstall(iosDirectory, options);
};
}
export function podInstall(iosDirectory: string, buildFolder?: string) {
export function podInstall(
iosDirectory: string,
options: {
buildFolder?: string;
repoUpdate?: boolean;
deployment?: boolean;
} = {
buildFolder: './build',
repoUpdate: false,
deployment: false,
}
) {
try {
execSync('pod install', {
cwd: iosDirectory,
stdio: 'inherit',
});
execSync(
`pod install ${options.repoUpdate ? '--repo-update' : ''} ${
options.deployment ? '--deployment' : ''
}`,
{
cwd: iosDirectory,
stdio: 'inherit',
}
);
} catch (e) {
logger.error(podInstallErrorMessage);
throw e;

View File

@ -1,15 +1,16 @@
export const nxVersion = require('../../package.json').version;
export const reactNativeVersion = '0.71.10';
export const typesReactNativeVersion = '0.71.7';
export const reactNativeVersion = '0.72.3';
export const typesReactNativeVersion = '0.72.2';
export const typesNodeVersion = '18.14.4';
export const metroVersion = '0.74.1';
export const metroVersion = '0.76.7';
export const reactNativeMetroConfigVersion = '^0.72.9';
export const reactNativeCommunityCli = '10.2.2';
export const reactNativeCommunityCliIos = '10.2.1';
export const reactNativeCommunityCliAndroid = '10.2.0';
export const reactNativeCommunityCli = '11.3.5';
export const reactNativeCommunityCliIos = '11.3.5';
export const reactNativeCommunityCliAndroid = '11.3.5';
export const reactVersion = '18.2.0';
export const reactDomVersion = '18.2.0';
@ -24,7 +25,7 @@ export const jestReactNativeVersion = '18.0.0';
export const reactNativeSvgTransformerVersion = '1.0.0';
export const reactNativeSvgVersion = '13.9.0';
export const babelRuntimeVersion = '7.21.0';
export const babelRuntimeVersion = '7.22.6';
export const reactNativeAsyncStorageVersion = '1.18.2';
export const reactNativeSafeAreaContextVersion = '4.5.3';
export const reactNativeAsyncStorageVersion = '1.19.0';
export const reactNativeSafeAreaContextVersion = '4.7.1';

705
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff