feat(react-native): add convert-to-inferred generator for Expo and React Native (#27326)
This PR adds `convert-to-inferred` generators to convert React Native an Expo apps using executors to use the corresponding inference plugins. Also: 1. Fixes casing for `@nx/react-native/plugin` so it is correctly set as `upgradeTargetName` not `upgradeTargetname` 2. Migration for the above fix for existing projects
This commit is contained in:
parent
2ce679755f
commit
d3747e020f
@ -7251,6 +7251,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "convert-to-inferred",
|
||||||
|
"path": "/nx-api/detox/generators/convert-to-inferred",
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
@ -7659,6 +7667,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "convert-to-inferred",
|
||||||
|
"path": "/nx-api/expo/generators/convert-to-inferred",
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
@ -9233,6 +9249,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "convert-to-inferred",
|
||||||
|
"path": "/nx-api/react-native/generators/convert-to-inferred",
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
|
|||||||
@ -607,6 +607,15 @@
|
|||||||
"originalFilePath": "/packages/detox/src/generators/application/schema.json",
|
"originalFilePath": "/packages/detox/src/generators/application/schema.json",
|
||||||
"path": "/nx-api/detox/generators/application",
|
"path": "/nx-api/detox/generators/application",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/detox/generators/convert-to-inferred": {
|
||||||
|
"description": "Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"file": "generated/packages/detox/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/detox/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "/nx-api/detox/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path": "/nx-api/detox"
|
"path": "/nx-api/detox"
|
||||||
@ -991,6 +1000,15 @@
|
|||||||
"originalFilePath": "/packages/expo/src/generators/component/schema.json",
|
"originalFilePath": "/packages/expo/src/generators/component/schema.json",
|
||||||
"path": "/nx-api/expo/generators/component",
|
"path": "/nx-api/expo/generators/component",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/expo/generators/convert-to-inferred": {
|
||||||
|
"description": "Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"file": "generated/packages/expo/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/expo/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "/nx-api/expo/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path": "/nx-api/expo"
|
"path": "/nx-api/expo"
|
||||||
@ -2553,6 +2571,15 @@
|
|||||||
"originalFilePath": "/packages/react-native/src/generators/web-configuration/schema.json",
|
"originalFilePath": "/packages/react-native/src/generators/web-configuration/schema.json",
|
||||||
"path": "/nx-api/react-native/generators/web-configuration",
|
"path": "/nx-api/react-native/generators/web-configuration",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/react-native/generators/convert-to-inferred": {
|
||||||
|
"description": "Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"file": "generated/packages/react-native/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/react-native/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "/nx-api/react-native/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path": "/nx-api/react-native"
|
"path": "/nx-api/react-native"
|
||||||
|
|||||||
@ -598,6 +598,15 @@
|
|||||||
"originalFilePath": "/packages/detox/src/generators/application/schema.json",
|
"originalFilePath": "/packages/detox/src/generators/application/schema.json",
|
||||||
"path": "detox/generators/application",
|
"path": "detox/generators/application",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"file": "generated/packages/detox/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/detox/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "detox/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
@ -977,6 +986,15 @@
|
|||||||
"originalFilePath": "/packages/expo/src/generators/component/schema.json",
|
"originalFilePath": "/packages/expo/src/generators/component/schema.json",
|
||||||
"path": "expo/generators/component",
|
"path": "expo/generators/component",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"file": "generated/packages/expo/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/expo/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "expo/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
@ -2526,6 +2544,15 @@
|
|||||||
"originalFilePath": "/packages/react-native/src/generators/web-configuration/schema.json",
|
"originalFilePath": "/packages/react-native/src/generators/web-configuration/schema.json",
|
||||||
"path": "react-native/generators/web-configuration",
|
"path": "react-native/generators/web-configuration",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"file": "generated/packages/react-native/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/react-native/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "react-native/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxDetoxConvertToInferred",
|
||||||
|
"description": "Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert Detox project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/detox:*` executors to use `@nx/detox/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"implementation": "/packages/detox/src/generators/convert-to-inferred/convert-to-inferred.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/detox/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoConvertToInferred",
|
||||||
|
"description": "Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert Expo project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/expo:*` executors to use `@nx/expo/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"implementation": "/packages/expo/src/generators/convert-to-inferred/convert-to-inferred.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/expo/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxReactNativeConvertToInferred",
|
||||||
|
"description": "Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert React Native project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/react-native:*` executors to use `@nx/react-native/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"implementation": "/packages/react-native/src/generators/convert-to-inferred/convert-to-inferred.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/react-native/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
@ -388,6 +388,7 @@
|
|||||||
- [generators](/nx-api/detox/generators)
|
- [generators](/nx-api/detox/generators)
|
||||||
- [init](/nx-api/detox/generators/init)
|
- [init](/nx-api/detox/generators/init)
|
||||||
- [application](/nx-api/detox/generators/application)
|
- [application](/nx-api/detox/generators/application)
|
||||||
|
- [convert-to-inferred](/nx-api/detox/generators/convert-to-inferred)
|
||||||
- [devkit](/nx-api/devkit)
|
- [devkit](/nx-api/devkit)
|
||||||
- [documents](/nx-api/devkit/documents)
|
- [documents](/nx-api/devkit/documents)
|
||||||
- [Overview](/nx-api/devkit/documents/nx_devkit)
|
- [Overview](/nx-api/devkit/documents/nx_devkit)
|
||||||
@ -437,6 +438,7 @@
|
|||||||
- [application](/nx-api/expo/generators/application)
|
- [application](/nx-api/expo/generators/application)
|
||||||
- [library](/nx-api/expo/generators/library)
|
- [library](/nx-api/expo/generators/library)
|
||||||
- [component](/nx-api/expo/generators/component)
|
- [component](/nx-api/expo/generators/component)
|
||||||
|
- [convert-to-inferred](/nx-api/expo/generators/convert-to-inferred)
|
||||||
- [express](/nx-api/express)
|
- [express](/nx-api/express)
|
||||||
- [documents](/nx-api/express/documents)
|
- [documents](/nx-api/express/documents)
|
||||||
- [Overview](/nx-api/express/documents/overview)
|
- [Overview](/nx-api/express/documents/overview)
|
||||||
@ -628,6 +630,7 @@
|
|||||||
- [stories](/nx-api/react-native/generators/stories)
|
- [stories](/nx-api/react-native/generators/stories)
|
||||||
- [upgrade-native](/nx-api/react-native/generators/upgrade-native)
|
- [upgrade-native](/nx-api/react-native/generators/upgrade-native)
|
||||||
- [web-configuration](/nx-api/react-native/generators/web-configuration)
|
- [web-configuration](/nx-api/react-native/generators/web-configuration)
|
||||||
|
- [convert-to-inferred](/nx-api/react-native/generators/convert-to-inferred)
|
||||||
- [remix](/nx-api/remix)
|
- [remix](/nx-api/remix)
|
||||||
- [documents](/nx-api/remix/documents)
|
- [documents](/nx-api/remix/documents)
|
||||||
- [Overview](/nx-api/remix/documents/overview)
|
- [Overview](/nx-api/remix/documents/overview)
|
||||||
|
|||||||
@ -15,6 +15,11 @@
|
|||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Create a Detox application."
|
"description": "Create a Detox application."
|
||||||
|
},
|
||||||
|
"convert-to-inferred": {
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": "./src/generators/convert-to-inferred/schema.json",
|
||||||
|
"description": "Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,14 +64,14 @@ describe('detox application generator', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.release': {
|
'ios.release': {
|
||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -136,14 +136,14 @@ describe('detox application generator', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
'../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"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",
|
"cd ../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.release': {
|
'ios.release': {
|
||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
'../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -208,14 +208,14 @@ describe('detox application generator', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
'../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"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",
|
"cd ../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.release': {
|
'ios.release': {
|
||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
'../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -279,14 +279,14 @@ describe('detox application generator', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
'../../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"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",
|
"cd ../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.release': {
|
'ios.release': {
|
||||||
binaryPath:
|
binaryPath:
|
||||||
'../../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
'../../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -355,7 +355,7 @@ describe('detox application generator', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
'../../my-dir/my-app/ios/build/Build/Products/Debug-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"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",
|
"cd ../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.local': {
|
'ios.local': {
|
||||||
@ -368,7 +368,7 @@ describe('detox application generator', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
'../../my-dir/my-app/ios/build/Build/Products/Release-iphonesimulator/MyDirMyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,12 +11,12 @@
|
|||||||
"apps": {
|
"apps": {
|
||||||
"ios.debug": {
|
"ios.debug": {
|
||||||
"type": "ios.app",
|
"type": "ios.app",
|
||||||
"build": "cd <%= offsetFromRoot %><%= appRoot %>/ios && xcodebuild -workspace <%= appClassName %>.xcworkspace -scheme <%= appClassName %> -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"build": "cd <%= offsetFromRoot %><%= appRoot %>/ios && xcodebuild -workspace <%= appClassName %>.xcworkspace -scheme <%= appClassName %> -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Debug-iphonesimulator/<%= appClassName %>.app"
|
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Debug-iphonesimulator/<%= appClassName %>.app"
|
||||||
},
|
},
|
||||||
"ios.release": {
|
"ios.release": {
|
||||||
"type": "ios.app",
|
"type": "ios.app",
|
||||||
"build": "cd <%= offsetFromRoot %><%= appRoot %>/ios && xcodebuild -workspace <%= appClassName %>.xcworkspace -scheme <%= appClassName %> -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"build": "cd <%= offsetFromRoot %><%= appRoot %>/ios && xcodebuild -workspace <%= appClassName %>.xcworkspace -scheme <%= appClassName %> -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Release-iphonesimulator/<%= appClassName %>.app"
|
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Release-iphonesimulator/<%= appClassName %>.app"
|
||||||
},
|
},
|
||||||
<% if (framework === 'expo') { %>
|
<% if (framework === 'expo') { %>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
"simulator": {
|
"simulator": {
|
||||||
"type": "ios.simulator",
|
"type": "ios.simulator",
|
||||||
"device": {
|
"device": {
|
||||||
"type": "iPhone 14"
|
"type": "iPhone 15 Plus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"emulator": {
|
"emulator": {
|
||||||
|
|||||||
@ -0,0 +1,432 @@
|
|||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
|
type ProjectConfiguration,
|
||||||
|
type ProjectGraph,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
type Tree,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { getRelativeProjectJsonSchemaPath } from 'nx/src/generators/utils/project-configuration';
|
||||||
|
import { convertToInferred } from './convert-to-inferred';
|
||||||
|
|
||||||
|
let fs: TempFs;
|
||||||
|
let projectGraph: ProjectGraph;
|
||||||
|
jest.mock('@nx/devkit', () => ({
|
||||||
|
...jest.requireActual('@nx/devkit'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve(projectGraph)),
|
||||||
|
updateProjectConfiguration: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((tree, projectName, projectConfiguration) => {
|
||||||
|
function handleEmptyTargets(
|
||||||
|
projectName: string,
|
||||||
|
projectConfiguration: ProjectConfiguration
|
||||||
|
): void {
|
||||||
|
if (
|
||||||
|
projectConfiguration.targets &&
|
||||||
|
!Object.keys(projectConfiguration.targets).length
|
||||||
|
) {
|
||||||
|
// Re-order `targets` to appear after the `// target` comment.
|
||||||
|
delete projectConfiguration.targets;
|
||||||
|
projectConfiguration[
|
||||||
|
'// targets'
|
||||||
|
] = `to see all targets run: nx show project ${projectName} --web`;
|
||||||
|
projectConfiguration.targets = {};
|
||||||
|
} else {
|
||||||
|
delete projectConfiguration['// targets'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectConfigFile = joinPathFragments(
|
||||||
|
projectConfiguration.root,
|
||||||
|
'project.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tree.exists(projectConfigFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleEmptyTargets(projectName, projectConfiguration);
|
||||||
|
writeJson(tree, projectConfigFile, {
|
||||||
|
name: projectConfiguration.name ?? projectName,
|
||||||
|
$schema: getRelativeProjectJsonSchemaPath(tree, projectConfiguration),
|
||||||
|
...projectConfiguration,
|
||||||
|
root: undefined,
|
||||||
|
});
|
||||||
|
projectGraph.nodes[projectName].data = projectConfiguration;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('nx/src/devkit-internals', () => ({
|
||||||
|
...jest.requireActual('nx/src/devkit-internals'),
|
||||||
|
getExecutorInformation: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((pkg, ...args) =>
|
||||||
|
jest
|
||||||
|
.requireActual('nx/src/devkit-internals')
|
||||||
|
.getExecutorInformation('@nx/webpack', ...args)
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
function addProject(tree: Tree, name: string, project: ProjectConfiguration) {
|
||||||
|
addProjectConfiguration(tree, name, project);
|
||||||
|
projectGraph.nodes[name] = {
|
||||||
|
name: name,
|
||||||
|
type: project.projectType === 'application' ? 'app' : 'lib',
|
||||||
|
data: {
|
||||||
|
projectType: project.projectType,
|
||||||
|
root: project.root,
|
||||||
|
targets: project.targets,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectOptions {
|
||||||
|
appName: string;
|
||||||
|
appRoot: string;
|
||||||
|
buildAndroidTargetName: string;
|
||||||
|
buildIosTargetName: string;
|
||||||
|
testAndroidTargetName: string;
|
||||||
|
testIosTargetName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultProjectOptions: ProjectOptions = {
|
||||||
|
appName: 'demo-e2e',
|
||||||
|
appRoot: 'apps/demo-e2e',
|
||||||
|
buildAndroidTargetName: 'build-android',
|
||||||
|
buildIosTargetName: 'build-ios',
|
||||||
|
testAndroidTargetName: 'test-android',
|
||||||
|
testIosTargetName: 'test-ios',
|
||||||
|
};
|
||||||
|
|
||||||
|
const detoxConfig = {
|
||||||
|
testRunner: {
|
||||||
|
args: {
|
||||||
|
$0: 'jest',
|
||||||
|
config: './jest.config.json',
|
||||||
|
},
|
||||||
|
jest: {
|
||||||
|
setupTimeout: 120000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apps: {
|
||||||
|
'ios.debug': {
|
||||||
|
type: 'ios.app',
|
||||||
|
build:
|
||||||
|
"cd ../demo/ios && xcodebuild -workspace Demo.xcworkspace -scheme Demo -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
|
binaryPath:
|
||||||
|
'../demo/ios/build/Build/Products/Debug-iphonesimulator/Demo.app',
|
||||||
|
},
|
||||||
|
'ios.release': {
|
||||||
|
type: 'ios.app',
|
||||||
|
build:
|
||||||
|
"cd ../demo/ios && xcodebuild -workspace Demo.xcworkspace -scheme Demo -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
|
binaryPath:
|
||||||
|
'../demo/ios/build/Build/Products/Release-iphonesimulator/Demo.app',
|
||||||
|
},
|
||||||
|
|
||||||
|
'android.debug': {
|
||||||
|
type: 'android.apk',
|
||||||
|
build:
|
||||||
|
'cd ../demo/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
|
||||||
|
binaryPath: '../demo/android/app/build/outputs/apk/debug/app-debug.apk',
|
||||||
|
},
|
||||||
|
'android.release': {
|
||||||
|
type: 'android.apk',
|
||||||
|
build:
|
||||||
|
'cd ../demo/android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release',
|
||||||
|
binaryPath:
|
||||||
|
'../demo/android/app/build/outputs/apk/release/app-release.apk',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
devices: {
|
||||||
|
simulator: {
|
||||||
|
type: 'ios.simulator',
|
||||||
|
device: {
|
||||||
|
type: 'iPhone 15 Plus',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emulator: {
|
||||||
|
type: 'android.emulator',
|
||||||
|
device: {
|
||||||
|
avdName: 'Pixel_4a_API_30',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
'ios.sim.release': {
|
||||||
|
device: 'simulator',
|
||||||
|
app: 'ios.release',
|
||||||
|
},
|
||||||
|
'ios.sim.debug': {
|
||||||
|
device: 'simulator',
|
||||||
|
app: 'ios.debug',
|
||||||
|
},
|
||||||
|
|
||||||
|
'android.emu.release': {
|
||||||
|
device: 'emulator',
|
||||||
|
app: 'android.release',
|
||||||
|
},
|
||||||
|
'android.emu.debug': {
|
||||||
|
device: 'emulator',
|
||||||
|
app: 'android.debug',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function writeDetoxConfig(tree: Tree, projectRoot: string) {
|
||||||
|
tree.write(`${projectRoot}/.detoxrc.json`, JSON.stringify(detoxConfig));
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectRoot}/.detoxrc.json`,
|
||||||
|
JSON.stringify(detoxConfig)
|
||||||
|
);
|
||||||
|
jest.doMock(
|
||||||
|
join(fs.tempDir, projectRoot, '.detoxrc.json'),
|
||||||
|
() => detoxConfig,
|
||||||
|
{
|
||||||
|
virtual: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProject(
|
||||||
|
tree: Tree,
|
||||||
|
options: Partial<ProjectOptions> = {},
|
||||||
|
extraTargetOptions?: Record<string, Record<string, unknown>>,
|
||||||
|
extraTargetConfigurations?: Record<
|
||||||
|
string,
|
||||||
|
Record<string, Record<string, unknown>>
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
let projectOptions = { ...defaultProjectOptions, ...options };
|
||||||
|
const project: ProjectConfiguration = {
|
||||||
|
name: projectOptions.appName,
|
||||||
|
root: projectOptions.appRoot,
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
[projectOptions.buildAndroidTargetName]: {
|
||||||
|
executor: '@nx/detox:build',
|
||||||
|
options: {
|
||||||
|
detoxConfiguration: 'android.emu.debug',
|
||||||
|
...extraTargetOptions?.[projectOptions.buildAndroidTargetName],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
...extraTargetConfigurations?.[
|
||||||
|
projectOptions.buildAndroidTargetName
|
||||||
|
].production,
|
||||||
|
detoxConfiguration: 'android.emu.release',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.buildIosTargetName]: {
|
||||||
|
executor: '@nx/detox:build',
|
||||||
|
options: {
|
||||||
|
detoxConfiguration: 'ios.sim.debug',
|
||||||
|
...extraTargetOptions?.[projectOptions.buildIosTargetName],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
...extraTargetConfigurations?.[projectOptions.buildIosTargetName]
|
||||||
|
.production,
|
||||||
|
detoxConfiguration: 'ios.sim.release',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.testAndroidTargetName]: {
|
||||||
|
executor: '@nx/detox:test',
|
||||||
|
options: {
|
||||||
|
detoxConfiguration: 'android.emu.debug',
|
||||||
|
buildTarget: 'demo-e2e:build-android',
|
||||||
|
...extraTargetOptions?.[projectOptions.testAndroidTargetName],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
detoxConfiguration: 'android.emu.release',
|
||||||
|
buildTarget: 'demo-e2e:build-android:production',
|
||||||
|
...extraTargetConfigurations?.[projectOptions.testAndroidTargetName]
|
||||||
|
.production,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.testIosTargetName]: {
|
||||||
|
executor: '@nx/detox:test',
|
||||||
|
options: {
|
||||||
|
detoxConfiguration: 'ios.sim.debug',
|
||||||
|
buildTarget: 'demo-e2e:build-ios',
|
||||||
|
...extraTargetOptions?.[projectOptions.testIosTargetName],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
detoxConfiguration: 'ios.sim.release',
|
||||||
|
buildTarget: 'demo-e2e:build-ios:production',
|
||||||
|
...extraTargetConfigurations?.[projectOptions.testIosTargetName]
|
||||||
|
.production,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addProject(tree, project.name, project);
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectOptions.appRoot}/project.json`,
|
||||||
|
JSON.stringify(project)
|
||||||
|
);
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('convert-to-inferred', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs = new TempFs('detox');
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.root = fs.tempDir;
|
||||||
|
|
||||||
|
projectGraph = {
|
||||||
|
nodes: {},
|
||||||
|
dependencies: {},
|
||||||
|
externalNodes: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.cleanup();
|
||||||
|
jest.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert project to use inference plugin', async () => {
|
||||||
|
const project = createProject(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
'build-android': {
|
||||||
|
configPath: '.detoxrc.dev.json',
|
||||||
|
},
|
||||||
|
'build-ios': {
|
||||||
|
configPath: '.detoxrc.dev.json',
|
||||||
|
},
|
||||||
|
'test-android': {
|
||||||
|
configPath: '.detoxrc.dev.json',
|
||||||
|
},
|
||||||
|
'test-ios': {
|
||||||
|
configPath: '.detoxrc.dev.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'build-android': {
|
||||||
|
production: { configPath: '.detoxrc.prod.json' },
|
||||||
|
},
|
||||||
|
'build-ios': {
|
||||||
|
production: { configPath: '.detoxrc.prod.json' },
|
||||||
|
},
|
||||||
|
'test-android': {
|
||||||
|
production: { configPath: '.detoxrc.prod.json' },
|
||||||
|
},
|
||||||
|
'test-ios': {
|
||||||
|
production: { configPath: '.detoxrc.prod.json' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
writeDetoxConfig(tree, project.root);
|
||||||
|
|
||||||
|
await convertToInferred(tree, { project: project.name });
|
||||||
|
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project.name);
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(nxJson.plugins).toEqual([
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
buildTargetName: 'build',
|
||||||
|
startTargetName: 'start',
|
||||||
|
testTargetName: 'test',
|
||||||
|
},
|
||||||
|
plugin: '@nx/detox/plugin',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(projectConfig.targets['build-android']).toEqual({
|
||||||
|
command: 'nx run demo-e2e:build',
|
||||||
|
options: {
|
||||||
|
args: [
|
||||||
|
'--args="-c android.emu.debug"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.dev.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
args: [
|
||||||
|
'--args="-c android.emu.release"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.prod.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['build-ios']).toEqual({
|
||||||
|
command: 'nx run demo-e2e:build',
|
||||||
|
options: {
|
||||||
|
args: [
|
||||||
|
'--args="-c ios.sim.debug"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.dev.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
args: [
|
||||||
|
'--args="-c ios.sim.release"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.prod.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['test-android']).toEqual({
|
||||||
|
command: 'nx run demo-e2e:test',
|
||||||
|
options: {
|
||||||
|
args: [
|
||||||
|
'--args="-c android.emu.debug"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.dev.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
args: [
|
||||||
|
'--args="-c android.emu.release"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.prod.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['test-ios']).toEqual({
|
||||||
|
command: 'nx run demo-e2e:test',
|
||||||
|
options: {
|
||||||
|
args: [
|
||||||
|
'--args="-c ios.sim.debug"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.dev.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
args: [
|
||||||
|
'--args="-c ios.sim.release"',
|
||||||
|
'--config-path',
|
||||||
|
'.detoxrc.prod.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
|
formatFiles,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
type Tree,
|
||||||
|
updateNxJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { migrateProjectExecutorsToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
|
||||||
|
import { createNodes } from '../../plugins/plugin';
|
||||||
|
import { processBuildOptions } from './lib/process-build-options';
|
||||||
|
import { postTargetTransformer } from './lib/post-target-transformer';
|
||||||
|
import { processTestOptions } from './lib/process-test-options';
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
project?: string;
|
||||||
|
skipFormat?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertToInferred(tree: Tree, options: Schema) {
|
||||||
|
const projectGraph = await createProjectGraphAsync();
|
||||||
|
const migrationLogs = new AggregatedLog();
|
||||||
|
const migratedProjects = await migrateProjectExecutorsToPluginV1(
|
||||||
|
tree,
|
||||||
|
projectGraph,
|
||||||
|
'@nx/detox/plugin',
|
||||||
|
createNodes,
|
||||||
|
{
|
||||||
|
buildTargetName: 'build',
|
||||||
|
startTargetName: 'start',
|
||||||
|
testTargetName: 'test',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
executors: ['@nx/detox:build'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processBuildOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
buildTargetName: targetName, // We should use "build" instead of "build-ios" or "build-android". We'll handle this later.
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/detox:test'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processTestOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
testTargetName: targetName, // We should use "test" instead of "test-ios" or "test-android". We'll handle this later.
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options.project
|
||||||
|
);
|
||||||
|
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
const detoxPlugins = nxJson.plugins?.filter(
|
||||||
|
(p) => typeof p !== 'string' && p.plugin === '@nx/detox/plugin'
|
||||||
|
);
|
||||||
|
|
||||||
|
// These were either `build-ios`, `test-ios`, etc., and we need to set them back to their generic names.
|
||||||
|
// The per-project targets will call these with additional `--args` passed to maintain the same
|
||||||
|
// behavior as previous executor-based targets.
|
||||||
|
for (const p of detoxPlugins) {
|
||||||
|
if (typeof p === 'string') continue;
|
||||||
|
p.options['buildTargetName'] = 'build';
|
||||||
|
p.options['testTargetName'] = 'test';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inform the users that the inferred targets are platform-agnostic, and they can remove the old targets if unnecessary.
|
||||||
|
for (const [project] of migratedProjects) {
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project,
|
||||||
|
executorName: '@nx/detox:build',
|
||||||
|
log: `The "build-android" target was migrated to use "nx run ${project}:build", which is platform-agnostic. If you no longer need this target, you can remove it.`,
|
||||||
|
});
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project,
|
||||||
|
executorName: '@nx/detox:test',
|
||||||
|
log: `The "test-android" target was migrated to use "nx run ${project}:test", which is platform-agnostic. If you no longer need this target, you can remove it.`,
|
||||||
|
});
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project,
|
||||||
|
executorName: '@nx/detox:build',
|
||||||
|
log: `The "build-ios" target was migrated to use "nx run ${project}:build", which is platform-agnostic. If you no longer need this target, you can remove it.`,
|
||||||
|
});
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project,
|
||||||
|
executorName: '@nx/detox:test',
|
||||||
|
log: `The "test-ios" target was migrated to use "nx run ${project}:test", which is platform-agnostic. If you no longer need this target, you can remove it.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
|
||||||
|
if (migratedProjects.size === 0) {
|
||||||
|
throw new Error('Could not find any targets to migrate.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
migrationLogs.flushLogs();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default convertToInferred;
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import type { TargetConfiguration, Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processTargetOutputs } from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
|
||||||
|
|
||||||
|
export function postTargetTransformer(
|
||||||
|
migrationLogs: AggregatedLog,
|
||||||
|
processOptions: (
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
target: TargetConfiguration | undefined,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
target: TargetConfiguration,
|
||||||
|
tree: Tree,
|
||||||
|
projectDetails: { projectName: string; root: string },
|
||||||
|
inferredTargetConfiguration: TargetConfiguration
|
||||||
|
) => {
|
||||||
|
if (target.options) {
|
||||||
|
processOptions(
|
||||||
|
tree,
|
||||||
|
target.options,
|
||||||
|
projectDetails.projectName,
|
||||||
|
projectDetails.root,
|
||||||
|
target,
|
||||||
|
migrationLogs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.configurations) {
|
||||||
|
for (const configurationName in target.configurations) {
|
||||||
|
const configuration = target.configurations[configurationName];
|
||||||
|
processOptions(
|
||||||
|
tree,
|
||||||
|
configuration,
|
||||||
|
projectDetails.projectName,
|
||||||
|
projectDetails.root,
|
||||||
|
undefined,
|
||||||
|
migrationLogs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(target.configurations).length === 0) {
|
||||||
|
if ('defaultConfiguration' in target) {
|
||||||
|
delete target.defaultConfiguration;
|
||||||
|
}
|
||||||
|
delete target.configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
'defaultConfiguration' in target &&
|
||||||
|
!target.configurations[target.defaultConfiguration]
|
||||||
|
) {
|
||||||
|
delete target.defaultConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.outputs) {
|
||||||
|
processTargetOutputs(target, [], inferredTargetConfiguration, {
|
||||||
|
projectName: projectDetails.projectName,
|
||||||
|
projectRoot: projectDetails.root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { names, type TargetConfiguration, type Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
|
||||||
|
export function processBuildOptions(
|
||||||
|
_tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
_projectRoot: string,
|
||||||
|
target: TargetConfiguration | undefined,
|
||||||
|
_migrationLogs: AggregatedLog
|
||||||
|
): void {
|
||||||
|
const args: string[] = [];
|
||||||
|
|
||||||
|
if ('detoxConfiguration' in options) {
|
||||||
|
// Need to wrap in --args since --configuration/-c is swallowed by Nx CLI.
|
||||||
|
args.push(`--args="-c ${options.detoxConfiguration}"`);
|
||||||
|
delete options.detoxConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(options)) {
|
||||||
|
let value = options[key];
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (value) args.push(`--${names(key).fileName}`);
|
||||||
|
} else {
|
||||||
|
args.push(`--${names(key).fileName}`, value);
|
||||||
|
}
|
||||||
|
delete options[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
target.command = `nx run ${projectName}:build`;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.args = args;
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import { names, type TargetConfiguration, type Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
|
||||||
|
export function processTestOptions(
|
||||||
|
_tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
_projectRoot: string,
|
||||||
|
target: TargetConfiguration | undefined,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
): void {
|
||||||
|
const args: string[] = [];
|
||||||
|
|
||||||
|
if ('detoxConfiguration' in options) {
|
||||||
|
// Need to wrap in --args since --configuration/-c is swallowed by Nx CLI.
|
||||||
|
args.push(`--args="-c ${options.detoxConfiguration}"`);
|
||||||
|
delete options.detoxConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('deviceBootArgs' in options) {
|
||||||
|
args.push(`--device-boot-args="${options.deviceBootArgs}"`); // the value must be specified after an equal sign (=) and inside quotes: https://wix.github.io/Detox/docs/cli/test
|
||||||
|
delete options.deviceBootArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('appLaunchArgs' in options) {
|
||||||
|
args.push(`--app-launch-args="${options.appLaunchArgs}"`); // the value must be specified after an equal sign (=) and inside quotes: https://wix.github.io/Detox/docs/cli/test
|
||||||
|
delete options.appLaunchArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('color' in options) {
|
||||||
|
// detox only accepts --no-color, not --color
|
||||||
|
if (!options.color) args.push('--no-color');
|
||||||
|
delete options.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('buildTarget' in options) {
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project: projectName,
|
||||||
|
executorName: '@nx/expo:test',
|
||||||
|
log: 'Unable to migrate `buildTarget` for Detox test. Use "nx run <project>:run-ios" or "nx run <project>:run-android", and pass "--reuse" option when running tests.',
|
||||||
|
});
|
||||||
|
delete options.buildTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deprecatedOptions = [
|
||||||
|
'runnerConfig',
|
||||||
|
'recordTimeline',
|
||||||
|
'workers',
|
||||||
|
'deviceLaunchArgs',
|
||||||
|
];
|
||||||
|
for (const key of deprecatedOptions) {
|
||||||
|
if (!(key in options)) continue;
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project: projectName,
|
||||||
|
executorName: '@nx/expo:test',
|
||||||
|
log: `Option "${key}" is not migrated since it was removed in Detox 20.`,
|
||||||
|
});
|
||||||
|
delete options[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(options)) {
|
||||||
|
let value = options[key];
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (value) args.push(`--${names(key).fileName}`);
|
||||||
|
} else {
|
||||||
|
args.push(`--${names(key).fileName}`, value);
|
||||||
|
}
|
||||||
|
delete options[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
target.command = `nx run ${projectName}:test`;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.args = args;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxDetoxConvertToInferred",
|
||||||
|
"description": "Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert Detox project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/detox:*` executors to use `@nx/detox/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,6 +28,11 @@
|
|||||||
"schema": "./src/generators/component/schema.json",
|
"schema": "./src/generators/component/schema.json",
|
||||||
"description": "Create a component",
|
"description": "Create a component",
|
||||||
"aliases": ["c"]
|
"aliases": ["c"]
|
||||||
|
},
|
||||||
|
"convert-to-inferred": {
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": "./src/generators/convert-to-inferred/schema.json",
|
||||||
|
"description": "Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,7 +139,7 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
'../my-dir/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.local': {
|
'ios.local': {
|
||||||
@ -152,7 +152,7 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
'../my-dir/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -200,7 +200,7 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.local': {
|
'ios.local': {
|
||||||
@ -213,7 +213,7 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -264,7 +264,7 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.local': {
|
'ios.local': {
|
||||||
@ -277,7 +277,7 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,464 @@
|
|||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
type ExpandedPluginConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
|
type ProjectConfiguration,
|
||||||
|
type ProjectGraph,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
type Tree,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { getRelativeProjectJsonSchemaPath } from 'nx/src/generators/utils/project-configuration';
|
||||||
|
import { convertToInferred } from './convert-to-inferred';
|
||||||
|
|
||||||
|
let fs: TempFs;
|
||||||
|
let projectGraph: ProjectGraph;
|
||||||
|
jest.mock('@nx/devkit', () => ({
|
||||||
|
...jest.requireActual('@nx/devkit'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve(projectGraph)),
|
||||||
|
updateProjectConfiguration: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((tree, projectName, projectConfiguration) => {
|
||||||
|
function handleEmptyTargets(
|
||||||
|
projectName: string,
|
||||||
|
projectConfiguration: ProjectConfiguration
|
||||||
|
): void {
|
||||||
|
if (
|
||||||
|
projectConfiguration.targets &&
|
||||||
|
!Object.keys(projectConfiguration.targets).length
|
||||||
|
) {
|
||||||
|
// Re-order `targets` to appear after the `// target` comment.
|
||||||
|
delete projectConfiguration.targets;
|
||||||
|
projectConfiguration[
|
||||||
|
'// targets'
|
||||||
|
] = `to see all targets run: nx show project ${projectName} --web`;
|
||||||
|
projectConfiguration.targets = {};
|
||||||
|
} else {
|
||||||
|
delete projectConfiguration['// targets'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectConfigFile = joinPathFragments(
|
||||||
|
projectConfiguration.root,
|
||||||
|
'project.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tree.exists(projectConfigFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleEmptyTargets(projectName, projectConfiguration);
|
||||||
|
writeJson(tree, projectConfigFile, {
|
||||||
|
name: projectConfiguration.name ?? projectName,
|
||||||
|
$schema: getRelativeProjectJsonSchemaPath(tree, projectConfiguration),
|
||||||
|
...projectConfiguration,
|
||||||
|
root: undefined,
|
||||||
|
});
|
||||||
|
projectGraph.nodes[projectName].data = projectConfiguration;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('nx/src/devkit-internals', () => ({
|
||||||
|
...jest.requireActual('nx/src/devkit-internals'),
|
||||||
|
getExecutorInformation: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((pkg, ...args) =>
|
||||||
|
jest
|
||||||
|
.requireActual('nx/src/devkit-internals')
|
||||||
|
.getExecutorInformation('@nx/webpack', ...args)
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
function addProject(tree: Tree, name: string, project: ProjectConfiguration) {
|
||||||
|
addProjectConfiguration(tree, name, project);
|
||||||
|
projectGraph.nodes[name] = {
|
||||||
|
name: name,
|
||||||
|
type: project.projectType === 'application' ? 'app' : 'lib',
|
||||||
|
data: {
|
||||||
|
projectType: project.projectType,
|
||||||
|
root: project.root,
|
||||||
|
targets: project.targets,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectOptions {
|
||||||
|
appName: string;
|
||||||
|
appRoot: string;
|
||||||
|
buildTargetName: string;
|
||||||
|
exportTargetName: string;
|
||||||
|
installTargetName: string;
|
||||||
|
prebuildTargetName: string;
|
||||||
|
runIosTargetName: string;
|
||||||
|
runAndroidTargetName: string;
|
||||||
|
serveTargetName: string;
|
||||||
|
startTargetName: string;
|
||||||
|
submitTargetName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultProjectOptions: ProjectOptions = {
|
||||||
|
appName: 'demo',
|
||||||
|
appRoot: 'apps/demo',
|
||||||
|
buildTargetName: 'build',
|
||||||
|
exportTargetName: 'export',
|
||||||
|
installTargetName: 'install',
|
||||||
|
prebuildTargetName: 'prebuild',
|
||||||
|
runAndroidTargetName: 'run-android',
|
||||||
|
runIosTargetName: 'run-ios',
|
||||||
|
serveTargetName: 'serve',
|
||||||
|
startTargetName: 'start',
|
||||||
|
submitTargetName: 'submit',
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultExpoConfig = {
|
||||||
|
expo: {
|
||||||
|
name: 'demo',
|
||||||
|
slug: 'demo',
|
||||||
|
version: '1.0.0',
|
||||||
|
orientation: 'portrait',
|
||||||
|
icon: './assets/icon.png',
|
||||||
|
splash: {
|
||||||
|
image: './assets/splash.png',
|
||||||
|
resizeMode: 'contain',
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
},
|
||||||
|
updates: {
|
||||||
|
fallbackToCacheTimeout: 0,
|
||||||
|
},
|
||||||
|
assetBundlePatterns: ['**/*'],
|
||||||
|
ios: {
|
||||||
|
supportsTablet: true,
|
||||||
|
bundleIdentifier: 'com.anonymous.demo',
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
adaptiveIcon: {
|
||||||
|
foregroundImage: './assets/adaptive-icon.png',
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
favicon: './assets/favicon.png',
|
||||||
|
bundler: 'metro',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function writeExpoConfig(
|
||||||
|
tree: Tree,
|
||||||
|
projectRoot: string,
|
||||||
|
expoConfig = defaultExpoConfig
|
||||||
|
) {
|
||||||
|
tree.write(`${projectRoot}/app.json`, JSON.stringify(expoConfig));
|
||||||
|
fs.createFileSync(`${projectRoot}/app.json`, JSON.stringify(expoConfig));
|
||||||
|
jest.doMock(join(fs.tempDir, projectRoot, 'app.json'), () => expoConfig, {
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProject(
|
||||||
|
tree: Tree,
|
||||||
|
options: Partial<ProjectOptions> = {},
|
||||||
|
additionalTargetOptions?: Record<string, Record<string, unknown>>
|
||||||
|
) {
|
||||||
|
let projectOptions = { ...defaultProjectOptions, ...options };
|
||||||
|
const project: ProjectConfiguration = {
|
||||||
|
name: projectOptions.appName,
|
||||||
|
root: projectOptions.appRoot,
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
[projectOptions.buildTargetName]: {
|
||||||
|
executor: '@nx/expo:build',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.buildTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.exportTargetName]: {
|
||||||
|
executor: '@nx/expo:export',
|
||||||
|
options: {
|
||||||
|
platform: 'all',
|
||||||
|
outputDir: `dist/${projectOptions.appName}`,
|
||||||
|
...additionalTargetOptions?.[projectOptions.exportTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.installTargetName]: {
|
||||||
|
executor: '@nx/expo:install',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.installTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.prebuildTargetName]: {
|
||||||
|
executor: '@nx/expo:prebuild',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.prebuildTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.runAndroidTargetName]: {
|
||||||
|
executor: '@nx/expo:run',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.runAndroidTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.runIosTargetName]: {
|
||||||
|
executor: '@nx/expo:run',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.runIosTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.serveTargetName]: {
|
||||||
|
executor: '@nx/expo:serve',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.startTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.startTargetName]: {
|
||||||
|
executor: '@nx/expo:start',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.serveTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.submitTargetName]: {
|
||||||
|
executor: '@nx/expo:submit',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.submitTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addProject(tree, project.name, project);
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectOptions.appRoot}/project.json`,
|
||||||
|
JSON.stringify(project)
|
||||||
|
);
|
||||||
|
|
||||||
|
// These file need to exist for inference, but they can be empty for the convert generator
|
||||||
|
fs.createFileSync(`${projectOptions.appRoot}/package.json`, '{}');
|
||||||
|
fs.createFileSync(`${projectOptions.appRoot}/metro.config.js`, '// empty');
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('convert-to-inferred', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs = new TempFs('expo');
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.root = fs.tempDir;
|
||||||
|
|
||||||
|
projectGraph = {
|
||||||
|
nodes: {},
|
||||||
|
dependencies: {},
|
||||||
|
externalNodes: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.cleanup();
|
||||||
|
jest.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert project to use inference plugin', async () => {
|
||||||
|
const project = createProject(tree);
|
||||||
|
writeExpoConfig(tree, project.root);
|
||||||
|
|
||||||
|
const project2 = createProject(tree, {
|
||||||
|
appName: 'app2',
|
||||||
|
appRoot: 'apps/app2',
|
||||||
|
});
|
||||||
|
|
||||||
|
const project2Build = project2.targets.build;
|
||||||
|
|
||||||
|
await convertToInferred(tree, { project: project.name });
|
||||||
|
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const expoPlugin = nxJsonPlugins.find(
|
||||||
|
(plugin): plugin is ExpandedPluginConfiguration =>
|
||||||
|
typeof plugin !== 'string' && plugin.plugin === '@nx/expo/plugin'
|
||||||
|
);
|
||||||
|
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project.name);
|
||||||
|
|
||||||
|
expect(expoPlugin).toBeDefined();
|
||||||
|
expect(projectConfig.targets).toEqual({
|
||||||
|
export: {
|
||||||
|
options: {
|
||||||
|
args: ['--output-dir=../../dist/demo', '--platform=all'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedProject2 = readProjectConfiguration(tree, project2.name);
|
||||||
|
expect(updatedProject2.targets.build).toEqual(project2Build);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should migrate options to CLI options and args', async () => {
|
||||||
|
const project = createProject(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
build: {
|
||||||
|
wait: true,
|
||||||
|
clearCache: true,
|
||||||
|
},
|
||||||
|
export: { bytecode: false, minify: false, platform: 'android' },
|
||||||
|
'run-android': {
|
||||||
|
platform: 'android',
|
||||||
|
variant: 'release',
|
||||||
|
clean: true,
|
||||||
|
bundler: false,
|
||||||
|
},
|
||||||
|
'run-ios': {
|
||||||
|
platform: 'ios',
|
||||||
|
xcodeConfiguration: 'Release',
|
||||||
|
buildCache: false,
|
||||||
|
},
|
||||||
|
install: { check: true },
|
||||||
|
prebuild: { clean: true },
|
||||||
|
serve: { dev: false },
|
||||||
|
start: { dev: false },
|
||||||
|
submit: { wait: true, interactive: false },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
writeExpoConfig(tree, project.root);
|
||||||
|
|
||||||
|
await convertToInferred(tree, { project: project.name });
|
||||||
|
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project.name);
|
||||||
|
expect(projectConfig.targets.build.options).toEqual({
|
||||||
|
args: ['--wait', '--clear-cache'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets.export.options).toEqual({
|
||||||
|
args: [
|
||||||
|
'--no-minify',
|
||||||
|
'--no-bytecode',
|
||||||
|
'--output-dir=../../dist/demo',
|
||||||
|
'--platform=android',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets.install.options).toEqual({
|
||||||
|
args: ['--check'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets.prebuild.options).toEqual({
|
||||||
|
args: ['--clean'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['run-android'].options).toEqual({
|
||||||
|
args: ['--variant', 'release', '--no-bundler'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['run-ios'].options).toEqual({
|
||||||
|
args: ['--configuration', 'Release', '--no-build-cache'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets.serve.options).toEqual({
|
||||||
|
args: ['--no-dev'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets.start.options).toEqual({
|
||||||
|
args: ['--no-dev'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets.submit.options).toEqual({
|
||||||
|
args: ['--non-interactive', '--wait'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should migrate custom run:ios and run:android target names', async () => {
|
||||||
|
const project1 = createProject(
|
||||||
|
tree,
|
||||||
|
{
|
||||||
|
appName: 'app1',
|
||||||
|
appRoot: 'apps/app1',
|
||||||
|
runAndroidTargetName: 'run-android-custom-1',
|
||||||
|
runIosTargetName: 'run-ios-custom-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'run-android-custom-1': {
|
||||||
|
platform: 'android',
|
||||||
|
buildCache: false,
|
||||||
|
},
|
||||||
|
'run-ios-custom-1': {
|
||||||
|
platform: 'ios',
|
||||||
|
buildCache: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const project2 = createProject(
|
||||||
|
tree,
|
||||||
|
{
|
||||||
|
appName: 'app2',
|
||||||
|
appRoot: 'apps/app2',
|
||||||
|
runAndroidTargetName: 'run-android-custom-2',
|
||||||
|
runIosTargetName: 'run-ios-custom-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'run-android-custom-2': {
|
||||||
|
platform: 'android',
|
||||||
|
variant: 'release',
|
||||||
|
},
|
||||||
|
'run-ios-custom-2': {
|
||||||
|
platform: 'ios',
|
||||||
|
xcodeConfiguration: 'Release',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
writeExpoConfig(tree, project2.root);
|
||||||
|
writeExpoConfig(tree, project1.root);
|
||||||
|
|
||||||
|
await convertToInferred(tree, {});
|
||||||
|
|
||||||
|
const config1 = readProjectConfiguration(tree, project1.name);
|
||||||
|
const config2 = readProjectConfiguration(tree, project2.name);
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
|
||||||
|
expect(config1.targets['run-android-custom-1'].options).toEqual({
|
||||||
|
args: ['--no-build-cache'],
|
||||||
|
});
|
||||||
|
expect(config1.targets['run-ios-custom-1'].options).toEqual({
|
||||||
|
args: ['--no-build-cache'],
|
||||||
|
});
|
||||||
|
expect(config2.targets['run-android-custom-2'].options).toEqual({
|
||||||
|
args: ['--variant', 'release'],
|
||||||
|
});
|
||||||
|
expect(config2.targets['run-ios-custom-2'].options).toEqual({
|
||||||
|
args: ['--configuration', 'Release'],
|
||||||
|
});
|
||||||
|
expect(nxJson.plugins).toEqual([
|
||||||
|
{
|
||||||
|
plugin: '@nx/expo/plugin',
|
||||||
|
options: {
|
||||||
|
buildTargetName: 'build',
|
||||||
|
exportTargetName: 'export',
|
||||||
|
installTargetName: 'install',
|
||||||
|
prebuildTargetName: 'prebuild',
|
||||||
|
runAndroidTargetName: 'run-android-custom-1',
|
||||||
|
runIosTargetName: 'run-ios-custom-1',
|
||||||
|
serveTargetName: 'serve',
|
||||||
|
startTargetName: 'start',
|
||||||
|
submitTargetName: 'submit',
|
||||||
|
},
|
||||||
|
include: ['apps/app1/**/*'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plugin: '@nx/expo/plugin',
|
||||||
|
options: {
|
||||||
|
buildTargetName: 'build',
|
||||||
|
exportTargetName: 'export',
|
||||||
|
installTargetName: 'install',
|
||||||
|
prebuildTargetName: 'prebuild',
|
||||||
|
runAndroidTargetName: 'run-android-custom-2',
|
||||||
|
runIosTargetName: 'run-ios-custom-2',
|
||||||
|
serveTargetName: 'serve',
|
||||||
|
startTargetName: 'start',
|
||||||
|
submitTargetName: 'submit',
|
||||||
|
},
|
||||||
|
include: ['apps/app2/**/*'],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
|
formatFiles,
|
||||||
|
getProjects,
|
||||||
|
type Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { migrateProjectExecutorsToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
|
||||||
|
import { createNodes } from '../../../plugins/plugin';
|
||||||
|
import { processBuildOptions } from './lib/process-build-options';
|
||||||
|
import { postTargetTransformer } from './lib/post-target-transformer';
|
||||||
|
import { processExportOptions } from './lib/process-export-options';
|
||||||
|
import { processRunOptions } from './lib/process-run-options';
|
||||||
|
import { processServeOptions } from './lib/process-serve-options';
|
||||||
|
import { processStartOptions } from './lib/process-start-options';
|
||||||
|
import { processSubmitOptions } from './lib/process-submit-options';
|
||||||
|
import { processPrebuildOptions } from './lib/process-prebuild-options';
|
||||||
|
import { processInstallOptions } from './lib/process-install-options';
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
project?: string;
|
||||||
|
skipFormat?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertToInferred(tree: Tree, options: Schema) {
|
||||||
|
const projectGraph = await createProjectGraphAsync();
|
||||||
|
const migrationLogs = new AggregatedLog();
|
||||||
|
const projects = getProjects(tree);
|
||||||
|
const migratedProjects = await migrateProjectExecutorsToPluginV1(
|
||||||
|
tree,
|
||||||
|
projectGraph,
|
||||||
|
'@nx/expo/plugin',
|
||||||
|
createNodes,
|
||||||
|
{
|
||||||
|
buildTargetName: 'build',
|
||||||
|
exportTargetName: 'export',
|
||||||
|
installTargetName: 'install',
|
||||||
|
prebuildTargetName: 'prebuild',
|
||||||
|
runAndroidTargetName: 'run-android',
|
||||||
|
runIosTargetName: 'run-ios',
|
||||||
|
serveTargetName: 'serve',
|
||||||
|
startTargetName: 'start',
|
||||||
|
submitTargetName: 'submit',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:build'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processBuildOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
buildTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:export'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processExportOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
exportTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:install'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processInstallOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
installTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:prebuild'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processPrebuildOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
prebuildTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:run'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processRunOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => {
|
||||||
|
// Assumption: There are no targets with the same name but different platforms.
|
||||||
|
// Most users will likely keep the `run-ios` and `run-android` target names that are generated.
|
||||||
|
// Otherwise, we look for the first target with a matching name, and use that platform.
|
||||||
|
const platform = getPlatformForFirstMatchedTarget(
|
||||||
|
targetName,
|
||||||
|
'@nx/expo:run',
|
||||||
|
projects
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
[platform === 'android'
|
||||||
|
? 'runAndroidTargetName'
|
||||||
|
: 'runIosTargetName']: targetName,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:serve'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processServeOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
serveTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:start'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processStartOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
startTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/expo:submit'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processSubmitOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
submitTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options.project
|
||||||
|
);
|
||||||
|
|
||||||
|
if (migratedProjects.size === 0) {
|
||||||
|
throw new Error('Could not find any targets to migrate.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
migrationLogs.flushLogs();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlatformForFirstMatchedTarget(
|
||||||
|
targetName: string,
|
||||||
|
executorName: string,
|
||||||
|
projects: Map<string, any>
|
||||||
|
): string {
|
||||||
|
for (const [, project] of projects) {
|
||||||
|
const target = project.targets[targetName];
|
||||||
|
if (target && target.executor === executorName && target.options.platform) {
|
||||||
|
return target.options.platform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Default is ios in executor, although we do always generate it in project.json.
|
||||||
|
return 'ios';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default convertToInferred;
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
import type { TargetConfiguration, Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processTargetOutputs } from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
|
||||||
|
|
||||||
|
export function postTargetTransformer(
|
||||||
|
migrationLogs: AggregatedLog,
|
||||||
|
processOptions: (
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
target: TargetConfiguration,
|
||||||
|
tree: Tree,
|
||||||
|
projectDetails: { projectName: string; root: string },
|
||||||
|
inferredTargetConfiguration: TargetConfiguration
|
||||||
|
) => {
|
||||||
|
if (target.options) {
|
||||||
|
processOptions(
|
||||||
|
tree,
|
||||||
|
target.options,
|
||||||
|
projectDetails.projectName,
|
||||||
|
projectDetails.root,
|
||||||
|
migrationLogs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.configurations) {
|
||||||
|
for (const configurationName in target.configurations) {
|
||||||
|
const configuration = target.configurations[configurationName];
|
||||||
|
processOptions(
|
||||||
|
tree,
|
||||||
|
configuration,
|
||||||
|
projectDetails.projectName,
|
||||||
|
projectDetails.root,
|
||||||
|
migrationLogs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(target.configurations).length === 0) {
|
||||||
|
if ('defaultConfiguration' in target) {
|
||||||
|
delete target.defaultConfiguration;
|
||||||
|
}
|
||||||
|
delete target.configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
'defaultConfiguration' in target &&
|
||||||
|
!target.configurations[target.defaultConfiguration]
|
||||||
|
) {
|
||||||
|
delete target.defaultConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.outputs) {
|
||||||
|
processTargetOutputs(target, [], inferredTargetConfiguration, {
|
||||||
|
projectName: projectDetails.projectName,
|
||||||
|
projectRoot: projectDetails.root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processBuildOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
): void {
|
||||||
|
const args: string[] = [];
|
||||||
|
if ('interactive' in options && options.interactive === false) {
|
||||||
|
args.push('--non-interactive');
|
||||||
|
delete options.interactive;
|
||||||
|
}
|
||||||
|
if ('wait' in options && options.wait === false) {
|
||||||
|
if (options.wait) args.push('--wait');
|
||||||
|
else args.push('--no-wait');
|
||||||
|
delete options.wait;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import { joinPathFragments, offsetFromRoot } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processExportOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args: string[] = [];
|
||||||
|
if ('minify' in options) {
|
||||||
|
if (options.minify === false) args.push('--no-minify');
|
||||||
|
delete options.minify;
|
||||||
|
}
|
||||||
|
if ('bytecode' in options) {
|
||||||
|
if (options.bytecode === false) args.push('--no-bytecode');
|
||||||
|
delete options.bytecode;
|
||||||
|
}
|
||||||
|
if ('outputDir' in options) {
|
||||||
|
const value = joinPathFragments(
|
||||||
|
offsetFromRoot(projectRoot),
|
||||||
|
options.outputDir
|
||||||
|
);
|
||||||
|
args.push(`--output-dir=${value}`);
|
||||||
|
delete options.outputDir;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { names } from '@nx/devkit';
|
||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
|
||||||
|
export function processGenericOptions(
|
||||||
|
_tree: Tree,
|
||||||
|
options: any,
|
||||||
|
_projectName: string,
|
||||||
|
_projectRoot: string,
|
||||||
|
_migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args = options.args ?? [];
|
||||||
|
for (const key of Object.keys(options)) {
|
||||||
|
if (key === 'args') continue;
|
||||||
|
let value = options[key];
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (value) args.push(`--${names(key).fileName}`);
|
||||||
|
} else {
|
||||||
|
args.push(`--${names(key).fileName}=${value}`);
|
||||||
|
}
|
||||||
|
delete options[key];
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processInstallOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
): void {
|
||||||
|
const args: string[] = [];
|
||||||
|
// Technically this will not be set in project.json since the value is passed through CLI e.g. nx run <project>:install foo,bar.
|
||||||
|
// Handling it here for correctness.
|
||||||
|
if ('packages' in options) {
|
||||||
|
const v = options.packages;
|
||||||
|
const packages = typeof v === 'string' ? v.split(',') : v;
|
||||||
|
args.push(...packages);
|
||||||
|
delete options.packages;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processPrebuildOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
): void {
|
||||||
|
const args: string[] = [];
|
||||||
|
if ('install' in options && options.install === false) {
|
||||||
|
args.push('--no-install');
|
||||||
|
delete options.install;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { names } from '@nx/devkit';
|
||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
|
||||||
|
export function processRunOptions(
|
||||||
|
_tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
_projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args: string[] = [];
|
||||||
|
|
||||||
|
for (const key of Object.keys(options)) {
|
||||||
|
const v = options[key];
|
||||||
|
if (key === 'xcodeConfiguration') {
|
||||||
|
args.push('--configuration', v);
|
||||||
|
} else if (typeof v === 'boolean') {
|
||||||
|
// no need to pass in the flag when it is true, pass the --no-<flag> when it is false. e.g. --no-build-cache, --no-bundler
|
||||||
|
if (v === false) {
|
||||||
|
args.push(`--no-${names(key).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (key === 'platform') {
|
||||||
|
// Platform isn't necessary to pass to the CLI since it is already part of the inferred command. e.g. run:ios, run:android
|
||||||
|
} else if (key === 'clean') {
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project: projectName,
|
||||||
|
executorName: '@nx/export:run',
|
||||||
|
log: 'Unable to migrate "clean" option. Use `nx run <project>:prebuild --clean` to regenerate native files.',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
args.push(`--${names(key).fileName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete options[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
options.args = args;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processServeOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args: string[] = [];
|
||||||
|
if ('dev' in options) {
|
||||||
|
if (options.dev === false) args.push('--no-dev');
|
||||||
|
delete options.dev;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processStartOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args: string[] = [];
|
||||||
|
if ('dev' in options) {
|
||||||
|
if (options.dev === false) args.push('--no-dev');
|
||||||
|
delete options.dev;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processGenericOptions } from './process-generic-options';
|
||||||
|
|
||||||
|
export function processSubmitOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: any,
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args: string[] = [];
|
||||||
|
if ('interactive' in options && options.interactive === false) {
|
||||||
|
args.push('--non-interactive');
|
||||||
|
delete options.interactive;
|
||||||
|
}
|
||||||
|
if ('wait' in options) {
|
||||||
|
if (options.wait) args.push('--wait');
|
||||||
|
else args.push('--no-wait');
|
||||||
|
delete options.wait;
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
processGenericOptions(tree, options, projectName, projectRoot, migrationLogs);
|
||||||
|
}
|
||||||
19
packages/expo/src/generators/convert-to-inferred/schema.json
Normal file
19
packages/expo/src/generators/convert-to-inferred/schema.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoConvertToInferred",
|
||||||
|
"description": "Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert Expo project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/expo:*` executors to use `@nx/expo/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -53,6 +53,11 @@
|
|||||||
"factory": "./src/generators/web-configuration/web-configuration#webConfigurationGenerator",
|
"factory": "./src/generators/web-configuration/web-configuration#webConfigurationGenerator",
|
||||||
"schema": "./src/generators/web-configuration/schema.json",
|
"schema": "./src/generators/web-configuration/schema.json",
|
||||||
"description": "Set up web configuration for a React Native app"
|
"description": "Set up web configuration for a React Native app"
|
||||||
|
},
|
||||||
|
"convert-to-inferred": {
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": "./src/generators/convert-to-inferred/schema.json",
|
||||||
|
"description": "Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,12 @@
|
|||||||
"version": "18.0.0-beta.0",
|
"version": "18.0.0-beta.0",
|
||||||
"description": "Add upgrade target to react native projects",
|
"description": "Add upgrade target to react native projects",
|
||||||
"implementation": "./src/migrations/update-18-0-0/add-upgrade-target"
|
"implementation": "./src/migrations/update-18-0-0/add-upgrade-target"
|
||||||
|
},
|
||||||
|
"update-19-6-0-rename-upgrade-target-name": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "19.6.0-beta.1",
|
||||||
|
"description": "Rename upgrade target name to fix casing.",
|
||||||
|
"implementation": "./src/migrations/update-19-6-0/rename-upgrade-target-name"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {
|
"packageJsonUpdates": {
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export interface ReactNativePluginOptions {
|
|||||||
buildAndroidTargetName?: string;
|
buildAndroidTargetName?: string;
|
||||||
bundleTargetName?: string;
|
bundleTargetName?: string;
|
||||||
syncDepsTargetName?: string;
|
syncDepsTargetName?: string;
|
||||||
upgradeTargetname?: string;
|
upgradeTargetName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachePath = join(workspaceDataDirectory, 'react-native.hash');
|
const cachePath = join(workspaceDataDirectory, 'react-native.hash');
|
||||||
@ -143,7 +143,7 @@ function buildReactNativeTargets(
|
|||||||
[options.syncDepsTargetName]: {
|
[options.syncDepsTargetName]: {
|
||||||
executor: '@nx/react-native:sync-deps',
|
executor: '@nx/react-native:sync-deps',
|
||||||
},
|
},
|
||||||
[options.upgradeTargetname]: {
|
[options.upgradeTargetName]: {
|
||||||
command: `react-native upgrade`,
|
command: `react-native upgrade`,
|
||||||
options: { cwd: projectRoot },
|
options: { cwd: projectRoot },
|
||||||
},
|
},
|
||||||
@ -194,6 +194,6 @@ function normalizeOptions(
|
|||||||
options.buildAndroidTargetName ??= 'build-android';
|
options.buildAndroidTargetName ??= 'build-android';
|
||||||
options.bundleTargetName ??= 'bundle';
|
options.bundleTargetName ??= 'bundle';
|
||||||
options.syncDepsTargetName ??= 'sync-deps';
|
options.syncDepsTargetName ??= 'sync-deps';
|
||||||
options.upgradeTargetname ??= 'upgrade';
|
options.upgradeTargetName ??= 'upgrade';
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,14 +173,14 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
'../my-dir/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.release': {
|
'ios.release': {
|
||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-dir/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
'../my-dir/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-dir/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -224,14 +224,14 @@ describe('app', () => {
|
|||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
'ios.release': {
|
'ios.release': {
|
||||||
binaryPath:
|
binaryPath:
|
||||||
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
'../my-app/ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
|
||||||
build:
|
build:
|
||||||
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
|
"cd ../my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15 Plus' -derivedDataPath ./build -quiet",
|
||||||
type: 'ios.app',
|
type: 'ios.app',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,327 @@
|
|||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
type ExpandedPluginConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
|
type ProjectConfiguration,
|
||||||
|
type ProjectGraph,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
type Tree,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { getRelativeProjectJsonSchemaPath } from 'nx/src/generators/utils/project-configuration';
|
||||||
|
import { convertToInferred } from './convert-to-inferred';
|
||||||
|
|
||||||
|
let fs: TempFs;
|
||||||
|
let projectGraph: ProjectGraph;
|
||||||
|
jest.mock('@nx/devkit', () => ({
|
||||||
|
...jest.requireActual('@nx/devkit'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve(projectGraph)),
|
||||||
|
updateProjectConfiguration: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((tree, projectName, projectConfiguration) => {
|
||||||
|
function handleEmptyTargets(
|
||||||
|
projectName: string,
|
||||||
|
projectConfiguration: ProjectConfiguration
|
||||||
|
): void {
|
||||||
|
if (
|
||||||
|
projectConfiguration.targets &&
|
||||||
|
!Object.keys(projectConfiguration.targets).length
|
||||||
|
) {
|
||||||
|
// Re-order `targets` to appear after the `// target` comment.
|
||||||
|
delete projectConfiguration.targets;
|
||||||
|
projectConfiguration[
|
||||||
|
'// targets'
|
||||||
|
] = `to see all targets run: nx show project ${projectName} --web`;
|
||||||
|
projectConfiguration.targets = {};
|
||||||
|
} else {
|
||||||
|
delete projectConfiguration['// targets'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectConfigFile = joinPathFragments(
|
||||||
|
projectConfiguration.root,
|
||||||
|
'project.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tree.exists(projectConfigFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleEmptyTargets(projectName, projectConfiguration);
|
||||||
|
writeJson(tree, projectConfigFile, {
|
||||||
|
name: projectConfiguration.name ?? projectName,
|
||||||
|
$schema: getRelativeProjectJsonSchemaPath(tree, projectConfiguration),
|
||||||
|
...projectConfiguration,
|
||||||
|
root: undefined,
|
||||||
|
});
|
||||||
|
projectGraph.nodes[projectName].data = projectConfiguration;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('nx/src/devkit-internals', () => ({
|
||||||
|
...jest.requireActual('nx/src/devkit-internals'),
|
||||||
|
getExecutorInformation: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((pkg, ...args) =>
|
||||||
|
jest
|
||||||
|
.requireActual('nx/src/devkit-internals')
|
||||||
|
.getExecutorInformation('@nx/webpack', ...args)
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
function addProject(tree: Tree, name: string, project: ProjectConfiguration) {
|
||||||
|
addProjectConfiguration(tree, name, project);
|
||||||
|
projectGraph.nodes[name] = {
|
||||||
|
name: name,
|
||||||
|
type: project.projectType === 'application' ? 'app' : 'lib',
|
||||||
|
data: {
|
||||||
|
projectType: project.projectType,
|
||||||
|
root: project.root,
|
||||||
|
targets: project.targets,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectOptions {
|
||||||
|
appName: string;
|
||||||
|
appRoot: string;
|
||||||
|
buildAndroidTargetName: string;
|
||||||
|
buildIosTargetName: string;
|
||||||
|
bundleAndroidTargetName: string;
|
||||||
|
bundleIosTargetName: string;
|
||||||
|
podInstallTargetName: string;
|
||||||
|
runAndroidTargetName: string;
|
||||||
|
runIosTargetName: string;
|
||||||
|
startTargetName: string;
|
||||||
|
syncDepsTargetName: string;
|
||||||
|
upgradeTargetName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultProjectOptions: ProjectOptions = {
|
||||||
|
appName: 'demo',
|
||||||
|
appRoot: 'apps/demo',
|
||||||
|
buildAndroidTargetName: 'build-android',
|
||||||
|
buildIosTargetName: 'build-ios',
|
||||||
|
bundleAndroidTargetName: 'bundle-android',
|
||||||
|
bundleIosTargetName: 'bundle-ios',
|
||||||
|
podInstallTargetName: 'pod-install',
|
||||||
|
runAndroidTargetName: 'run-android',
|
||||||
|
runIosTargetName: 'run-ios',
|
||||||
|
syncDepsTargetName: 'sync-deps',
|
||||||
|
startTargetName: 'start',
|
||||||
|
upgradeTargetName: 'upgrade',
|
||||||
|
};
|
||||||
|
|
||||||
|
const appConfig = { name: 'demo', displayName: 'Demo' };
|
||||||
|
|
||||||
|
function writeAppConfig(tree: Tree, projectRoot: string) {
|
||||||
|
tree.write(`${projectRoot}/app.json`, JSON.stringify(appConfig));
|
||||||
|
fs.createFileSync(`${projectRoot}/app.json`, JSON.stringify(appConfig));
|
||||||
|
jest.doMock(join(fs.tempDir, projectRoot, 'app.json'), () => appConfig, {
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProject(
|
||||||
|
tree: Tree,
|
||||||
|
options: Partial<ProjectOptions> = {},
|
||||||
|
additionalTargetOptions?: Record<string, Record<string, unknown>>
|
||||||
|
) {
|
||||||
|
let projectOptions = { ...defaultProjectOptions, ...options };
|
||||||
|
const project: ProjectConfiguration = {
|
||||||
|
name: projectOptions.appName,
|
||||||
|
root: projectOptions.appRoot,
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
[projectOptions.buildAndroidTargetName]: {
|
||||||
|
executor: '@nx/react-native:build-android',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.buildAndroidTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.buildIosTargetName]: {
|
||||||
|
executor: '@nx/react-native:build-ios',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.buildIosTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.bundleAndroidTargetName]: {
|
||||||
|
executor: '@nx/react-native:bundle',
|
||||||
|
options: {
|
||||||
|
platform: 'android',
|
||||||
|
...additionalTargetOptions?.[projectOptions.bundleAndroidTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.bundleIosTargetName]: {
|
||||||
|
executor: '@nx/react-native:bundle',
|
||||||
|
options: {
|
||||||
|
platform: 'ios',
|
||||||
|
...additionalTargetOptions?.[projectOptions.bundleIosTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.podInstallTargetName]: {
|
||||||
|
executor: '@nx/react-native:pod-install',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.podInstallTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.runAndroidTargetName]: {
|
||||||
|
executor: '@nx/react-native:run-android',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.runAndroidTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.runIosTargetName]: {
|
||||||
|
executor: '@nx/react-native:run-ios',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.runIosTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.startTargetName]: {
|
||||||
|
executor: '@nx/react-native:start',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.startTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.syncDepsTargetName]: {
|
||||||
|
executor: '@nx/react-native:sync-deps',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.syncDepsTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[projectOptions.upgradeTargetName]: {
|
||||||
|
executor: '@nx/react-native:upgrade',
|
||||||
|
options: {
|
||||||
|
...additionalTargetOptions?.[projectOptions.upgradeTargetName],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addProject(tree, project.name, project);
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectOptions.appRoot}/project.json`,
|
||||||
|
JSON.stringify(project)
|
||||||
|
);
|
||||||
|
|
||||||
|
// These file need to exist for inference, but they can be empty for the convert generator
|
||||||
|
fs.createFileSync(`${projectOptions.appRoot}/package.json`, '{}');
|
||||||
|
fs.createFileSync(`${projectOptions.appRoot}/metro.config.js`, '// empty');
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('convert-to-inferred', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs = new TempFs('expo');
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.root = fs.tempDir;
|
||||||
|
|
||||||
|
projectGraph = {
|
||||||
|
nodes: {},
|
||||||
|
dependencies: {},
|
||||||
|
externalNodes: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.cleanup();
|
||||||
|
jest.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert project to use inference plugin', async () => {
|
||||||
|
const project = createProject(tree);
|
||||||
|
writeAppConfig(tree, project.root);
|
||||||
|
|
||||||
|
const project2 = createProject(tree, {
|
||||||
|
appName: 'app2',
|
||||||
|
appRoot: 'apps/app2',
|
||||||
|
});
|
||||||
|
|
||||||
|
const project2Build = project2.targets['build-ios'];
|
||||||
|
|
||||||
|
await convertToInferred(tree, { project: project.name });
|
||||||
|
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const rnPlugin = nxJsonPlugins.find(
|
||||||
|
(plugin): plugin is ExpandedPluginConfiguration =>
|
||||||
|
typeof plugin !== 'string' &&
|
||||||
|
plugin.plugin === '@nx/react-native/plugin'
|
||||||
|
);
|
||||||
|
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project.name);
|
||||||
|
|
||||||
|
expect(rnPlugin).toBeDefined();
|
||||||
|
expect(projectConfig.targets).toEqual({
|
||||||
|
'bundle-android': {
|
||||||
|
executor: '@nx/react-native:bundle',
|
||||||
|
options: {
|
||||||
|
platform: 'android',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'bundle-ios': {
|
||||||
|
executor: '@nx/react-native:bundle',
|
||||||
|
options: {
|
||||||
|
platform: 'ios',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedProject2 = readProjectConfiguration(tree, project2.name);
|
||||||
|
expect(updatedProject2.targets['build-ios']).toEqual(project2Build);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should migrate options to CLI options and args', async () => {
|
||||||
|
const project = createProject(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
'build-android': {
|
||||||
|
mode: 'Release',
|
||||||
|
},
|
||||||
|
'build-ios': {
|
||||||
|
mode: 'Release',
|
||||||
|
},
|
||||||
|
'run-android': {
|
||||||
|
resetCache: true,
|
||||||
|
activeArchOnly: true,
|
||||||
|
},
|
||||||
|
'run-ios': {
|
||||||
|
resetCache: true,
|
||||||
|
buildFolder: './custom',
|
||||||
|
},
|
||||||
|
start: {
|
||||||
|
resetCache: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
writeAppConfig(tree, project.root);
|
||||||
|
|
||||||
|
await convertToInferred(tree, { project: project.name });
|
||||||
|
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project.name);
|
||||||
|
expect(projectConfig.targets['build-android'].options).toEqual({
|
||||||
|
args: ['--mode', 'Release'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['build-ios'].options).toEqual({
|
||||||
|
args: ['--mode', 'Release'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['run-android'].options).toEqual({
|
||||||
|
args: ['--active-arch-only'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['run-ios'].options).toEqual({
|
||||||
|
args: ['--buildFolder', './custom'],
|
||||||
|
});
|
||||||
|
expect(projectConfig.targets['start'].options).toEqual({
|
||||||
|
args: ['--reset-cache'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,138 @@
|
|||||||
|
import { createProjectGraphAsync, formatFiles, type Tree } from '@nx/devkit';
|
||||||
|
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { migrateProjectExecutorsToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
|
||||||
|
import { createNodes } from '../../../plugins/plugin';
|
||||||
|
import { postTargetTransformer } from './lib/post-target-transformer';
|
||||||
|
import { processStartOptions } from './lib/process-start-options';
|
||||||
|
import { createProcessOptions } from './lib/create-process-options';
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
project?: string;
|
||||||
|
skipFormat?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertToInferred(tree: Tree, options: Schema) {
|
||||||
|
const projectGraph = await createProjectGraphAsync();
|
||||||
|
const migrationLogs = new AggregatedLog();
|
||||||
|
const migratedProjects = await migrateProjectExecutorsToPluginV1(
|
||||||
|
tree,
|
||||||
|
projectGraph,
|
||||||
|
'@nx/react-native/plugin',
|
||||||
|
createNodes,
|
||||||
|
{
|
||||||
|
buildAndroidTargetName: 'build-android',
|
||||||
|
buildIosTargetName: 'build-ios',
|
||||||
|
bundleTargetName: 'bundle',
|
||||||
|
podInstallTargetName: 'pod-install',
|
||||||
|
runAndroidTargetName: 'run-android',
|
||||||
|
runIosTargetName: 'run-ios',
|
||||||
|
startTargetName: 'start',
|
||||||
|
syncDepsTargetName: 'sync-deps',
|
||||||
|
upgradeTargetName: 'upgrade',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:build-android'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
createProcessOptions(
|
||||||
|
'@nx/react-native:build-android',
|
||||||
|
['port', 'resetCache'],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
buildAndroidTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:build-ios'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
createProcessOptions(
|
||||||
|
'@nx/react-native:build-ios',
|
||||||
|
['port', 'resetCache'],
|
||||||
|
['buildFolder']
|
||||||
|
)
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
buildIosTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:run-android'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
createProcessOptions(
|
||||||
|
'@nx/react-native:run-android',
|
||||||
|
['port', 'resetCache'],
|
||||||
|
['appId', 'appIdSuffix', 'deviceId']
|
||||||
|
)
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
runAndroidTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:pod-install'],
|
||||||
|
postTargetTransformer: postTargetTransformer(migrationLogs),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
podInstallTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:run-ios'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
createProcessOptions(
|
||||||
|
'@nx/react-native:run-ios',
|
||||||
|
['port', 'resetCache'],
|
||||||
|
['buildFolder']
|
||||||
|
)
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
runIosTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:start'],
|
||||||
|
postTargetTransformer: postTargetTransformer(
|
||||||
|
migrationLogs,
|
||||||
|
processStartOptions
|
||||||
|
),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
startTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:sync-deps'],
|
||||||
|
postTargetTransformer: postTargetTransformer(migrationLogs),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
startTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executors: ['@nx/react-native:upgrade'],
|
||||||
|
postTargetTransformer: postTargetTransformer(migrationLogs),
|
||||||
|
targetPluginOptionMapper: (targetName) => ({
|
||||||
|
upgradeTargetName: targetName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options.project
|
||||||
|
);
|
||||||
|
|
||||||
|
if (migratedProjects.size === 0) {
|
||||||
|
throw new Error('Could not find any targets to migrate.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
migrationLogs.flushLogs();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default convertToInferred;
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { names } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logic copied from `packages/react-native/src/utils/get-cli-options.ts`,
|
||||||
|
* which was used by most executors to map their options to CLI options.
|
||||||
|
*/
|
||||||
|
export function createProcessOptions(
|
||||||
|
executorName: string,
|
||||||
|
optionKeysToIgnore: string[],
|
||||||
|
optionKeysInCamelName: string[]
|
||||||
|
) {
|
||||||
|
return (projectName: string, options: any, migrationLogs: AggregatedLog) => {
|
||||||
|
const args = [];
|
||||||
|
for (const optionKey of Object.keys(options)) {
|
||||||
|
const optionValue = options[optionKey];
|
||||||
|
delete options[optionKey];
|
||||||
|
|
||||||
|
if (optionKeysToIgnore.includes(optionKey)) {
|
||||||
|
migrationLogs.addLog({
|
||||||
|
project: projectName,
|
||||||
|
executorName,
|
||||||
|
log: `Unable to migrate '${optionKey}' to inferred target configuration.`,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cliKey = optionKeysInCamelName.includes(optionKey)
|
||||||
|
? names(optionKey).propertyName
|
||||||
|
: names(optionKey).fileName; // cli uses kebab case as default
|
||||||
|
|
||||||
|
if (Array.isArray(optionValue)) {
|
||||||
|
args.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
|
||||||
|
args.push(`--${cliKey}`);
|
||||||
|
} else {
|
||||||
|
args.push(`--${cliKey}`, optionValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import type { TargetConfiguration, Tree } from '@nx/devkit';
|
||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
import { processTargetOutputs } from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
|
||||||
|
|
||||||
|
export function postTargetTransformer(
|
||||||
|
migrationLogs: AggregatedLog,
|
||||||
|
processOptions?: (
|
||||||
|
projectName: string,
|
||||||
|
options: any,
|
||||||
|
migrationLogs: AggregatedLog
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
target: TargetConfiguration,
|
||||||
|
_tree: Tree,
|
||||||
|
projectDetails: { projectName: string; root: string },
|
||||||
|
inferredTargetConfiguration: TargetConfiguration
|
||||||
|
) => {
|
||||||
|
if (target.options && processOptions) {
|
||||||
|
processOptions(projectDetails.projectName, target.options, migrationLogs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.configurations && processOptions) {
|
||||||
|
for (const configurationName in target.configurations) {
|
||||||
|
const configuration = target.configurations[configurationName];
|
||||||
|
processOptions(
|
||||||
|
projectDetails.projectName,
|
||||||
|
configuration,
|
||||||
|
migrationLogs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(target.configurations).length === 0) {
|
||||||
|
if ('defaultConfiguration' in target) {
|
||||||
|
delete target.defaultConfiguration;
|
||||||
|
}
|
||||||
|
delete target.configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
'defaultConfiguration' in target &&
|
||||||
|
!target.configurations[target.defaultConfiguration]
|
||||||
|
) {
|
||||||
|
delete target.defaultConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.outputs) {
|
||||||
|
processTargetOutputs(target, [], inferredTargetConfiguration, {
|
||||||
|
projectName: projectDetails.projectName,
|
||||||
|
projectRoot: projectDetails.root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import type { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||||
|
|
||||||
|
export function processStartOptions(
|
||||||
|
_projectName: string,
|
||||||
|
options: any,
|
||||||
|
_migrationLogs: AggregatedLog
|
||||||
|
) {
|
||||||
|
const args: string[] = [];
|
||||||
|
for (const k of Object.keys(options)) {
|
||||||
|
if (k === 'resetCache') {
|
||||||
|
if (options[k] === true) {
|
||||||
|
args.push(`--reset-cache`);
|
||||||
|
}
|
||||||
|
} else if (k === 'interactive') {
|
||||||
|
if (options[k] === false) {
|
||||||
|
args.push(`--no-interactive`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.push(`--${k}`, options[k]);
|
||||||
|
}
|
||||||
|
delete options[k];
|
||||||
|
}
|
||||||
|
options.args = args;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxReactNativeConvertToInferred",
|
||||||
|
"description": "Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert React Native project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/react-native:*` executors to use `@nx/react-native/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,7 +46,7 @@ export async function reactNativeInitGeneratorInternal(
|
|||||||
createNodes,
|
createNodes,
|
||||||
{
|
{
|
||||||
startTargetName: ['start', 'react-native:start', 'react-native-start'],
|
startTargetName: ['start', 'react-native:start', 'react-native-start'],
|
||||||
upgradeTargetname: [
|
upgradeTargetName: [
|
||||||
'update',
|
'update',
|
||||||
'react-native:update',
|
'react-native:update',
|
||||||
'react-native-update',
|
'react-native-update',
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import update from './rename-upgrade-target-name';
|
||||||
|
|
||||||
|
describe('rename-upgrade-target-name', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fix upgrade target name option', async () => {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.plugins = [
|
||||||
|
{
|
||||||
|
plugin: '@nx/react-native/plugin',
|
||||||
|
options: {
|
||||||
|
upgradeTargetname: 'upgrade',
|
||||||
|
buildIosTargetName: 'build-ios',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
|
||||||
|
await update(tree);
|
||||||
|
|
||||||
|
const updatedNxJson = readNxJson(tree);
|
||||||
|
|
||||||
|
expect(updatedNxJson.plugins).toEqual([
|
||||||
|
{
|
||||||
|
plugin: '@nx/react-native/plugin',
|
||||||
|
options: {
|
||||||
|
upgradeTargetName: 'upgrade',
|
||||||
|
buildIosTargetName: 'build-ios',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { formatFiles, readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There was a typo in @nx/react-native/plugin, where "upgradeTargetName" was "upgradeTargetname"
|
||||||
|
*/
|
||||||
|
export default async function update(tree: Tree) {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
let updated = false;
|
||||||
|
for (const plugin of nxJson.plugins) {
|
||||||
|
if (typeof plugin === 'string') continue;
|
||||||
|
if (plugin.plugin !== '@nx/react-native/plugin') continue;
|
||||||
|
if (plugin.options['upgradeTargetname']) {
|
||||||
|
plugin.options['upgradeTargetName'] = plugin.options['upgradeTargetname'];
|
||||||
|
delete plugin.options['upgradeTargetname'];
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updated) updateNxJson(tree, nxJson);
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user