feat(react-native): upgrade react native to 0.73 (#20896)
This commit is contained in:
parent
d43d5365c8
commit
65e86eacac
@ -7531,6 +7531,14 @@
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "serve",
|
||||
"path": "/nx-api/expo/executors/serve",
|
||||
"name": "serve",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@ -9007,6 +9015,14 @@
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "upgrade",
|
||||
"path": "/nx-api/react-native/executors/upgrade",
|
||||
"name": "upgrade",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@ -9080,6 +9096,14 @@
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "web-configuration",
|
||||
"path": "/nx-api/react-native/generators/web-configuration",
|
||||
"name": "web-configuration",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
|
||||
@ -904,6 +904,15 @@
|
||||
"originalFilePath": "/packages/expo/src/executors/submit/schema.json",
|
||||
"path": "/nx-api/expo/executors/submit",
|
||||
"type": "executor"
|
||||
},
|
||||
"/nx-api/expo/executors/serve": {
|
||||
"description": "Serve up the Expo web app locally",
|
||||
"file": "generated/packages/expo/executors/serve.json",
|
||||
"hidden": false,
|
||||
"name": "serve",
|
||||
"originalFilePath": "/packages/expo/src/executors/serve/schema.json",
|
||||
"path": "/nx-api/expo/executors/serve",
|
||||
"type": "executor"
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
@ -2365,11 +2374,20 @@
|
||||
"originalFilePath": "/packages/react-native/src/executors/pod-install/schema.json",
|
||||
"path": "/nx-api/react-native/executors/pod-install",
|
||||
"type": "executor"
|
||||
},
|
||||
"/nx-api/react-native/executors/upgrade": {
|
||||
"description": "upgrade executor",
|
||||
"file": "generated/packages/react-native/executors/upgrade.json",
|
||||
"hidden": false,
|
||||
"name": "upgrade",
|
||||
"originalFilePath": "/packages/react-native/src/executors/upgrade/schema.json",
|
||||
"path": "/nx-api/react-native/executors/upgrade",
|
||||
"type": "executor"
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
"/nx-api/react-native/generators/init": {
|
||||
"description": "Initialize the `@nrwl/react-native` plugin.",
|
||||
"description": "Initialize the `@nx/react-native` plugin.",
|
||||
"file": "generated/packages/react-native/generators/init.json",
|
||||
"hidden": true,
|
||||
"name": "init",
|
||||
@ -2405,7 +2423,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/react-native/generators/storybook-configuration": {
|
||||
"description": "Set up Storybook for a React-native application or library.",
|
||||
"description": "Set up Storybook for a React Native application or library.",
|
||||
"file": "generated/packages/react-native/generators/storybook-configuration.json",
|
||||
"hidden": false,
|
||||
"name": "storybook-configuration",
|
||||
@ -2414,7 +2432,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/react-native/generators/component-story": {
|
||||
"description": "Generate Storybook story for a React-native component.",
|
||||
"description": "Generate Storybook story for a React Native component.",
|
||||
"file": "generated/packages/react-native/generators/component-story.json",
|
||||
"hidden": false,
|
||||
"name": "component-story",
|
||||
@ -2423,7 +2441,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/react-native/generators/stories": {
|
||||
"description": "Create stories/specs for all components declared in an application or library.",
|
||||
"description": "Create stories for all components declared in an application or library.",
|
||||
"file": "generated/packages/react-native/generators/stories.json",
|
||||
"hidden": false,
|
||||
"name": "stories",
|
||||
@ -2439,6 +2457,15 @@
|
||||
"originalFilePath": "/packages/react-native/src/generators/upgrade-native/schema.json",
|
||||
"path": "/nx-api/react-native/generators/upgrade-native",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/react-native/generators/web-configuration": {
|
||||
"description": "Set up web configuration for a React Native app",
|
||||
"file": "generated/packages/react-native/generators/web-configuration.json",
|
||||
"hidden": false,
|
||||
"name": "web-configuration",
|
||||
"originalFilePath": "/packages/react-native/src/generators/web-configuration/schema.json",
|
||||
"path": "/nx-api/react-native/generators/web-configuration",
|
||||
"type": "generator"
|
||||
}
|
||||
},
|
||||
"path": "/nx-api/react-native"
|
||||
|
||||
@ -890,6 +890,15 @@
|
||||
"originalFilePath": "/packages/expo/src/executors/submit/schema.json",
|
||||
"path": "expo/executors/submit",
|
||||
"type": "executor"
|
||||
},
|
||||
{
|
||||
"description": "Serve up the Expo web app locally",
|
||||
"file": "generated/packages/expo/executors/serve.json",
|
||||
"hidden": false,
|
||||
"name": "serve",
|
||||
"originalFilePath": "/packages/expo/src/executors/serve/schema.json",
|
||||
"path": "expo/executors/serve",
|
||||
"type": "executor"
|
||||
}
|
||||
],
|
||||
"generators": [
|
||||
@ -2339,11 +2348,20 @@
|
||||
"originalFilePath": "/packages/react-native/src/executors/pod-install/schema.json",
|
||||
"path": "react-native/executors/pod-install",
|
||||
"type": "executor"
|
||||
},
|
||||
{
|
||||
"description": "upgrade executor",
|
||||
"file": "generated/packages/react-native/executors/upgrade.json",
|
||||
"hidden": false,
|
||||
"name": "upgrade",
|
||||
"originalFilePath": "/packages/react-native/src/executors/upgrade/schema.json",
|
||||
"path": "react-native/executors/upgrade",
|
||||
"type": "executor"
|
||||
}
|
||||
],
|
||||
"generators": [
|
||||
{
|
||||
"description": "Initialize the `@nrwl/react-native` plugin.",
|
||||
"description": "Initialize the `@nx/react-native` plugin.",
|
||||
"file": "generated/packages/react-native/generators/init.json",
|
||||
"hidden": true,
|
||||
"name": "init",
|
||||
@ -2379,7 +2397,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Set up Storybook for a React-native application or library.",
|
||||
"description": "Set up Storybook for a React Native application or library.",
|
||||
"file": "generated/packages/react-native/generators/storybook-configuration.json",
|
||||
"hidden": false,
|
||||
"name": "storybook-configuration",
|
||||
@ -2388,7 +2406,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Generate Storybook story for a React-native component.",
|
||||
"description": "Generate Storybook story for a React Native component.",
|
||||
"file": "generated/packages/react-native/generators/component-story.json",
|
||||
"hidden": false,
|
||||
"name": "component-story",
|
||||
@ -2397,7 +2415,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Create stories/specs for all components declared in an application or library.",
|
||||
"description": "Create stories for all components declared in an application or library.",
|
||||
"file": "generated/packages/react-native/generators/stories.json",
|
||||
"hidden": false,
|
||||
"name": "stories",
|
||||
@ -2413,6 +2431,15 @@
|
||||
"originalFilePath": "/packages/react-native/src/generators/upgrade-native/schema.json",
|
||||
"path": "react-native/generators/upgrade-native",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Set up web configuration for a React Native app",
|
||||
"file": "generated/packages/react-native/generators/web-configuration.json",
|
||||
"hidden": false,
|
||||
"name": "web-configuration",
|
||||
"originalFilePath": "/packages/react-native/src/generators/web-configuration/schema.json",
|
||||
"path": "react-native/generators/web-configuration",
|
||||
"type": "generator"
|
||||
}
|
||||
],
|
||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||
|
||||
@ -14,40 +14,38 @@
|
||||
"platform": {
|
||||
"description": "Choose the platform to compile for",
|
||||
"enum": ["ios", "android", "all", "web"],
|
||||
"default": "all",
|
||||
"alias": "p",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"dev": {
|
||||
"type": "boolean",
|
||||
"description": "Bundle for development environments without minifying code or stripping the __DEV__ boolean. Configure static files for developing locally using a non-https server."
|
||||
"description": "Configure static files for developing locally using a non-https server"
|
||||
},
|
||||
"clear": {
|
||||
"type": "boolean",
|
||||
"description": "Clear the bundler cache before exporting"
|
||||
},
|
||||
"minify": { "type": "boolean", "description": "Minify source" },
|
||||
"outputDir": {
|
||||
"type": "string",
|
||||
"description": "The directory to export the static files to. Default: dist"
|
||||
"description": "Relative to workspace root, the directory to export the static files to. Default: dist"
|
||||
},
|
||||
"maxWorkers": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of tasks to allow Metro to spawn"
|
||||
"description": "When bundler is metro, the maximum number of tasks to allow the bundler to spawn"
|
||||
},
|
||||
"dumpAssetmap": {
|
||||
"type": "boolean",
|
||||
"description": "Dump the asset map for further processing"
|
||||
"description": "When bundler is metro, whether to dump the asset map for further processing"
|
||||
},
|
||||
"dumpSourcemap": {
|
||||
"sourceMaps": {
|
||||
"type": "boolean",
|
||||
"description": "Dump the source map for debugging the JS bundle"
|
||||
},
|
||||
"bundler": {
|
||||
"enum": ["metro", "webpack"],
|
||||
"description": "Choose the bundler to compile for",
|
||||
"default": "metro"
|
||||
"description": "When bundler is metro, whether to emit JavaScript source maps"
|
||||
}
|
||||
},
|
||||
"required": [],
|
||||
"required": ["platform"],
|
||||
"examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"export\": {\n \"executor\": \"@nx/expo:export\",\n \"options\": {\n \"outputs\": [\"{options.outputDir}\"],\n \"platform\": \"all\",\n \"outputDir\": \"dist/apps/mobile\"\n },\n \"dependsOn\": [\"sync-deps\"]\n }\n //...\n }\n}\n```\n\n```shell\nnx run mobile:export\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Specify outputDir\" %}\nThe `outputDir` option allows you to specify the output directory of your bundle:\n\n```json\n \"export\": {\n \"executor\": \"@nx/expo:export\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"platform\": \"all\",\n \"bundler\": \"metro\",\n \"outputDir\": \"dist/apps/mobile\"\n },\n \"dependsOn\": [\"sync-deps\"]\n },\n```\n\nor run command: `nx run mobile:export --outputDir=dist/apps/mobile`.\n\n{% /tab %}\n{% tab label=\"Specify the platform\" %}\nThe `platform` option allows you to specify the platform to compile with metro bundler: \"ios\", \"android\", \"all\", and \"web\".\n\nFor example, to bundle for web:\n\n```json\n \"export\": {\n \"executor\": \"@nx/expo:export\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"platform\": \"web\",\n \"bundler\": \"metro\",\n \"outputDir\": \"dist/apps/dogs\"\n },\n \"dependsOn\": [\"sync-deps\"]\n },\n```\n\nor run command `nx export mobile --platform=web`.\n\n{% /tab %}\n{% tab label=\"Bundle for development\" %}\n\nThe `dev` option allows you to bundle for development environments.\n\n```json\n \"export\": {\n \"executor\": \"@nx/expo:export\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"platform\": \"web\",\n \"bundler\": \"metro\",\n \"outputDir\": \"dist/apps/dogs\",\n \"dev\": true\n },\n \"dependsOn\": [\"sync-deps\"]\n },\n```\n\nor run command `nx export mobile --dev`.\n\n{% /tab %}\n{% tab label=\"Clear bundle cache\" %}\n\nThe `clear` option allows you to clear bundle cache.\n\n```json\n \"export\": {\n \"executor\": \"@nx/expo:export\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"platform\": \"web\",\n \"bundler\": \"metro\",\n \"outputDir\": \"dist/apps/dogs\",\n \"clear\": true\n },\n \"dependsOn\": [\"sync-deps\"]\n },\n```\n\nor run command `nx export mobile --clear`.\n\n{% /tab %}\n{% /tabs %}\n",
|
||||
"presets": []
|
||||
},
|
||||
"description": "Export the JavaScript and assets for your app using Metro/webpack bundler",
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
"description": "Project template to clone from. File path pointing to a local tar file or a github repo"
|
||||
}
|
||||
},
|
||||
"required": ["platform"],
|
||||
"examplesFile": "The `prebuild` command generates native code before a native app can compile.\n\n`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"prebuild\": {\n \"executor\": \"@nx/expo:prebuild\",\n \"options\": {}\n }\n //...\n }\n}\n```\n\n```shell\nnx run mobile:prebuild\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Generate Native Code for Different Platforms\" %}\nThe `platform` option allows you to specify the platform to generate native code for (e.g. android, ios, all).\n\n```json\n \"prebuild\": {\n \"executor\": \"@nx/expo:prebuild\",\n \"options\": {\n \"platform\": \"android\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Regenerate Native Code\" %}\n\nThe `clean` option allows you to delete the native folders and regenerate them before apply changes.\n\n```json\n \"prebuild\": {\n \"executor\": \"@nx/expo:prebuild\",\n \"options\": {\n \"clean\": true\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Install NPM Packages and CocoaPods\" %}\n\nThe `install` option allows you to install NPM Packages and CocoaPods.\n\n```json\n \"prebuild\": {\n \"executor\": \"@nx/expo:prebuild\",\n \"options\": {\n \"install\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n",
|
||||
"presets": []
|
||||
},
|
||||
|
||||
45
docs/generated/packages/expo/executors/serve.json
Normal file
45
docs/generated/packages/expo/executors/serve.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "serve",
|
||||
"implementation": "/packages/expo/src/executors/serve/serve.impl.ts",
|
||||
"schema": {
|
||||
"version": 2,
|
||||
"outputCapture": "direct-nodejs",
|
||||
"cli": "nx",
|
||||
"$id": "NxExpoServe",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"title": "Serve web app for Expo",
|
||||
"description": "Packager Server target options.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to start the native Metro bundler on (does not apply to web or tunnel)",
|
||||
"default": 19000,
|
||||
"alias": "p"
|
||||
},
|
||||
"clear": {
|
||||
"type": "boolean",
|
||||
"description": "Clear the Metro bundler cache",
|
||||
"alias": "c"
|
||||
},
|
||||
"maxWorkers": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of tasks to allow Metro to spawn"
|
||||
},
|
||||
"dev": {
|
||||
"type": "boolean",
|
||||
"description": "Turn development mode on or off"
|
||||
},
|
||||
"minify": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not to minify code"
|
||||
}
|
||||
},
|
||||
"presets": []
|
||||
},
|
||||
"description": "Serve up the Expo web app locally",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/expo/src/executors/serve/schema.json",
|
||||
"type": "executor"
|
||||
}
|
||||
@ -86,7 +86,7 @@
|
||||
"description": "Allows this command to run while offline"
|
||||
}
|
||||
},
|
||||
"examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081\n }\n }\n //...\n }\n}\n```\n\n```shell\nnx run mobile:start\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Specify starting on platform\" %}\nThe `ios`, `android` and `web` option allows you to start the server on different platforms.\n\nOpens your app in Expo Go in a currently running iOS simulator on your computer:\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"ios\": true\n }\n }\n```\n\nOpens your app in Expo Go on a connected Android device\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"android\": true\n }\n }\n```\n\nOpens your app in a web browser:\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"web\": true\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Specify the host\" %}\nThe `host` option allows you to specify the type of host to use. `lan` uses the local network; `tunnel` ues any network by tunnel through ngrok; `localhost` connects to the dev server over localhost.\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"host\": \"localhost\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Starts the server with cache reset\" %}\n\nThe `clear` option allows you to remove Metro bundler cache.\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"clear\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n",
|
||||
"examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081\n }\n }\n //...\n }\n}\n```\n\n```shell\nnx run mobile:start\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Specify starting on platform\" %}\nThe `ios`, `android` and `web` option allows you to start the server on different platforms.\n\nOpens your app in Expo Go in a currently running iOS simulator on your computer:\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"ios\": true\n }\n }\n```\n\nor run command `nx start <your app name> --ios`.\n\nOpens your app in Expo Go on a connected Android device\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"android\": true\n }\n }\n```\n\nor run command `nx start <your app name> --android`.\n\nOpens your app in a web browser:\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"web\": true\n }\n }\n```\n\nor run command `nx start <your app name> --web`.\n\n{% /tab %}\n{% tab label=\"Specify the host\" %}\nThe `host` option allows you to specify the type of host to use. `lan` uses the local network; `tunnel` ues any network by tunnel through ngrok; `localhost` connects to the dev server over localhost.\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"host\": \"localhost\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Starts the server with cache reset\" %}\n\nThe `clear` option allows you to remove Metro bundler cache.\n\n```json\n \"start\": {\n \"executor\": \"@nx/expo:start\",\n \"options\": {\n \"port\": 8081,\n \"clear\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n",
|
||||
"presets": []
|
||||
},
|
||||
"description": "Start a local dev server for the app or start a Webpack dev server for the web app",
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
@ -75,8 +75,8 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "Adds the specified e2e test runner",
|
||||
"type": "string",
|
||||
"enum": ["detox", "none"],
|
||||
"default": "detox"
|
||||
"enum": ["cypress", "playwright", "detox", "none"],
|
||||
"default": "cypress"
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
|
||||
21
docs/generated/packages/react-native/executors/upgrade.json
Normal file
21
docs/generated/packages/react-native/executors/upgrade.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "upgrade",
|
||||
"implementation": "/packages/react-native/src/executors/upgrade/upgrade.impl.ts",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"version": 2,
|
||||
"cli": "nx",
|
||||
"$id": "NxReactNativeUpgrade",
|
||||
"title": "React Native Upgrade Executor",
|
||||
"description": "Upgrade React Native code for project.",
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": [],
|
||||
"presets": []
|
||||
},
|
||||
"description": "upgrade executor",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/react-native/src/executors/upgrade/schema.json",
|
||||
"type": "executor"
|
||||
}
|
||||
@ -48,7 +48,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
@ -75,20 +75,28 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "Adds the specified e2e test runner.",
|
||||
"type": "string",
|
||||
"enum": ["detox", "none"],
|
||||
"default": "detox"
|
||||
"enum": ["cypress", "playwright", "detox", "none"],
|
||||
"default": "cypress"
|
||||
},
|
||||
"install": {
|
||||
"type": "boolean",
|
||||
"description": "Runs `pod install` for native modules before building iOS app.",
|
||||
"default": true,
|
||||
"x-priority": "internal"
|
||||
"x-prompt": "Run 'pod install' for native modules?",
|
||||
"default": true
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"description": "Do not add dependencies to `package.json`.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "webpack",
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
"required": [],
|
||||
|
||||
@ -15,28 +15,36 @@
|
||||
"description": "The project where to add the components.",
|
||||
"examples": ["shared-ui-component"],
|
||||
"$default": { "$source": "projectName", "index": 0 },
|
||||
"x-prompt": "What's the name of the project where the component lives?"
|
||||
"x-prompt": "What's name of the project where the component lives?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentPath": {
|
||||
"type": "string",
|
||||
"description": "Relative path to the component file from the library root.",
|
||||
"examples": ["lib/components"],
|
||||
"x-prompt": "What's path of the component relative to the project's lib root?"
|
||||
"x-prompt": "What's path of the component relative to the project's lib root?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
"required": ["project", "componentPath"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Generate Storybook story for a React-native component.",
|
||||
"hidden": false,
|
||||
"description": "Generate Storybook story for a React Native component.",
|
||||
"implementation": "/packages/react-native/src/generators/component-story/component-story#componentStoryGenerator.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/react-native/src/generators/component-story/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
"required": [],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Initialize the `@nrwl/react-native` plugin.",
|
||||
"description": "Initialize the `@nx/react-native` plugin.",
|
||||
"hidden": true,
|
||||
"implementation": "/packages/react-native/src/generators/init/init#reactNativeInitGenerator.ts",
|
||||
"aliases": [],
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
|
||||
@ -14,7 +14,20 @@
|
||||
"aliases": ["name", "projectName"],
|
||||
"description": "Project for which to generate stories.",
|
||||
"$default": { "$source": "projectName", "index": 0 },
|
||||
"x-prompt": "For which project do you want to generate stories?"
|
||||
"x-prompt": "For which project do you want to generate stories?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"x-prompt": "Do you want to set up Storybook interaction tests?",
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript files rather than TypeScript files.",
|
||||
"default": false
|
||||
},
|
||||
"ignorePaths": {
|
||||
"type": "array",
|
||||
@ -41,10 +54,10 @@
|
||||
"required": ["project"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Create stories/specs for all components declared in an application or library.",
|
||||
"hidden": false,
|
||||
"description": "Create stories for all components declared in an application or library.",
|
||||
"implementation": "/packages/react-native/src/generators/stories/stories#storiesGenerator.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/react-native/src/generators/stories/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
|
||||
@ -9,21 +9,37 @@
|
||||
"description": "Set up Storybook for a React-Native app or library.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"aliases": ["project", "projectName"],
|
||||
"aliases": ["name", "projectName"],
|
||||
"description": "Project for which to generate Storybook configuration.",
|
||||
"$default": { "$source": "argv", "index": 0 },
|
||||
"x-prompt": "For which project do you want to generate Storybook configuration?",
|
||||
"x-dropdown": "projects"
|
||||
"x-dropdown": "projects",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"x-prompt": "Do you want to set up Storybook interaction tests?",
|
||||
"x-priority": "important",
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate *.stories.ts files for components declared in this project?",
|
||||
"description": "Automatically generate `*.stories.ts` files for components declared in this project?",
|
||||
"x-prompt": "Automatically generate *.stories.ts files for components declared in this project?",
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
"x-prompt": "Configure a static file server for the storybook instance?",
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript story files rather than TypeScript story files.",
|
||||
@ -40,12 +56,6 @@
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"x-deprecated": "Nx only supports standaloneConfig"
|
||||
},
|
||||
"ignorePaths": {
|
||||
"type": "array",
|
||||
"description": "Paths to ignore when looking for components.",
|
||||
@ -62,13 +72,13 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"required": ["project"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Set up Storybook for a React-native application or library.",
|
||||
"hidden": false,
|
||||
"description": "Set up Storybook for a React Native application or library.",
|
||||
"implementation": "/packages/react-native/src/generators/storybook-configuration/configuration#storybookConfigurationGenerator.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/react-native/src/generators/storybook-configuration/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "Adds the specified e2e test runner.",
|
||||
"type": "string",
|
||||
"enum": ["detox", "none"],
|
||||
"default": "detox"
|
||||
"enum": ["cypress", "playwright", "detox", "none"],
|
||||
"default": "cypress"
|
||||
},
|
||||
"install": {
|
||||
"type": "boolean",
|
||||
@ -41,9 +41,9 @@
|
||||
"presets": []
|
||||
},
|
||||
"description": "Destructive command to upgrade native iOS and Android code to latest.",
|
||||
"hidden": false,
|
||||
"implementation": "/packages/react-native/src/generators/upgrade-native/upgrade-native#reactNativeUpgradeNativeGenerator.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/react-native/src/generators/upgrade-native/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "web-configuration",
|
||||
"factory": "./src/generators/web-configuration/web-configuration#webConfigurationGenerator",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxReactNativeWebConfiguration",
|
||||
"description": "Setup web configuration to React Native apps using react-native-web.",
|
||||
"title": "Nx React Native Web configuration",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"aliases": ["name", "projectName"],
|
||||
"description": "Project for which to generate web configuration.",
|
||||
"$default": { "$source": "argv", "index": 0 },
|
||||
"x-prompt": "For which project do you want to generate web configuration?",
|
||||
"x-dropdown": "projects",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"description": "Do not add dependencies to `package.json`.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "webpack",
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
"required": ["project", "bundler"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Set up web configuration for a React Native app",
|
||||
"implementation": "/packages/react-native/src/generators/web-configuration/web-configuration#webConfigurationGenerator.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/react-native/src/generators/web-configuration/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
@ -63,13 +63,6 @@
|
||||
"description": "Add a static-storybook to serve the static storybook built files.",
|
||||
"default": false
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The Storybook builder to use.",
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"default": "webpack",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"uiFramework": {
|
||||
"type": "string",
|
||||
"description": "Storybook UI Framework to use.",
|
||||
|
||||
@ -433,6 +433,7 @@
|
||||
- [install](/nx-api/expo/executors/install)
|
||||
- [export](/nx-api/expo/executors/export)
|
||||
- [submit](/nx-api/expo/executors/submit)
|
||||
- [serve](/nx-api/expo/executors/serve)
|
||||
- [generators](/nx-api/expo/generators)
|
||||
- [init](/nx-api/expo/generators/init)
|
||||
- [application](/nx-api/expo/generators/application)
|
||||
@ -612,6 +613,7 @@
|
||||
- [ensure-symlink](/nx-api/react-native/executors/ensure-symlink)
|
||||
- [storybook](/nx-api/react-native/executors/storybook)
|
||||
- [pod-install](/nx-api/react-native/executors/pod-install)
|
||||
- [upgrade](/nx-api/react-native/executors/upgrade)
|
||||
- [generators](/nx-api/react-native/generators)
|
||||
- [init](/nx-api/react-native/generators/init)
|
||||
- [application](/nx-api/react-native/generators/application)
|
||||
@ -621,6 +623,7 @@
|
||||
- [component-story](/nx-api/react-native/generators/component-story)
|
||||
- [stories](/nx-api/react-native/generators/stories)
|
||||
- [upgrade-native](/nx-api/react-native/generators/upgrade-native)
|
||||
- [web-configuration](/nx-api/react-native/generators/web-configuration)
|
||||
- [remix](/nx-api/remix)
|
||||
- [documents](/nx-api/remix/documents)
|
||||
- [Overview](/nx-api/remix/documents/overview)
|
||||
|
||||
@ -10,15 +10,16 @@ import {
|
||||
checkFilesExist,
|
||||
updateFile,
|
||||
runCLIAsync,
|
||||
runE2ETests,
|
||||
killPorts,
|
||||
} from 'e2e/utils';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('@nx/expo/plugin', () => {
|
||||
let project: string;
|
||||
let appName: string;
|
||||
|
||||
beforeAll(() => {
|
||||
project = newProject();
|
||||
newProject();
|
||||
appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/expo:app ${appName} --project-name-and-root-format=as-provided --no-interactive`,
|
||||
@ -107,6 +108,32 @@ describe('@nx/expo/plugin', () => {
|
||||
const prebuildResult = await runCLIAsync(
|
||||
`prebuild ${appName} --no-interactive --install=false`
|
||||
);
|
||||
expect(prebuildResult.combinedOutput).toContain('Config synced');
|
||||
expect(prebuildResult.combinedOutput).toContain(
|
||||
'Successfully ran target prebuild for project'
|
||||
);
|
||||
});
|
||||
|
||||
it('should run e2e for cypress', async () => {
|
||||
if (runE2ETests()) {
|
||||
const results = runCLI(`e2e ${appName}-e2e`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
await killPorts(4200);
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should create storybook with application', async () => {
|
||||
runCLI(
|
||||
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
);
|
||||
checkFilesExist(
|
||||
`${appName}/.storybook/main.ts`,
|
||||
`${appName}/src/app/App.stories.tsx`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -11,10 +11,12 @@ import {
|
||||
runCLIAsync,
|
||||
runCommand,
|
||||
runCommandUntil,
|
||||
runE2ETests,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
} from '@nx/e2e/utils';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('expo', () => {
|
||||
@ -34,7 +36,9 @@ describe('expo', () => {
|
||||
nxJson.targetDefaults.build.inputs = ['production', '^production'];
|
||||
return nxJson;
|
||||
});
|
||||
runCLI(`generate @nx/expo:application ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/expo:application ${appName} --e2eTestRunner=cypress --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/expo:library ${libName} --buildable --publishable --importPath=${proj}/${libName}`
|
||||
);
|
||||
@ -63,22 +67,46 @@ describe('expo', () => {
|
||||
expect(libLintResults.combinedOutput).toContain('All files pass linting.');
|
||||
});
|
||||
|
||||
it('should serve with metro', async () => {
|
||||
let process: ChildProcess;
|
||||
const port = 8081;
|
||||
|
||||
try {
|
||||
process = await runCommandUntil(
|
||||
`serve ${appName} --interactive=false --port=${port}`,
|
||||
(output) => {
|
||||
return (
|
||||
output.includes(`http://localhost::${port}`) ||
|
||||
output.includes('Starting JS server...')
|
||||
);
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
if (process && process.pid) {
|
||||
await promisifiedTreeKill(process.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
}
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should export', async () => {
|
||||
const exportResults = await runCLIAsync(
|
||||
`export ${appName} --no-interactive`
|
||||
);
|
||||
expect(exportResults.combinedOutput).toContain(
|
||||
'Export was successful. Your exported files can be found'
|
||||
'Successfully ran target export for project'
|
||||
);
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/index.html`,
|
||||
`dist/apps/${appName}/metadata.json`
|
||||
);
|
||||
checkFilesExist(`dist/apps/${appName}/metadata.json`);
|
||||
});
|
||||
|
||||
it('should export-web', async () => {
|
||||
expect(() => {
|
||||
runCLI(`export-web ${appName}`);
|
||||
checkFilesExist(`apps/${appName}/dist/index.html`);
|
||||
checkFilesExist(`apps/${appName}/dist/metadata.json`);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should prebuild', async () => {
|
||||
@ -100,10 +128,14 @@ describe('expo', () => {
|
||||
const prebuildResult = await runCLIAsync(
|
||||
`prebuild ${appName} --no-interactive --install=false`
|
||||
);
|
||||
expect(prebuildResult.combinedOutput).toContain('Config synced');
|
||||
expect(prebuildResult.combinedOutput).toContain(
|
||||
'Successfully ran target prebuild for project'
|
||||
);
|
||||
});
|
||||
|
||||
it('should install', async () => {
|
||||
// TODO (@xiongemi): this test is disabled due to expo requires typescript ^5.3.0
|
||||
// re-enable it when typescript is updated
|
||||
xit('should install', async () => {
|
||||
// run install command
|
||||
const installResults = await runCLIAsync(
|
||||
`install ${appName} --no-interactive`
|
||||
@ -189,4 +221,60 @@ describe('expo', () => {
|
||||
`Successfully ran target test for project ${libName}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should create storybook with application', async () => {
|
||||
runCLI(
|
||||
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
);
|
||||
checkFilesExist(
|
||||
`apps/${appName}/.storybook/main.ts`,
|
||||
`apps/${appName}/src/app/App.stories.tsx`
|
||||
);
|
||||
});
|
||||
|
||||
it('should run e2e for cypress', async () => {
|
||||
if (runE2ETests()) {
|
||||
const results = runCLI(`e2e ${appName}-e2e`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
await killPorts(4200);
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should run e2e for cypress with configuration ci', async () => {
|
||||
if (runE2ETests()) {
|
||||
const results = runCLI(`e2e ${appName}-e2e --configuration=ci`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
await killPorts(4200);
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should run e2e for playwright', async () => {
|
||||
const appName2 = uniq('my-app');
|
||||
runCLI(
|
||||
`generate @nx/expo:application ${appName2} --e2eTestRunner=playwright --no-interactive`
|
||||
);
|
||||
if (runE2ETests()) {
|
||||
const results = runCLI(`e2e ${appName2}-e2e`, { verbose: true });
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
await killPorts(4200);
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -8,6 +8,8 @@ import {
|
||||
runCommandUntil,
|
||||
killProcessAndPorts,
|
||||
fileExists,
|
||||
checkFilesExist,
|
||||
runE2ETests,
|
||||
} from 'e2e/utils';
|
||||
|
||||
describe('@nx/react-native/plugin', () => {
|
||||
@ -70,4 +72,49 @@ describe('@nx/react-native/plugin', () => {
|
||||
await killProcessAndPorts(process.pid, port);
|
||||
}
|
||||
});
|
||||
|
||||
it('should serve', async () => {
|
||||
let process: ChildProcess;
|
||||
const port = 8081;
|
||||
|
||||
try {
|
||||
process = await runCommandUntil(
|
||||
`serve ${appName} --interactive=false --port=${port}`,
|
||||
(output) => {
|
||||
return output.includes(`http://localhost:${port}`);
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
if (process && process.pid) {
|
||||
await killProcessAndPorts(process.pid, port);
|
||||
}
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should run e2e for cypress', async () => {
|
||||
if (runE2ETests()) {
|
||||
let results = runCLI(`e2e ${appName}-e2e`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
|
||||
results = runCLI(`e2e ${appName}-e2e --configuration=ci`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
}
|
||||
});
|
||||
|
||||
it('should create storybook with application', async () => {
|
||||
runCLI(
|
||||
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
);
|
||||
checkFilesExist(
|
||||
`${appName}/.storybook/main.ts`,
|
||||
`${appName}/src/app/App.stories.tsx`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,14 +4,14 @@ import {
|
||||
expectTestsPass,
|
||||
getPackageManagerCommand,
|
||||
isOSX,
|
||||
killPorts,
|
||||
killProcessAndPorts,
|
||||
newProject,
|
||||
promisifiedTreeKill,
|
||||
readJson,
|
||||
runCLI,
|
||||
runCLIAsync,
|
||||
runCommand,
|
||||
runCommandUntil,
|
||||
runE2ETests,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
@ -37,7 +37,7 @@ describe('react native', () => {
|
||||
return nxJson;
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react-native:application ${appName} --install=false --no-interactive`
|
||||
`generate @nx/react-native:application ${appName} --bunlder=webpack --e2eTestRunner=cypress --install=false --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react-native:library ${libName} --buildable --publishable --importPath=${proj}/${libName} --no-interactive`
|
||||
@ -45,6 +45,11 @@ describe('react native', () => {
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
it('should build for web', async () => {
|
||||
const results = runCLI(`build ${appName}`);
|
||||
expect(results).toContain('Successfully ran target build');
|
||||
});
|
||||
|
||||
it('should test and lint', async () => {
|
||||
const componentName = uniq('Component');
|
||||
runCLI(
|
||||
@ -66,6 +71,16 @@ describe('react native', () => {
|
||||
expect(libLintResults.combinedOutput).toContain('All files pass linting.');
|
||||
});
|
||||
|
||||
it('should run e2e for cypress', async () => {
|
||||
if (runE2ETests()) {
|
||||
let results = runCLI(`e2e ${appName}-e2e`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
|
||||
results = runCLI(`e2e ${appName}-e2e --configuration=ci`);
|
||||
expect(results).toContain('Successfully ran target e2e');
|
||||
}
|
||||
});
|
||||
|
||||
it('should bundle-ios', async () => {
|
||||
const iosBundleResult = await runCLIAsync(
|
||||
`bundle-ios ${appName} --sourcemapOutput=../../dist/apps/${appName}/ios/main.map`
|
||||
@ -114,8 +129,32 @@ describe('react native', () => {
|
||||
// port and process cleanup
|
||||
try {
|
||||
if (process && process.pid) {
|
||||
await promisifiedTreeKill(process.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
await killProcessAndPorts(process.pid, port);
|
||||
}
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should serve', async () => {
|
||||
let process: ChildProcess;
|
||||
const port = 8081;
|
||||
|
||||
try {
|
||||
process = await runCommandUntil(
|
||||
`serve ${appName} --interactive=false --port=${port}`,
|
||||
(output) => {
|
||||
return output.includes(`http://localhost:${port}`);
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// port and process cleanup
|
||||
try {
|
||||
if (process && process.pid) {
|
||||
await killProcessAndPorts(process.pid, port);
|
||||
}
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
@ -136,33 +175,14 @@ describe('react native', () => {
|
||||
runCLI(
|
||||
`generate @nx/react-native:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
);
|
||||
expect(() =>
|
||||
checkFilesExist(
|
||||
`.storybook/story-loader.ts`,
|
||||
`apps/${appName}/src/storybook/storybook.ts`,
|
||||
`apps/${appName}/src/storybook/toggle-storybook.tsx`,
|
||||
`apps/${appName}/src/app/App.stories.tsx`
|
||||
)
|
||||
).not.toThrow();
|
||||
|
||||
await runCLIAsync(`storybook ${appName}`);
|
||||
const result = readJson(join('apps', appName, 'package.json'));
|
||||
expect(result).toMatchObject({
|
||||
dependencies: {
|
||||
'@storybook/addon-ondevice-actions': '*',
|
||||
'@storybook/addon-ondevice-backgrounds': '*',
|
||||
'@storybook/addon-ondevice-controls': '*',
|
||||
'@storybook/addon-ondevice-notes': '*',
|
||||
},
|
||||
});
|
||||
checkFilesExist(
|
||||
`apps/${appName}/.storybook/main.ts`,
|
||||
`apps/${appName}/src/app/App.stories.tsx`
|
||||
);
|
||||
});
|
||||
|
||||
it('should upgrade native for application', async () => {
|
||||
expect(() =>
|
||||
runCLI(
|
||||
`generate @nx/react-native:upgrade-native ${appName} --install=false`
|
||||
)
|
||||
).not.toThrow();
|
||||
expect(() => runCLI(`upgrade ${appName}`)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should build publishable library', async () => {
|
||||
@ -180,13 +200,11 @@ describe('react native', () => {
|
||||
|
||||
it('sync npm dependencies for autolink', async () => {
|
||||
// Add npm package with native modules
|
||||
updateFile(join('package.json'), (content) => {
|
||||
const json = JSON.parse(content);
|
||||
json.dependencies['react-native-image-picker'] = '5.3.1';
|
||||
json.dependencies['@react-native-async-storage/async-storage'] = '1.18.1';
|
||||
return JSON.stringify(json, null, 2);
|
||||
});
|
||||
runCommand(`${getPackageManagerCommand().install}`);
|
||||
runCommand(
|
||||
`${
|
||||
getPackageManagerCommand().addDev
|
||||
} react-native-image-picker @react-native-async-storage/async-storage`
|
||||
);
|
||||
|
||||
// Add import for Nx to pick up
|
||||
updateFile(join('apps', appName, 'src/app/App.tsx'), (content) => {
|
||||
@ -202,6 +220,8 @@ describe('react native', () => {
|
||||
dependencies: {
|
||||
'react-native-image-picker': '*',
|
||||
'react-native': '*',
|
||||
},
|
||||
devDependencies: {
|
||||
'@react-native-async-storage/async-storage': '*',
|
||||
},
|
||||
});
|
||||
@ -261,4 +281,25 @@ describe('react native', () => {
|
||||
`Successfully ran target test for project ${libName}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should run build with vite bundler and e2e with playwright', async () => {
|
||||
const appName2 = uniq('my-app');
|
||||
runCLI(
|
||||
`generate @nx/react-native:application ${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive`
|
||||
);
|
||||
const buildResults = runCLI(`build ${appName2}`);
|
||||
expect(buildResults).toContain('Successfully ran target build');
|
||||
if (runE2ETests()) {
|
||||
const e2eResults = runCLI(`e2e ${appName2}-e2e`);
|
||||
expect(e2eResults).toContain('Successfully ran target e2e');
|
||||
}
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react-native:storybook-configuration ${appName2} --generateStories --no-interactive`
|
||||
);
|
||||
checkFilesExist(
|
||||
`apps/${appName2}/.storybook/main.ts`,
|
||||
`apps/${appName2}/src/app/App.stories.tsx`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -224,8 +224,8 @@
|
||||
"mdast-util-to-markdown": "^1.5.0",
|
||||
"mdast-util-to-string": "^3.2.0",
|
||||
"memfs": "^3.0.1",
|
||||
"metro-config": "0.76.8",
|
||||
"metro-resolver": "0.76.8",
|
||||
"metro-config": "~0.80.4",
|
||||
"metro-resolver": "~0.80.4",
|
||||
"mini-css-extract-plugin": "~2.4.7",
|
||||
"minimatch": "9.0.3",
|
||||
"next-sitemap": "^3.1.10",
|
||||
|
||||
@ -99,6 +99,15 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"18.0.0": {
|
||||
"version": "18.0.0-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "^20.16.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const detoxVersion = '^20.11.1';
|
||||
export const testingLibraryJestDom = '5.16.5';
|
||||
export const detoxVersion = '^20.16.0';
|
||||
export const testingLibraryJestDom = '6.2.0';
|
||||
export const configPluginsDetoxVersion = '~6.0.0'; // only required for expo
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"nx",
|
||||
"@nx/rollup",
|
||||
"@nx/webpack",
|
||||
"@nx/cypress",
|
||||
"@nx/playwright",
|
||||
"@nx/detox",
|
||||
"typescript",
|
||||
"eslint",
|
||||
"expo",
|
||||
|
||||
112
packages/expo/docs/export-examples.md
Normal file
112
packages/expo/docs/export-examples.md
Normal file
@ -0,0 +1,112 @@
|
||||
`project.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "mobile",
|
||||
//...
|
||||
"targets": {
|
||||
//...
|
||||
"export": {
|
||||
"executor": "@nx/expo:export",
|
||||
"options": {
|
||||
"outputs": ["{options.outputDir}"],
|
||||
"platform": "all",
|
||||
"outputDir": "dist/apps/mobile"
|
||||
},
|
||||
"dependsOn": ["sync-deps"]
|
||||
}
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```shell
|
||||
nx run mobile:export
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Specify outputDir" %}
|
||||
The `outputDir` option allows you to specify the output directory of your bundle:
|
||||
|
||||
```json
|
||||
"export": {
|
||||
"executor": "@nx/expo:export",
|
||||
"outputs": ["{options.outputDir}"],
|
||||
"options": {
|
||||
"platform": "all",
|
||||
"bundler": "metro",
|
||||
"outputDir": "dist/apps/mobile"
|
||||
},
|
||||
"dependsOn": ["sync-deps"]
|
||||
},
|
||||
```
|
||||
|
||||
or run command: `nx run mobile:export --outputDir=dist/apps/mobile`.
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Specify the platform" %}
|
||||
The `platform` option allows you to specify the platform to compile with metro bundler: "ios", "android", "all", and "web".
|
||||
|
||||
For example, to bundle for web:
|
||||
|
||||
```json
|
||||
"export": {
|
||||
"executor": "@nx/expo:export",
|
||||
"outputs": ["{options.outputDir}"],
|
||||
"options": {
|
||||
"platform": "web",
|
||||
"bundler": "metro",
|
||||
"outputDir": "dist/apps/dogs"
|
||||
},
|
||||
"dependsOn": ["sync-deps"]
|
||||
},
|
||||
```
|
||||
|
||||
or run command `nx export mobile --platform=web`.
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Bundle for development" %}
|
||||
|
||||
The `dev` option allows you to bundle for development environments.
|
||||
|
||||
```json
|
||||
"export": {
|
||||
"executor": "@nx/expo:export",
|
||||
"outputs": ["{options.outputDir}"],
|
||||
"options": {
|
||||
"platform": "web",
|
||||
"bundler": "metro",
|
||||
"outputDir": "dist/apps/dogs",
|
||||
"dev": true
|
||||
},
|
||||
"dependsOn": ["sync-deps"]
|
||||
},
|
||||
```
|
||||
|
||||
or run command `nx export mobile --dev`.
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Clear bundle cache" %}
|
||||
|
||||
The `clear` option allows you to clear bundle cache.
|
||||
|
||||
```json
|
||||
"export": {
|
||||
"executor": "@nx/expo:export",
|
||||
"outputs": ["{options.outputDir}"],
|
||||
"options": {
|
||||
"platform": "web",
|
||||
"bundler": "metro",
|
||||
"outputDir": "dist/apps/dogs",
|
||||
"clear": true
|
||||
},
|
||||
"dependsOn": ["sync-deps"]
|
||||
},
|
||||
```
|
||||
|
||||
or run command `nx export mobile --clear`.
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
@ -39,6 +39,8 @@ Opens your app in Expo Go in a currently running iOS simulator on your computer:
|
||||
}
|
||||
```
|
||||
|
||||
or run command `nx start <your app name> --ios`.
|
||||
|
||||
Opens your app in Expo Go on a connected Android device
|
||||
|
||||
```json
|
||||
@ -51,6 +53,8 @@ Opens your app in Expo Go on a connected Android device
|
||||
}
|
||||
```
|
||||
|
||||
or run command `nx start <your app name> --android`.
|
||||
|
||||
Opens your app in a web browser:
|
||||
|
||||
```json
|
||||
@ -63,6 +67,8 @@ Opens your app in a web browser:
|
||||
}
|
||||
```
|
||||
|
||||
or run command `nx start <your app name> --web`.
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Specify the host" %}
|
||||
The `host` option allows you to specify the type of host to use. `lan` uses the local network; `tunnel` ues any network by tunnel through ngrok; `localhost` connects to the dev server over localhost.
|
||||
|
||||
@ -54,6 +54,11 @@
|
||||
"implementation": "./src/executors/submit/submit.impl",
|
||||
"schema": "./src/executors/submit/schema.json",
|
||||
"description": "Submit app binary to App Store and/or Play Store"
|
||||
},
|
||||
"serve": {
|
||||
"implementation": "./src/executors/serve/serve.impl",
|
||||
"schema": "./src/executors/serve/schema.json",
|
||||
"description": "Serve up the Expo web app locally"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,12 +36,6 @@
|
||||
"description": "Update package.json eas build lifecycle scripts",
|
||||
"implementation": "./src/migrations/update-16-1-4/update-eas-scripts"
|
||||
},
|
||||
"add-detox-app-json-16-1-4": {
|
||||
"version": "16.1.4-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Add app.json for detox",
|
||||
"factory": "./src/migrations/update-16-1-4/add-detox-app-json"
|
||||
},
|
||||
"update-16-6-0-add-dependsOn": {
|
||||
"cli": "nx",
|
||||
"version": "16.6.0-beta.0",
|
||||
@ -65,6 +59,30 @@
|
||||
"version": "16.9.0-beta.1",
|
||||
"description": "Update eas.json cli version",
|
||||
"implementation": "./src/migrations/update-16-9-0/update-eas-cli-version"
|
||||
},
|
||||
"update-18-0-0-remove-block-list": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove blockList in metro.config.js",
|
||||
"implementation": "./src/migrations/update-18-0-0/remove-block-list"
|
||||
},
|
||||
"update-18-0-0-remove-symlink-target": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove symlink target in project.json",
|
||||
"implementation": "./src/migrations/update-18-0-0/remove-symlink-target"
|
||||
},
|
||||
"update-18-0-0-remove-eas-cli": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove eas-cli from package.json",
|
||||
"implementation": "./src/migrations/update-18-0-0/remove-eas-cli"
|
||||
},
|
||||
"update-18-0-0-remove-offset-export-outputDir": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove the offset from the outputDir of the export target",
|
||||
"implementation": "./src/migrations/update-18-0-0/change-outputDir-export-target"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
@ -605,6 +623,67 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"18.0.0": {
|
||||
"version": "18.0.0-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "50.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-splash-screen": {
|
||||
"version": "~0.26.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-status-bar": {
|
||||
"version": "~1.11.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/cli": {
|
||||
"version": "~0.16.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-preset-expo": {
|
||||
"version": "~10.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "~18.2.45",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "~0.73.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-web": {
|
||||
"version": "~0.19.9",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "~0.17.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-runtime": {
|
||||
"version": "~3.1.1",
|
||||
"addToPackageJson": "devDependencies"
|
||||
},
|
||||
"react-native-svg-transformer": {
|
||||
"version": "1.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "14.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/react-native": {
|
||||
"version": "~12.4.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-expo": {
|
||||
"version": "~50.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,27 +26,23 @@
|
||||
"main": "./index",
|
||||
"types": "index.d.ts",
|
||||
"dependencies": {
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"chalk": "^4.1.0",
|
||||
"enhanced-resolve": "^5.8.3",
|
||||
"fs-extra": "^11.1.0",
|
||||
"metro-config": "~0.76.8",
|
||||
"metro-resolver": "~0.76.8",
|
||||
"metro-config": "~0.80.4",
|
||||
"metro-resolver": "~0.80.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"tslib": "^2.3.0",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||
"@nx/detox": "file:../detox",
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/jest": "file:../jest",
|
||||
"@nx/js": "file:../js",
|
||||
"@nx/eslint": "file:../eslint",
|
||||
"@nx/react": "file:../react",
|
||||
"@nx/web": "file:../web",
|
||||
"@nx/webpack": "file:../webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": ">= 49.0.0",
|
||||
"@expo/cli": ">= 0.10.0",
|
||||
"eas-cli": ">= 3.15.0"
|
||||
},
|
||||
"executors": "./executors.json",
|
||||
"ng-update": {
|
||||
"requirements": {},
|
||||
|
||||
@ -45,6 +45,11 @@ export function getResolveRequest(extensions: string[]) {
|
||||
if (resolvedPath) {
|
||||
return resolvedPath;
|
||||
}
|
||||
if (debug) {
|
||||
console.log(
|
||||
chalk.red(`[Nx] Unable to resolve with any resolver: ${realModuleName}`)
|
||||
);
|
||||
}
|
||||
throw new Error(`Cannot resolve ${chalk.bold(realModuleName)}`);
|
||||
};
|
||||
}
|
||||
@ -53,7 +58,7 @@ function resolveRequestFromContext(
|
||||
resolveRequest: Function,
|
||||
context: any,
|
||||
realModuleName: string,
|
||||
platform: string,
|
||||
platform: string | null,
|
||||
debug: boolean
|
||||
) {
|
||||
try {
|
||||
@ -75,7 +80,7 @@ function resolveRequestFromContext(
|
||||
function defaultMetroResolver(
|
||||
context: any,
|
||||
realModuleName: string,
|
||||
platform: string,
|
||||
platform: string | null,
|
||||
debug: boolean
|
||||
) {
|
||||
try {
|
||||
@ -130,7 +135,7 @@ function tsconfigPathsResolver(
|
||||
context: any,
|
||||
extensions: string[],
|
||||
realModuleName: string,
|
||||
platform: string,
|
||||
platform: string | null,
|
||||
debug: boolean
|
||||
) {
|
||||
try {
|
||||
@ -145,7 +150,7 @@ function tsconfigPathsResolver(
|
||||
} catch {
|
||||
if (debug) {
|
||||
console.log(
|
||||
chalk.red(`[Nx] Failed to resolve ${chalk.bold(realModuleName)}`)
|
||||
chalk.cyan(`[Nx] Failed to resolve ${chalk.bold(realModuleName)}`)
|
||||
);
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { joinPathFragments, workspaceRoot } from '@nx/devkit';
|
||||
import { mergeConfig } from 'metro-config';
|
||||
import type { MetroConfig } from 'metro-config';
|
||||
import { existsSync } from 'fs-extra';
|
||||
import { existsSync, readdirSync, statSync } from 'fs-extra';
|
||||
|
||||
import { getResolveRequest } from './metro-resolver';
|
||||
|
||||
@ -19,16 +19,26 @@ export async function withNxMetro(
|
||||
if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true';
|
||||
if (opts.extensions) extensions.push(...opts.extensions);
|
||||
|
||||
let watchFolders = [workspaceRoot];
|
||||
let watchFolders = readdirSync(workspaceRoot)
|
||||
.filter(
|
||||
(fileName) =>
|
||||
!['dist', 'e2e'].includes(fileName) && !fileName.startsWith('.')
|
||||
)
|
||||
.map((fileName) => joinPathFragments(workspaceRoot, fileName))
|
||||
.filter((filePath) => statSync(filePath).isDirectory());
|
||||
|
||||
if (opts.watchFolders?.length) {
|
||||
watchFolders = watchFolders.concat(opts.watchFolders);
|
||||
}
|
||||
|
||||
watchFolders = watchFolders.filter((folder) => existsSync(folder));
|
||||
watchFolders = [...new Set(watchFolders)].filter((folder) =>
|
||||
existsSync(folder)
|
||||
);
|
||||
|
||||
const nxConfig: MetroConfig = {
|
||||
resolver: {
|
||||
resolveRequest: getResolveRequest(extensions),
|
||||
nodeModulesPaths: [joinPathFragments(workspaceRoot, 'node_modules')],
|
||||
},
|
||||
watchFolders,
|
||||
};
|
||||
|
||||
@ -2,12 +2,14 @@ import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
||||
import { resolve } from 'path';
|
||||
|
||||
/**
|
||||
* @deprecated TODO(v17) use bundler: 'metro' instead, will be removed in 16.0.0
|
||||
* @deprecated TODO(v19) use bundler: 'metro' instead, will be removed in v19
|
||||
* This function add additional rules to expo's webpack config to make expo web working
|
||||
*/
|
||||
export async function withNxWebpack(config) {
|
||||
// add additional rule to load files under libs
|
||||
const rules = config.module.rules[1]?.oneOf;
|
||||
const rules = config.module.rules.find((rule) =>
|
||||
Array.isArray(rule.oneOf)
|
||||
)?.oneOf;
|
||||
if (rules) {
|
||||
rules.push({
|
||||
test: /\.(mjs|[jt]sx?)$/,
|
||||
@ -34,17 +36,19 @@ export async function withNxWebpack(config) {
|
||||
if (!config.resolve.plugins) {
|
||||
config.resolve.plugins = [];
|
||||
}
|
||||
|
||||
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
||||
const tsConfigPath = resolve('tsconfig.json');
|
||||
|
||||
const tsConfigPath = resolve(__dirname, 'tsconfig.json');
|
||||
config.resolve.plugins.push(
|
||||
new TsconfigPathsPlugin({
|
||||
configFile: tsConfigPath,
|
||||
extensions,
|
||||
})
|
||||
);
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
crypto: require.resolve('crypto-browserify'),
|
||||
stream: require.resolve('stream-browserify'),
|
||||
};
|
||||
|
||||
config.resolve.symlinks = true;
|
||||
return config;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { ExecutorContext, logger, names } from '@nx/devkit';
|
||||
import { resolve as pathResolve } from 'path';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
|
||||
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||
import { resolveEas } from '../../utils/resolve-eas';
|
||||
|
||||
import { ExpoEasBuildListOptions } from './schema';
|
||||
|
||||
@ -18,7 +18,6 @@ export default async function* buildListExecutor(
|
||||
): AsyncGenerator<ReactNativeBuildListOutput> {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||
|
||||
try {
|
||||
if (options.json) {
|
||||
@ -42,7 +41,7 @@ export function runCliBuildList(
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess = fork(
|
||||
require.resolve('eas-cli/bin/run'),
|
||||
resolveEas(workspaceRoot),
|
||||
['build:list', ...createBuildListOptions(options)],
|
||||
{
|
||||
cwd: pathResolve(workspaceRoot, projectRoot),
|
||||
|
||||
@ -2,6 +2,8 @@ import { ExecutorContext, names } from '@nx/devkit';
|
||||
import { resolve as pathResolve } from 'path';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
|
||||
import { resolveEas } from '../../utils/resolve-eas';
|
||||
|
||||
import { ExpoEasBuildOptions } from './schema';
|
||||
|
||||
export interface ReactNativeBuildOutput {
|
||||
@ -34,7 +36,7 @@ function runCliBuild(
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess = fork(
|
||||
require.resolve('eas-cli/bin/run'),
|
||||
resolveEas(workspaceRoot),
|
||||
['build', ...createBuildOptions(options)],
|
||||
{
|
||||
cwd: pathResolve(workspaceRoot, projectRoot),
|
||||
|
||||
@ -6,6 +6,10 @@ export interface ExpoEnsureSymlinkOutput {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (@xiongemi): remove this function in v19.
|
||||
* @deprecated It is no longer needed for react native 73.
|
||||
*/
|
||||
export default async function* ensureSymlinkExecutor(
|
||||
_,
|
||||
context: ExecutorContext
|
||||
|
||||
18
packages/expo/src/executors/export/export.impl.spec.ts
Normal file
18
packages/expo/src/executors/export/export.impl.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { createExportOptions } from './export.impl';
|
||||
|
||||
describe('createExportOptions', () => {
|
||||
it('should not pass --dev if dev is false', () => {
|
||||
const options = createExportOptions({ dev: false }, '');
|
||||
expect(options).toEqual([]);
|
||||
});
|
||||
|
||||
it('should pass --dev if dev is true', () => {
|
||||
const options = createExportOptions({ dev: true }, '');
|
||||
expect(options).toEqual(['--dev']);
|
||||
});
|
||||
|
||||
it('should pass --output-dir with offset to workspace root', () => {
|
||||
const options = createExportOptions({ outputDir: 'dist' }, 'apps/app');
|
||||
expect(options).toEqual(['--output-dir', '../../dist']);
|
||||
});
|
||||
});
|
||||
@ -1,4 +1,9 @@
|
||||
import { ExecutorContext, names } from '@nx/devkit';
|
||||
import {
|
||||
ExecutorContext,
|
||||
joinPathFragments,
|
||||
names,
|
||||
offsetFromRoot,
|
||||
} from '@nx/devkit';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { resolve as pathResolve } from 'path';
|
||||
|
||||
@ -19,7 +24,6 @@ export default async function* exportExecutor(
|
||||
|
||||
try {
|
||||
await exportAsync(context.root, projectRoot, options);
|
||||
|
||||
yield {
|
||||
success: true,
|
||||
};
|
||||
@ -38,11 +42,7 @@ function exportAsync(
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess = fork(
|
||||
require.resolve('@expo/cli/build/bin/cli'),
|
||||
[
|
||||
`export${options.bundler === 'webpack' ? ':web' : ''}`,
|
||||
'.',
|
||||
...createExportOptions(options),
|
||||
],
|
||||
[`export`, ...createExportOptions(options, projectRoot)],
|
||||
{ cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
|
||||
);
|
||||
|
||||
@ -63,19 +63,34 @@ function exportAsync(
|
||||
});
|
||||
}
|
||||
|
||||
const nxOptions = ['bundler'];
|
||||
const nxOptions = ['bundler', 'interactive']; // interactive is passed in by e2e tests
|
||||
// options from https://github.com/expo/expo/blob/main/packages/@expo/cli/src/export/index.ts
|
||||
function createExportOptions(options: ExportExecutorSchema) {
|
||||
export function createExportOptions(
|
||||
options: ExportExecutorSchema,
|
||||
projectRoot: string
|
||||
) {
|
||||
return Object.keys(options).reduce((acc, k) => {
|
||||
if (!nxOptions.includes(k)) {
|
||||
const v = options[k];
|
||||
if (typeof v === 'boolean') {
|
||||
if (v === true) {
|
||||
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||
acc.push(`--${names(k).fileName}`);
|
||||
}
|
||||
} else {
|
||||
acc.push(`--${names(k).fileName}`, v);
|
||||
switch (k) {
|
||||
case 'outputDir':
|
||||
const path = joinPathFragments(offsetFromRoot(projectRoot), v); // need to add offset for the outputDir
|
||||
acc.push('--output-dir', path);
|
||||
break;
|
||||
case 'minify':
|
||||
if (v === false) {
|
||||
acc.push('--no-minify'); // cli only accpets --no-minify
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (typeof v === 'boolean') {
|
||||
if (v === true) {
|
||||
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||
acc.push(`--${names(k).fileName}`);
|
||||
}
|
||||
} else {
|
||||
acc.push(`--${names(k).fileName}`, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
// options form https://github.com/expo/expo/blob/main/packages/%40expo/cli/src/export/resolveOptions.ts
|
||||
// https://github.com/expo/expo/blob/main/packages/%40expo/cli/src/export/index.ts
|
||||
|
||||
export interface ExportExecutorSchema {
|
||||
platform?: 'ios' | 'android' | 'all' | 'web'; // default is 'all'
|
||||
outputDir?: string;
|
||||
clear?: boolean;
|
||||
dev?: boolean;
|
||||
minify?: boolean;
|
||||
maxWorkers?: number;
|
||||
dumpAssetmap?: boolean;
|
||||
dumpSourcemap?: boolean;
|
||||
bundler: 'metro' | 'webpack';
|
||||
sourcemap?: boolean;
|
||||
}
|
||||
|
||||
@ -11,38 +11,39 @@
|
||||
"platform": {
|
||||
"description": "Choose the platform to compile for",
|
||||
"enum": ["ios", "android", "all", "web"],
|
||||
"default": "all",
|
||||
"alias": "p",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"dev": {
|
||||
"type": "boolean",
|
||||
"description": "Bundle for development environments without minifying code or stripping the __DEV__ boolean. Configure static files for developing locally using a non-https server."
|
||||
"description": "Configure static files for developing locally using a non-https server"
|
||||
},
|
||||
"clear": {
|
||||
"type": "boolean",
|
||||
"description": "Clear the bundler cache before exporting"
|
||||
},
|
||||
"minify": {
|
||||
"type": "boolean",
|
||||
"description": "Minify source"
|
||||
},
|
||||
"outputDir": {
|
||||
"type": "string",
|
||||
"description": "The directory to export the static files to. Default: dist"
|
||||
"description": "Relative to workspace root, the directory to export the static files to. Default: dist"
|
||||
},
|
||||
"maxWorkers": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of tasks to allow Metro to spawn"
|
||||
"description": "When bundler is metro, the maximum number of tasks to allow the bundler to spawn"
|
||||
},
|
||||
"dumpAssetmap": {
|
||||
"type": "boolean",
|
||||
"description": "Dump the asset map for further processing"
|
||||
"description": "When bundler is metro, whether to dump the asset map for further processing"
|
||||
},
|
||||
"dumpSourcemap": {
|
||||
"sourceMaps": {
|
||||
"type": "boolean",
|
||||
"description": "Dump the source map for debugging the JS bundle"
|
||||
},
|
||||
"bundler": {
|
||||
"enum": ["metro", "webpack"],
|
||||
"description": "Choose the bundler to compile for",
|
||||
"default": "metro"
|
||||
"description": "When bundler is metro, whether to emit JavaScript source maps"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
"required": ["platform"],
|
||||
"examplesFile": "../../../docs/export-examples.md"
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { ExecutorContext, names } from '@nx/devkit';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
|
||||
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||
import { ExpoInstallOptions } from './schema';
|
||||
|
||||
export interface ExpoInstallOutput {
|
||||
@ -19,7 +18,6 @@ export default async function* installExecutor(
|
||||
|
||||
try {
|
||||
await installAsync(context.root, options);
|
||||
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||
|
||||
yield {
|
||||
success: true,
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { ExecutorContext, names } from '@nx/devkit';
|
||||
import { ExecutorContext, names, workspaceRoot } from '@nx/devkit';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||
import { podInstall } from '../../utils/pod-install-task';
|
||||
import { installAsync } from '../install/install.impl';
|
||||
import { ExpoPrebuildOptions } from './schema';
|
||||
@ -19,13 +18,12 @@ export default async function* prebuildExecutor(
|
||||
): AsyncGenerator<ExpoPrebuildOutput> {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||
|
||||
try {
|
||||
await prebuildAsync(context.root, projectRoot, options);
|
||||
|
||||
if (options.install) {
|
||||
await installAsync(context.root, {});
|
||||
await installAsync(workspaceRoot, {});
|
||||
if (options.platform === 'ios') {
|
||||
podInstall(join(context.root, projectRoot, 'ios'));
|
||||
}
|
||||
@ -70,13 +68,20 @@ export function prebuildAsync(
|
||||
});
|
||||
}
|
||||
|
||||
const nxOptions = ['install', 'interactive'];
|
||||
const nxOptions = ['install', 'interactive']; // interactive is passed in by e2e tests
|
||||
// options from https://github.com/expo/expo/blob/main/packages/%40expo/cli/src/prebuild/index.ts
|
||||
function createPrebuildOptions(options: ExpoPrebuildOptions) {
|
||||
return Object.keys(options).reduce((acc, k) => {
|
||||
if (!nxOptions.includes(k)) {
|
||||
const v = options[k];
|
||||
acc.push(`--${names(k).fileName}=${v}`);
|
||||
if (typeof v === 'boolean') {
|
||||
if (v === true) {
|
||||
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||
acc.push(`--${names(k).fileName}`);
|
||||
}
|
||||
} else {
|
||||
acc.push(`--${names(k).fileName}`, v);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
@ -31,5 +31,6 @@
|
||||
"description": "Project template to clone from. File path pointing to a local tar file or a github repo"
|
||||
}
|
||||
},
|
||||
"required": ["platform"],
|
||||
"examplesFile": "../../../docs/prebuild-examples.md"
|
||||
}
|
||||
|
||||
@ -4,11 +4,6 @@ import { ChildProcess, fork } from 'child_process';
|
||||
import { platform } from 'os';
|
||||
import { existsSync } from 'fs-extra';
|
||||
|
||||
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||
import {
|
||||
displayNewlyAddedDepsMessage,
|
||||
syncDeps,
|
||||
} from '../sync-deps/sync-deps.impl';
|
||||
import { ExpoRunOptions } from './schema';
|
||||
import { prebuildAsync } from '../prebuild/prebuild.impl';
|
||||
import { podInstall } from '../../utils/pod-install-task';
|
||||
|
||||
13
packages/expo/src/executors/serve/lib/is-packager-running.ts
Normal file
13
packages/expo/src/executors/serve/lib/is-packager-running.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
export async function isPackagerRunning(
|
||||
packagerPort: number
|
||||
): Promise<'running' | 'not_running' | 'unrecognized'> {
|
||||
try {
|
||||
const resp = await fetch(`http://localhost:${packagerPort}/status`);
|
||||
const data = await resp.text();
|
||||
return data === 'packager-status:running' ? 'running' : 'unrecognized';
|
||||
} catch {
|
||||
return 'not_running';
|
||||
}
|
||||
}
|
||||
7
packages/expo/src/executors/serve/schema.d.ts
vendored
Normal file
7
packages/expo/src/executors/serve/schema.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
export interface ExpoServeExecutorSchema {
|
||||
port: number;
|
||||
dev?: boolean;
|
||||
minify?: boolean;
|
||||
https?: boolean;
|
||||
clear?: boolean;
|
||||
}
|
||||
35
packages/expo/src/executors/serve/schema.json
Normal file
35
packages/expo/src/executors/serve/schema.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"version": 2,
|
||||
"outputCapture": "direct-nodejs",
|
||||
"cli": "nx",
|
||||
"$id": "NxExpoServe",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"title": "Serve web app for Expo",
|
||||
"description": "Packager Server target options.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to start the native Metro bundler on (does not apply to web or tunnel)",
|
||||
"default": 19000,
|
||||
"alias": "p"
|
||||
},
|
||||
"clear": {
|
||||
"type": "boolean",
|
||||
"description": "Clear the Metro bundler cache",
|
||||
"alias": "c"
|
||||
},
|
||||
"maxWorkers": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of tasks to allow Metro to spawn"
|
||||
},
|
||||
"dev": {
|
||||
"type": "boolean",
|
||||
"description": "Turn development mode on or off"
|
||||
},
|
||||
"minify": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not to minify code"
|
||||
}
|
||||
}
|
||||
}
|
||||
127
packages/expo/src/executors/serve/serve.impl.ts
Normal file
127
packages/expo/src/executors/serve/serve.impl.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import { ExecutorContext, logger, names } from '@nx/devkit';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { resolve as pathResolve } from 'path';
|
||||
import { isPackagerRunning } from './lib/is-packager-running';
|
||||
import { ExpoServeExecutorSchema } from './schema';
|
||||
|
||||
export interface ExpoServeOutput {
|
||||
port?: number;
|
||||
baseUrl?: string;
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export default async function* serveExecutor(
|
||||
options: ExpoServeExecutorSchema,
|
||||
context: ExecutorContext
|
||||
): AsyncGenerator<ExpoServeOutput> {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
|
||||
const serveProcess = await runCliServe(context.root, projectRoot, options);
|
||||
|
||||
yield {
|
||||
port: options.port,
|
||||
baseUrl: `http://localhost:${options.port}`,
|
||||
success: true,
|
||||
};
|
||||
|
||||
if (!serveProcess) {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve) => {
|
||||
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
|
||||
serveProcess.kill(signal);
|
||||
resolve();
|
||||
process.exit();
|
||||
};
|
||||
process.once('exit', (signal) => serveProcess.kill(signal));
|
||||
process.once('SIGTERM', processExitListener);
|
||||
process.once('SIGINT', processExitListener);
|
||||
process.once('SIGQUIT', processExitListener);
|
||||
});
|
||||
}
|
||||
|
||||
export async function runCliServe(
|
||||
workspaceRoot: string,
|
||||
projectRoot: string,
|
||||
options: ExpoServeExecutorSchema
|
||||
): Promise<ChildProcess> {
|
||||
const result = await isPackagerRunning(options.port);
|
||||
if (result === 'running') {
|
||||
logger.info(`JS server already running on port ${options.port}.`);
|
||||
} else if (result === 'unrecognized') {
|
||||
logger.warn('JS server not recognized.');
|
||||
} else {
|
||||
// result === 'not_running'
|
||||
logger.info('Starting JS server...');
|
||||
|
||||
try {
|
||||
return await serveAsync(workspaceRoot, projectRoot, options);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to serve the packager server. Error details: ${error.message}`
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function serveAsync(
|
||||
workspaceRoot: string,
|
||||
projectRoot: string,
|
||||
options: ExpoServeExecutorSchema
|
||||
): Promise<ChildProcess> {
|
||||
return new Promise<ChildProcess>((resolve, reject) => {
|
||||
const childProcess = fork(
|
||||
require.resolve('@expo/cli/build/bin/cli'),
|
||||
['start', '--web', ...createServeOptions(options)],
|
||||
{
|
||||
cwd: pathResolve(workspaceRoot, projectRoot),
|
||||
env: process.env,
|
||||
stdio: ['inherit', 'pipe', 'pipe', 'ipc'],
|
||||
}
|
||||
);
|
||||
|
||||
childProcess.stdout.on('data', (data) => {
|
||||
process.stdout.write(data);
|
||||
if (data.toString().includes('Bundling complete')) {
|
||||
resolve(childProcess);
|
||||
}
|
||||
});
|
||||
childProcess.stderr.on('data', (data) => {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
|
||||
childProcess.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
childProcess.on('exit', (code) => {
|
||||
if (code === 0) {
|
||||
resolve(childProcess);
|
||||
} else {
|
||||
reject(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createServeOptions(options: ExpoServeExecutorSchema): string[] {
|
||||
return Object.keys(options).reduce((acc, k) => {
|
||||
const v = options[k];
|
||||
if (k === 'dev') {
|
||||
if (v === false) {
|
||||
acc.push(`--no-dev`); // only no-dev flag is supported
|
||||
}
|
||||
} else {
|
||||
if (typeof v === 'boolean') {
|
||||
if (v === true) {
|
||||
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||
acc.push(`--${names(k).fileName}`);
|
||||
}
|
||||
} else {
|
||||
acc.push(`--${names(k).fileName}`, v);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
@ -45,7 +45,13 @@ function startAsync(
|
||||
childProcess = fork(
|
||||
require.resolve('@expo/cli/build/bin/cli'),
|
||||
['start', ...createStartOptions(options)],
|
||||
{ cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
|
||||
{
|
||||
cwd: pathResolve(workspaceRoot, projectRoot),
|
||||
env: {
|
||||
RCT_METRO_PORT: options.port.toString(),
|
||||
...process.env,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Ensure the child process is killed when the parent exits
|
||||
|
||||
@ -2,6 +2,8 @@ import { ExecutorContext, names } from '@nx/devkit';
|
||||
import { resolve as pathResolve } from 'path';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
|
||||
import { resolveEas } from '../../utils/resolve-eas';
|
||||
|
||||
import { SubmitExecutorSchema } from './schema';
|
||||
|
||||
export interface ReactNativeSubmitOutput {
|
||||
@ -35,7 +37,7 @@ function runCliSubmit(
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess = fork(
|
||||
require.resolve('eas-cli/bin/run'),
|
||||
resolveEas(workspaceRoot),
|
||||
['submit', ...createSubmitOptions(options)],
|
||||
{
|
||||
cwd: pathResolve(workspaceRoot, projectRoot),
|
||||
|
||||
@ -3,32 +3,27 @@ import * as chalk from 'chalk';
|
||||
import {
|
||||
ExecutorContext,
|
||||
logger,
|
||||
ProjectGraph,
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import { findAllNpmDependencies } from '../../utils/find-all-npm-dependencies';
|
||||
|
||||
import { ExpoSyncDepsOptions } from './schema';
|
||||
|
||||
export interface ExpoSyncDepsOutput {
|
||||
export interface ReactNativeSyncDepsOutput {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export default async function* syncDepsExecutor(
|
||||
options: ExpoSyncDepsOptions,
|
||||
context: ExecutorContext
|
||||
): AsyncGenerator<ExpoSyncDepsOutput> {
|
||||
): AsyncGenerator<ReactNativeSyncDepsOutput> {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
displayNewlyAddedDepsMessage(
|
||||
context.projectName,
|
||||
await syncDeps(
|
||||
context.projectName,
|
||||
projectRoot,
|
||||
context.root,
|
||||
context.projectGraph,
|
||||
typeof options.include === 'string'
|
||||
? options.include.split(',')
|
||||
: options.include,
|
||||
@ -42,14 +37,16 @@ export default async function* syncDepsExecutor(
|
||||
}
|
||||
|
||||
export async function syncDeps(
|
||||
projectName: string,
|
||||
projectRoot: string,
|
||||
workspaceRoot: string,
|
||||
projectGraph: ProjectGraph,
|
||||
include: string[] = [],
|
||||
exclude: string[] = []
|
||||
): Promise<string[]> {
|
||||
let npmDeps = findAllNpmDependencies(projectGraph, projectName);
|
||||
const workspacePackageJsonPath = join(workspaceRoot, 'package.json');
|
||||
const workspacePackageJson = readJsonFile(workspacePackageJsonPath);
|
||||
let npmDeps = Object.keys(workspacePackageJson.dependencies || {});
|
||||
let npmDevdeps = Object.keys(workspacePackageJson.devDependencies || {});
|
||||
|
||||
const packageJsonPath = join(workspaceRoot, projectRoot, 'package.json');
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
const newDeps = [];
|
||||
@ -67,13 +64,27 @@ export async function syncDeps(
|
||||
npmDeps = npmDeps.filter((dep) => !exclude.includes(dep));
|
||||
}
|
||||
|
||||
if (!packageJson.devDependencies) {
|
||||
packageJson.devDependencies = {};
|
||||
}
|
||||
if (!packageJson.dependencies) {
|
||||
packageJson.dependencies = {};
|
||||
}
|
||||
|
||||
npmDeps.forEach((dep) => {
|
||||
if (!packageJson.dependencies[dep]) {
|
||||
if (!packageJson.dependencies[dep] && !packageJson.devDependencies[dep]) {
|
||||
packageJson.dependencies[dep] = '*';
|
||||
newDeps.push(dep);
|
||||
updated = true;
|
||||
}
|
||||
});
|
||||
npmDevdeps.forEach((dep) => {
|
||||
if (!packageJson.dependencies[dep] && !packageJson.devDependencies[dep]) {
|
||||
packageJson.devDependencies[dep] = '*';
|
||||
newDeps.push(dep);
|
||||
updated = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (updated) {
|
||||
writeJsonFile(packageJsonPath, packageJson);
|
||||
|
||||
@ -2,15 +2,15 @@ import { ExecutorContext, names } from '@nx/devkit';
|
||||
import { resolve as pathResolve } from 'path';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
|
||||
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||
|
||||
import { ExpoEasUpdateOptions } from './schema';
|
||||
import { resolveEas } from '../../utils/resolve-eas';
|
||||
import {
|
||||
displayNewlyAddedDepsMessage,
|
||||
syncDeps,
|
||||
} from '../sync-deps/sync-deps.impl';
|
||||
import { installAsync } from '../install/install.impl';
|
||||
|
||||
import { ExpoEasUpdateOptions } from './schema';
|
||||
|
||||
export interface ReactNativeUpdateOutput {
|
||||
success: boolean;
|
||||
}
|
||||
@ -26,15 +26,8 @@ export default async function* buildExecutor(
|
||||
await installAsync(context.root, { packages: ['expo-updates'] });
|
||||
displayNewlyAddedDepsMessage(
|
||||
context.projectName,
|
||||
await syncDeps(
|
||||
context.projectName,
|
||||
projectRoot,
|
||||
context.root,
|
||||
context.projectGraph,
|
||||
['expo-updates']
|
||||
)
|
||||
await syncDeps(projectRoot, context.root, ['expo-updates'])
|
||||
);
|
||||
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||
|
||||
try {
|
||||
await runCliUpdate(context.root, projectRoot, options);
|
||||
@ -53,7 +46,7 @@ function runCliUpdate(
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess = fork(
|
||||
require.resolve('eas-cli/bin/run'),
|
||||
resolveEas(workspaceRoot),
|
||||
['update', ...createUpdateOptions(options)],
|
||||
{ cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
|
||||
);
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
|
||||
import { runSymlink } from '../../utils/symlink-task';
|
||||
import { addLinting } from '../../utils/add-linting';
|
||||
import { addJest } from '../../utils/add-jest';
|
||||
|
||||
@ -16,7 +15,7 @@ import initGenerator from '../init/init';
|
||||
import { addProject } from './lib/add-project';
|
||||
import { createApplicationFiles } from './lib/create-application-files';
|
||||
import { addEasScripts } from './lib/add-eas-scripts';
|
||||
import { addDetox } from './lib/add-detox';
|
||||
import { addE2e } from './lib/add-e2e';
|
||||
import { Schema } from './schema';
|
||||
import { ensureDependencies } from '../../utils/ensure-dependencies';
|
||||
import { initRootBabelConfig } from '../../utils/init-root-babel-config';
|
||||
@ -72,10 +71,8 @@ export async function expoApplicationGeneratorInternal(
|
||||
options.skipPackageJson
|
||||
);
|
||||
tasks.push(jestTask);
|
||||
const detoxTask = await addDetox(host, options);
|
||||
tasks.push(detoxTask);
|
||||
const symlinkTask = runSymlink(host.root, options.appProjectRoot);
|
||||
tasks.push(symlinkTask);
|
||||
const e2eTask = await addE2e(host, options);
|
||||
tasks.push(e2eTask);
|
||||
addEasScripts(host);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
"bundler": "metro"
|
||||
},
|
||||
"plugins": [
|
||||
<% if (e2eTestRunner === 'detox') { %>
|
||||
[
|
||||
"@config-plugins/detox",
|
||||
{
|
||||
@ -37,6 +38,7 @@
|
||||
"subdomains": ["10.0.2.2", "localhost"]
|
||||
}
|
||||
]
|
||||
<% } %>
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,8 +21,5 @@
|
||||
},
|
||||
"submit": {
|
||||
"production": {}
|
||||
},
|
||||
"cli": {
|
||||
"version": ">= <%= easCliVersion.replace('~', '') %>"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
const { withNxMetro } = require('@nx/expo');
|
||||
const { getDefaultConfig } = require('@expo/metro-config');
|
||||
const { mergeConfig } = require('metro-config');
|
||||
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
||||
|
||||
const defaultConfig = getDefaultConfig(__dirname);
|
||||
const { assetExts, sourceExts } = defaultConfig.resolver;
|
||||
@ -19,9 +18,6 @@ const customConfig = {
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
|
||||
// unstable_enableSymlinks: true,
|
||||
// unstable_enablePackageExports: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -19,7 +19,10 @@ export const App = () => {
|
||||
return (
|
||||
<>
|
||||
<StatusBar barStyle="dark-content" />
|
||||
<SafeAreaView>
|
||||
<SafeAreaView
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<ScrollView
|
||||
ref={(ref) => {
|
||||
scrollViewRef.current = ref;
|
||||
@ -29,7 +32,7 @@ export const App = () => {
|
||||
>
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.textLg}>Hello there,</Text>
|
||||
<Text style={[styles.textXL, styles.appTitleText]} testID="heading">
|
||||
<Text style={[styles.textXL, styles.appTitleText]} testID="heading" role="heading">
|
||||
Welcome <%= displayName %> 👋
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
||||
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
|
||||
const { resolve } = require('path');
|
||||
|
||||
/**
|
||||
* @deprecated use bundler: 'metro' instead
|
||||
*/
|
||||
module.exports = async function (env, argv) {
|
||||
const config = await createExpoWebpackConfigAsync(env, argv);
|
||||
|
||||
// Customize the config before returning it.
|
||||
// add additional rule to load files under libs
|
||||
const rules = config.module.rules.find((rule) =>
|
||||
Array.isArray(rule.oneOf)
|
||||
)?.oneOf;
|
||||
if (rules) {
|
||||
rules.push({
|
||||
test: /\.(mjs|[jt]sx?)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: require.resolve('@nx/webpack/src/utils/web-babel-loader.js'),
|
||||
options: {
|
||||
presets: [
|
||||
[
|
||||
'@nx/react/babel',
|
||||
{
|
||||
runtime: 'automatic',
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!config.resolve) {
|
||||
config.resolve = {};
|
||||
}
|
||||
if (!config.resolve.plugins) {
|
||||
config.resolve.plugins = [];
|
||||
}
|
||||
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
||||
const tsConfigPath = resolve(__dirname, 'tsconfig.json');
|
||||
config.resolve.plugins.push(
|
||||
new TsconfigPathsPlugin({
|
||||
configFile: tsConfigPath,
|
||||
extensions,
|
||||
})
|
||||
);
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
crypto: require.resolve('crypto-browserify'),
|
||||
stream: require.resolve('stream-browserify'),
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
@ -1,24 +0,0 @@
|
||||
import { detoxApplicationGenerator } from '@nx/detox';
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
import { Linter } from '@nx/eslint';
|
||||
|
||||
export async function addDetox(host: Tree, options: NormalizedSchema) {
|
||||
if (options?.e2eTestRunner !== 'detox') {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
return detoxApplicationGenerator(host, {
|
||||
...options,
|
||||
linter: Linter.EsLint,
|
||||
e2eName: `${options.projectName}-e2e`,
|
||||
e2eDirectory: `${options.appProjectRoot}-e2e`,
|
||||
projectNameAndRootFormat: 'as-provided',
|
||||
appProject: options.projectName,
|
||||
appDisplayName: options.displayName,
|
||||
appName: options.name,
|
||||
framework: 'expo',
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
104
packages/expo/src/generators/application/lib/add-e2e.ts
Normal file
104
packages/expo/src/generators/application/lib/add-e2e.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import type { GeneratorCallback, Tree } from '@nx/devkit';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
ensurePackage,
|
||||
getPackageManagerCommand,
|
||||
joinPathFragments,
|
||||
} from '@nx/devkit';
|
||||
import { webStaticServeGenerator } from '@nx/web';
|
||||
|
||||
import { nxVersion } from '../../../utils/versions';
|
||||
import { hasExpoPlugin } from '../../../utils/has-expo-plugin';
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
|
||||
export async function addE2e(
|
||||
tree: Tree,
|
||||
options: NormalizedSchema
|
||||
): Promise<GeneratorCallback> {
|
||||
switch (options.e2eTestRunner) {
|
||||
case 'cypress': {
|
||||
const hasNxExportPlugin = hasExpoPlugin(tree);
|
||||
if (!hasNxExportPlugin) {
|
||||
webStaticServeGenerator(tree, {
|
||||
buildTarget: `${options.projectName}:export`,
|
||||
targetName: 'serve-static',
|
||||
});
|
||||
}
|
||||
|
||||
const { configurationGenerator } = ensurePackage<
|
||||
typeof import('@nx/cypress')
|
||||
>('@nx/cypress', nxVersion);
|
||||
|
||||
addProjectConfiguration(tree, options.e2eProjectName, {
|
||||
projectType: 'application',
|
||||
root: options.e2eProjectRoot,
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
targets: {},
|
||||
implicitDependencies: [options.projectName],
|
||||
tags: [],
|
||||
});
|
||||
|
||||
return await configurationGenerator(tree, {
|
||||
...options,
|
||||
project: options.e2eProjectName,
|
||||
directory: 'src',
|
||||
// the name and root are already normalized, instruct the generator to use them as is
|
||||
bundler: 'none',
|
||||
skipFormat: true,
|
||||
devServerTarget: `${options.projectName}:serve`,
|
||||
port: 4200,
|
||||
baseUrl: 'http://localhost:4200',
|
||||
ciWebServerCommand: hasNxExportPlugin
|
||||
? `nx run ${options.projectName}:serve-static`
|
||||
: undefined,
|
||||
jsx: true,
|
||||
rootProject: options.rootProject,
|
||||
});
|
||||
}
|
||||
case 'playwright': {
|
||||
const { configurationGenerator } = ensurePackage<
|
||||
typeof import('@nx/playwright')
|
||||
>('@nx/playwright', nxVersion);
|
||||
addProjectConfiguration(tree, options.e2eProjectName, {
|
||||
projectType: 'application',
|
||||
root: options.e2eProjectRoot,
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
targets: {},
|
||||
implicitDependencies: [options.projectName],
|
||||
});
|
||||
return configurationGenerator(tree, {
|
||||
project: options.e2eProjectName,
|
||||
skipFormat: true,
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
directory: 'src',
|
||||
js: false,
|
||||
linter: options.linter,
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
webServerCommand: `${getPackageManagerCommand().exec} nx serve ${
|
||||
options.name
|
||||
}`,
|
||||
webServerAddress: 'http://localhost:4200',
|
||||
rootProject: options.rootProject,
|
||||
});
|
||||
}
|
||||
case 'detox':
|
||||
const { detoxApplicationGenerator } = ensurePackage<
|
||||
typeof import('@nx/detox')
|
||||
>('@nx/detox', nxVersion);
|
||||
return detoxApplicationGenerator(tree, {
|
||||
...options,
|
||||
e2eName: options.e2eProjectName,
|
||||
e2eDirectory: options.e2eProjectRoot,
|
||||
projectNameAndRootFormat: 'as-provided',
|
||||
appProject: options.projectName,
|
||||
appDisplayName: options.displayName,
|
||||
appName: options.name,
|
||||
framework: 'expo',
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
skipFormat: true,
|
||||
});
|
||||
case 'none':
|
||||
default:
|
||||
return () => {};
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
offsetFromRoot,
|
||||
ProjectConfiguration,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
@ -40,22 +39,21 @@ function getTargets(options: NormalizedSchema) {
|
||||
|
||||
architect.start = {
|
||||
executor: '@nx/expo:start',
|
||||
dependsOn: ['ensure-symlink', 'sync-deps'],
|
||||
options: {
|
||||
port: 8081,
|
||||
},
|
||||
dependsOn: ['sync-deps'],
|
||||
options: {},
|
||||
};
|
||||
|
||||
architect.serve = {
|
||||
executor: 'nx:run-commands',
|
||||
executor: '@nx/expo:serve',
|
||||
dependsOn: ['sync-deps'],
|
||||
options: {
|
||||
command: `nx start ${options.projectName}`,
|
||||
port: 4200,
|
||||
},
|
||||
};
|
||||
|
||||
architect['run-ios'] = {
|
||||
executor: '@nx/expo:run',
|
||||
dependsOn: ['ensure-symlink', 'sync-deps'],
|
||||
dependsOn: ['sync-deps'],
|
||||
options: {
|
||||
platform: 'ios',
|
||||
},
|
||||
@ -63,7 +61,7 @@ function getTargets(options: NormalizedSchema) {
|
||||
|
||||
architect['run-android'] = {
|
||||
executor: '@nx/expo:run',
|
||||
dependsOn: ['ensure-symlink', 'sync-deps'],
|
||||
dependsOn: ['sync-deps'],
|
||||
options: {
|
||||
platform: 'android',
|
||||
},
|
||||
@ -71,6 +69,7 @@ function getTargets(options: NormalizedSchema) {
|
||||
|
||||
architect['build'] = {
|
||||
executor: '@nx/expo:build',
|
||||
dependsOn: ['sync-deps'],
|
||||
options: {},
|
||||
};
|
||||
|
||||
@ -89,14 +88,9 @@ function getTargets(options: NormalizedSchema) {
|
||||
options: {},
|
||||
};
|
||||
|
||||
architect['ensure-symlink'] = {
|
||||
executor: '@nx/expo:ensure-symlink',
|
||||
options: {},
|
||||
};
|
||||
|
||||
architect['prebuild'] = {
|
||||
executor: '@nx/expo:prebuild',
|
||||
dependsOn: ['ensure-symlink', 'sync-deps'],
|
||||
dependsOn: ['sync-deps'],
|
||||
options: {},
|
||||
};
|
||||
|
||||
@ -112,19 +106,11 @@ function getTargets(options: NormalizedSchema) {
|
||||
|
||||
architect['export'] = {
|
||||
executor: '@nx/expo:export',
|
||||
dependsOn: ['ensure-symlink', 'sync-deps'],
|
||||
dependsOn: ['sync-deps'],
|
||||
outputs: ['{options.outputDir}'],
|
||||
options: {
|
||||
platform: 'all',
|
||||
outputDir: `${offsetFromRoot(options.appProjectRoot)}dist/${
|
||||
options.appProjectRoot
|
||||
}`,
|
||||
},
|
||||
};
|
||||
|
||||
architect['export-web'] = {
|
||||
executor: '@nx/expo:export',
|
||||
options: {
|
||||
bundler: 'metro',
|
||||
outputDir: `dist/${options.appProjectRoot}`,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { join } from 'path';
|
||||
import { easCliVersion } from '../../../utils/versions';
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
|
||||
export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
@ -23,7 +22,6 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
||||
packageManager,
|
||||
packageLockFile,
|
||||
easCliVersion,
|
||||
});
|
||||
if (options.unitTestRunner === 'none') {
|
||||
host.delete(join(options.appProjectRoot, `App.spec.tsx`));
|
||||
|
||||
@ -2,7 +2,7 @@ import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
import { Schema } from '../schema';
|
||||
import { normalizeOptions } from './normalize-options';
|
||||
import { NormalizedSchema, normalizeOptions } from './normalize-options';
|
||||
|
||||
describe('Normalize Options', () => {
|
||||
let appTree: Tree;
|
||||
@ -36,7 +36,10 @@ describe('Normalize Options', () => {
|
||||
unitTestRunner: 'jest',
|
||||
skipFormat: false,
|
||||
js: true,
|
||||
});
|
||||
rootProject: false,
|
||||
e2eProjectName: 'my-app-e2e',
|
||||
e2eProjectRoot: 'my-app-e2e',
|
||||
} as NormalizedSchema);
|
||||
});
|
||||
|
||||
it('should normalize options with name in camel case', async () => {
|
||||
@ -64,7 +67,10 @@ describe('Normalize Options', () => {
|
||||
skipFormat: false,
|
||||
js: true,
|
||||
unitTestRunner: 'jest',
|
||||
});
|
||||
rootProject: false,
|
||||
e2eProjectName: 'myApp-e2e',
|
||||
e2eProjectRoot: 'myApp-e2e',
|
||||
} as NormalizedSchema);
|
||||
});
|
||||
|
||||
it('should normalize options with directory', async () => {
|
||||
@ -94,7 +100,10 @@ describe('Normalize Options', () => {
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
js: true,
|
||||
});
|
||||
rootProject: false,
|
||||
e2eProjectName: 'my-app-e2e',
|
||||
e2eProjectRoot: 'directory-e2e',
|
||||
} as NormalizedSchema);
|
||||
});
|
||||
|
||||
it('should normalize options that has directory in its name', async () => {
|
||||
@ -122,7 +131,10 @@ describe('Normalize Options', () => {
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
js: true,
|
||||
});
|
||||
rootProject: false,
|
||||
e2eProjectName: 'my-app-e2e',
|
||||
e2eProjectRoot: 'directory/my-app-e2e',
|
||||
} as NormalizedSchema);
|
||||
});
|
||||
|
||||
it('should normalize options with display name', async () => {
|
||||
@ -151,6 +163,9 @@ describe('Normalize Options', () => {
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
js: true,
|
||||
});
|
||||
rootProject: false,
|
||||
e2eProjectName: 'my-app-e2e',
|
||||
e2eProjectRoot: 'my-app-e2e',
|
||||
} as NormalizedSchema);
|
||||
});
|
||||
});
|
||||
|
||||
@ -8,6 +8,9 @@ export interface NormalizedSchema extends Schema {
|
||||
appProjectRoot: string;
|
||||
lowerCaseName: string;
|
||||
parsedTags: string[];
|
||||
rootProject: boolean;
|
||||
e2eProjectName: string;
|
||||
e2eProjectRoot: string;
|
||||
}
|
||||
|
||||
export async function normalizeOptions(
|
||||
@ -32,11 +35,14 @@ export async function normalizeOptions(
|
||||
const parsedTags = options.tags
|
||||
? options.tags.split(',').map((s) => s.trim())
|
||||
: [];
|
||||
const rootProject = appProjectRoot === '.';
|
||||
const e2eProjectName = rootProject ? 'e2e' : `${appProjectName}-e2e`;
|
||||
const e2eProjectRoot = rootProject ? 'e2e' : `${appProjectRoot}-e2e`;
|
||||
|
||||
return {
|
||||
...options,
|
||||
unitTestRunner: options.unitTestRunner || 'jest',
|
||||
e2eTestRunner: options.e2eTestRunner || 'detox',
|
||||
e2eTestRunner: options.e2eTestRunner,
|
||||
name: projectNames.projectSimpleName,
|
||||
className,
|
||||
lowerCaseName: className.toLowerCase(),
|
||||
@ -44,5 +50,8 @@ export async function normalizeOptions(
|
||||
projectName: appProjectName,
|
||||
appProjectRoot,
|
||||
parsedTags,
|
||||
rootProject,
|
||||
e2eProjectName,
|
||||
e2eProjectRoot,
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ export interface Schema {
|
||||
js: boolean; // default is false
|
||||
linter: Linter; // default is eslint
|
||||
setParserOptionsProject?: boolean; // default is false
|
||||
e2eTestRunner: 'detox' | 'none'; // default is detox
|
||||
e2eTestRunner: 'cypress' | 'playwright' | 'detox' | 'none'; // default is cypress
|
||||
standaloneConfig?: boolean;
|
||||
skipPackageJson?: boolean; // default is false
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
@ -75,8 +75,8 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "Adds the specified e2e test runner",
|
||||
"type": "string",
|
||||
"enum": ["detox", "none"],
|
||||
"default": "detox"
|
||||
"enum": ["cypress", "playwright", "detox", "none"],
|
||||
"default": "cypress"
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts';
|
||||
import { createNodes, ExpoPluginOptions } from '../../../plugins/plugin';
|
||||
import {
|
||||
easCliVersion,
|
||||
expoCliVersion,
|
||||
expoVersion,
|
||||
nxVersion,
|
||||
@ -20,6 +19,7 @@ import {
|
||||
reactVersion,
|
||||
} from '../../utils/versions';
|
||||
import { hasExpoPlugin } from '../../utils/has-expo-plugin';
|
||||
|
||||
import { addGitIgnoreEntry } from './lib/add-git-ignore-entry';
|
||||
import { Schema } from './schema';
|
||||
|
||||
@ -59,7 +59,6 @@ export function updateDependencies(host: Tree, schema: Schema) {
|
||||
{
|
||||
'@nx/expo': nxVersion,
|
||||
'@expo/cli': expoCliVersion,
|
||||
'eas-cli': easCliVersion,
|
||||
},
|
||||
undefined,
|
||||
schema.keepExistingVersions
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
|
||||
return {
|
||||
presets: [
|
||||
[
|
||||
'@nx/react/babel',
|
||||
{
|
||||
runtime: 'automatic',
|
||||
useBuiltIns: 'usage',
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [],
|
||||
env: {
|
||||
test: {
|
||||
presets: ['babel-preset-expo'],
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -1,17 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": ["babel-preset-expo"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-detox-app-json';
|
||||
|
||||
describe('add-eas-update-target', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'product', {
|
||||
root: 'apps/product',
|
||||
sourceRoot: 'apps/product/src',
|
||||
targets: {
|
||||
start: {
|
||||
executor: '@nrwl/expo:start',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write('apps/product/app.json', '{"expo": {}}');
|
||||
});
|
||||
|
||||
it(`should update app.json with plugin detox`, async () => {
|
||||
await update(tree);
|
||||
|
||||
const appJson = JSON.parse(tree.read('apps/product/app.json').toString());
|
||||
expect(appJson).toEqual({
|
||||
expo: {
|
||||
plugins: [
|
||||
[
|
||||
'@config-plugins/detox',
|
||||
{
|
||||
skipProguard: false,
|
||||
subdomains: ['10.0.2.2', 'localhost'],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
import { Tree, formatFiles, getProjects, updateJson } from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Add detox plugin to app.json for expo
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((config) => {
|
||||
if (
|
||||
config.targets?.['start']?.executor === '@nrwl/expo:start' ||
|
||||
config.targets?.['start']?.executor === '@nx/expo:start'
|
||||
) {
|
||||
updateJson(tree, `${config.root}/app.json`, (json) => {
|
||||
if (!json.expo.plugins) {
|
||||
json.expo.plugins = [];
|
||||
}
|
||||
json.expo.plugins.push([
|
||||
'@config-plugins/detox',
|
||||
{
|
||||
skipProguard: false,
|
||||
subdomains: ['10.0.2.2', 'localhost'],
|
||||
},
|
||||
]);
|
||||
return json;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -36,7 +36,6 @@ const content = `
|
||||
const { withNxMetro } = require('@nx/expo');
|
||||
const { getDefaultConfig } = require('@expo/metro-config');
|
||||
const { mergeConfig } = require('metro-config');
|
||||
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
||||
|
||||
const defaultConfig = getDefaultConfig(__dirname);
|
||||
const { assetExts, sourceExts } = defaultConfig.resolver;
|
||||
@ -54,9 +53,6 @@ const customConfig = {
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
blockList: exclusionList([/^(?!.*node_modules).*\\/dist\\/.*/]),
|
||||
unstable_enableSymlinks: true,
|
||||
unstable_enablePackageExports: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import { readJson, Tree, updateJson } from '@nx/devkit';
|
||||
import {
|
||||
formatFiles,
|
||||
removeDependenciesFromPackageJson,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Remove @types/react-native package since it is no longer required. It would be a part of react native package.
|
||||
@ -6,17 +10,6 @@ import { readJson, Tree, updateJson } from '@nx/devkit';
|
||||
* @returns
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
|
||||
if (
|
||||
!packageJson.devDependencies['@types/react-native'] ||
|
||||
!packageJson.dependencies['react-native']
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateJson(tree, 'package.json', (packageJson) => {
|
||||
delete packageJson.devDependencies['@types/react-native'];
|
||||
return packageJson;
|
||||
});
|
||||
removeDependenciesFromPackageJson(tree, [], ['@types/react-native']);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { easCliVersion } from '../../utils/versions';
|
||||
import { Tree, getProjects, updateJson } from '@nx/devkit';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
@ -14,7 +7,7 @@ export default async function update(tree: Tree) {
|
||||
if (config.targets?.['start']?.executor === '@nx/expo:start') {
|
||||
updateJson(tree, `${config.root}/eas.json`, (easJson) => {
|
||||
if (easJson?.cli?.version) {
|
||||
easJson.cli.version = `>= ${easCliVersion.replace('~', '')}`;
|
||||
easJson.cli.version = `>= 5`;
|
||||
}
|
||||
return easJson;
|
||||
});
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './change-outputDir-export-target';
|
||||
|
||||
describe('change-outputDir-export-target', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'product', {
|
||||
root: 'apps/product',
|
||||
sourceRoot: 'apps/product/src',
|
||||
targets: {
|
||||
export: {
|
||||
executor: '@nx/expo:export',
|
||||
options: {
|
||||
platform: 'all',
|
||||
outputDir: '../../dist/apps/dogs',
|
||||
},
|
||||
dependsOn: ['sync-deps'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should remove offset from outputDir`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['export']).toEqual({
|
||||
dependsOn: ['sync-deps'],
|
||||
executor: '@nx/expo:export',
|
||||
options: {
|
||||
outputDir: 'dist/apps/dogs',
|
||||
platform: 'all',
|
||||
},
|
||||
outputs: ['{options.outputDir}'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,29 @@
|
||||
import {
|
||||
Tree,
|
||||
getProjects,
|
||||
offsetFromRoot,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Remove the offset from the outputDir of the export target
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [projectName, config] of projects.entries()) {
|
||||
if (config.targets?.['export']?.executor === '@nx/expo:export') {
|
||||
const target = config.targets['export'];
|
||||
if (target.options?.outputDir) {
|
||||
const offset = offsetFromRoot(config.root);
|
||||
target.options.outputDir = target.options.outputDir.replace(offset, '');
|
||||
target.outputs = ['{options.outputDir}'];
|
||||
updateProjectConfiguration(tree, projectName, config);
|
||||
}
|
||||
}
|
||||
if (config.targets?.['export-web']?.executor === '@nx/expo:export') {
|
||||
delete config.targets['export-web'];
|
||||
updateProjectConfiguration(tree, projectName, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Tree, addProjectConfiguration } from '@nx/devkit';
|
||||
import update from './remove-block-list';
|
||||
|
||||
describe('remove-block-list', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'product', {
|
||||
root: 'apps/product',
|
||||
targets: {
|
||||
start: {
|
||||
executor: '@nx/expo:start',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write(
|
||||
'apps/product/metro.config.js',
|
||||
`
|
||||
const { withNxMetro } = require('@nx/expo');
|
||||
const { getDefaultConfig } = require('@expo/metro-config');
|
||||
const { mergeConfig } = require('metro-config');
|
||||
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
||||
|
||||
const defaultConfig = getDefaultConfig(__dirname);
|
||||
const { assetExts, sourceExts } = defaultConfig.resolver;
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
* https://facebook.github.io/metro/docs/configuration
|
||||
*
|
||||
* @type {import('metro-config').MetroConfig}
|
||||
*/
|
||||
const customConfig = {
|
||||
transformer: {
|
||||
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
|
||||
unstable_enableSymlinks: true,
|
||||
unstable_enablePackageExports: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
|
||||
// Change this to true to see debugging info.
|
||||
// Useful if you have issues resolving modules
|
||||
debug: false,
|
||||
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
|
||||
extensions: [],
|
||||
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
|
||||
watchFolders: [],
|
||||
});`
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove blockList', async () => {
|
||||
await update(tree);
|
||||
const metroConfig = tree.read(`apps/product/metro.config.js`).toString();
|
||||
expect(metroConfig).not.toContain('blockList');
|
||||
expect(metroConfig).not.toContain('unstable_enableSymlinks');
|
||||
expect(metroConfig).not.toContain('unstable_enablePackageExports');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,32 @@
|
||||
import { Tree, formatFiles, getProjects, joinPathFragments } from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* This migration remove blockList in metro.config.js.
|
||||
* It is now excluding dist folder in watchFolders in withNxMetro.
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [_, config] of projects.entries()) {
|
||||
if (config.targets?.['start']?.executor === '@nx/expo:start') {
|
||||
if (tree.exists(joinPathFragments(config.root, 'metro.config.js'))) {
|
||||
let content = tree
|
||||
.read(joinPathFragments(config.root, 'metro.config.js'))
|
||||
.toString();
|
||||
content = content.replace(
|
||||
`blockList: exclusionList([/^(?!.*node_modules).*/dist/.*/]),`,
|
||||
''
|
||||
);
|
||||
content = content.replace('unstable_enableSymlinks: true,', '');
|
||||
content = content.replace('unstable_enablePackageExports: true,', '');
|
||||
content = content.replace(
|
||||
`const exclusionList = require('metro-config/src/defaults/exclusionList');`,
|
||||
''
|
||||
);
|
||||
|
||||
tree.write(joinPathFragments(config.root, 'metro.config.js'), content);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
packages/expo/src/migrations/update-18-0-0/remove-eas-cli.ts
Normal file
24
packages/expo/src/migrations/update-18-0-0/remove-eas-cli.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {
|
||||
formatFiles,
|
||||
removeDependenciesFromPackageJson,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Remove eas-cli from dev dependencies.
|
||||
* Use globally eas-cli.
|
||||
*
|
||||
* Remove metro and metro-resolver from dev dependencies.
|
||||
* react-native has dependency of @react-native/community-cli-plugin
|
||||
* @react-native/community-cli-plugin has dependency of metro
|
||||
* @param tree
|
||||
* @returns
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
removeDependenciesFromPackageJson(
|
||||
tree,
|
||||
[],
|
||||
['eas-cli', 'metro', 'metro-resolver']
|
||||
);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './remove-symlink-target';
|
||||
|
||||
describe('remove-symlink-target', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'product', {
|
||||
root: 'apps/product',
|
||||
sourceRoot: 'apps/product/src',
|
||||
targets: {
|
||||
'ensure-symlink': {
|
||||
executor: '@nx/expo:ensure-symlink',
|
||||
options: {},
|
||||
},
|
||||
export: {
|
||||
executor: '@nx/expo:export',
|
||||
options: {
|
||||
platform: 'all',
|
||||
outputDir: '../../dist/apps/dogs',
|
||||
},
|
||||
dependsOn: ['ensure-symlink', 'sync-deps'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should remove ensure-symlink target from project.json`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['ensure-symlink']).toBeUndefined();
|
||||
expect(project.targets['export'].dependsOn).toEqual(['sync-deps']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,43 @@
|
||||
import {
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { removeSync } from 'fs-extra';
|
||||
|
||||
/**
|
||||
* Remove ensure-symlink target.
|
||||
* It is going to be supported by react-native version 0.73 by default.
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [projectName, config] of projects.entries()) {
|
||||
if (
|
||||
config.targets?.['ensure-symlink']?.executor === '@nx/expo:ensure-symlink'
|
||||
) {
|
||||
removeTargets(config.targets, 'ensure-symlink');
|
||||
updateProjectConfiguration(tree, projectName, config);
|
||||
removeSync(`${config.root}/node_modules`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeTargets(
|
||||
targets: {
|
||||
[targetName: string]: TargetConfiguration<any>;
|
||||
},
|
||||
targetNameToRemove: string
|
||||
) {
|
||||
for (const targetName in targets) {
|
||||
if (targetName === targetNameToRemove) {
|
||||
delete targets[targetName];
|
||||
}
|
||||
if (targets[targetName]?.dependsOn?.length) {
|
||||
targets[targetName].dependsOn = targets[targetName].dependsOn.filter(
|
||||
(dependsOn) => dependsOn !== targetNameToRemove
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,19 +25,28 @@ export async function addJest(
|
||||
});
|
||||
|
||||
// overwrite the jest.config.ts file because react native needs to have special transform property
|
||||
// use preset from https://github.com/expo/expo/blob/main/packages/jest-expo/jest-preset.js
|
||||
const configPath = `${appProjectRoot}/jest.config.${js ? 'js' : 'ts'}`;
|
||||
const content = `module.exports = {
|
||||
displayName: '${projectName}',
|
||||
resolver: '@nx/jest/plugins/resolver',
|
||||
preset: 'jest-expo',
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
|
||||
setupFilesAfterEnv: ['<rootDir>/test-setup.${js ? 'js' : 'ts'}'],
|
||||
moduleNameMapper: {
|
||||
'\\\\.svg$': '@nx/expo/plugins/jest/svg-mock'
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
'\\.[jt]sx?$': [
|
||||
'babel-jest',
|
||||
{
|
||||
configFile: __dirname + '/.babelrc.js',
|
||||
},
|
||||
],
|
||||
'^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp|ttf|otf|m4v|mov|mp4|mpeg|mpg|webm|aac|aiff|caf|m4a|mp3|wav|html|pdf|obj)$': require.resolve(
|
||||
'jest-expo/src/preset/assetFileTransformer.js'
|
||||
),
|
||||
},
|
||||
};`;
|
||||
host.write(configPath, content);
|
||||
|
||||
|
||||
@ -6,10 +6,10 @@ import {
|
||||
import {
|
||||
babelPresetExpoVersion,
|
||||
expoMetroConfigVersion,
|
||||
expoMetroRuntimeVersion,
|
||||
expoSplashScreenVersion,
|
||||
expoStatusBarVersion,
|
||||
jestExpoVersion,
|
||||
metroVersion,
|
||||
reactNativeSvgTransformerVersion,
|
||||
reactNativeSvgVersion,
|
||||
reactNativeWebVersion,
|
||||
@ -27,13 +27,12 @@ export function ensureDependencies(host: Tree): GeneratorCallback {
|
||||
'expo-status-bar': expoStatusBarVersion,
|
||||
'react-native-web': reactNativeWebVersion,
|
||||
'@expo/metro-config': expoMetroConfigVersion,
|
||||
'@expo/metro-runtime': expoMetroRuntimeVersion,
|
||||
'react-native-svg-transformer': reactNativeSvgTransformerVersion,
|
||||
'react-native-svg': reactNativeSvgVersion,
|
||||
},
|
||||
{
|
||||
'@types/react': typesReactVersion,
|
||||
metro: metroVersion,
|
||||
'metro-resolver': metroVersion,
|
||||
'react-test-renderer': reactTestRendererVersion,
|
||||
'@testing-library/react-native': testingLibraryReactNativeVersion,
|
||||
'@testing-library/jest-native': testingLibraryJestNativeVersion,
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
import { findAllNpmDependencies } from './find-all-npm-dependencies';
|
||||
import { DependencyType, ProjectGraph } from '@nx/devkit';
|
||||
|
||||
test('findAllNpmDependencies', () => {
|
||||
const graph: ProjectGraph = {
|
||||
nodes: {
|
||||
myapp: {
|
||||
type: 'app',
|
||||
name: 'myapp',
|
||||
data: { files: [] },
|
||||
},
|
||||
lib1: {
|
||||
type: 'lib',
|
||||
name: 'lib1',
|
||||
data: { files: [] },
|
||||
},
|
||||
lib2: {
|
||||
type: 'lib',
|
||||
name: 'lib2',
|
||||
data: { files: [] },
|
||||
},
|
||||
lib3: {
|
||||
type: 'lib',
|
||||
name: 'lib3',
|
||||
data: { files: [] },
|
||||
},
|
||||
} as any,
|
||||
externalNodes: {
|
||||
'npm:react-native-image-picker': {
|
||||
type: 'npm',
|
||||
name: 'npm:react-native-image-picker',
|
||||
data: {
|
||||
version: '1',
|
||||
packageName: 'react-native-image-picker',
|
||||
},
|
||||
},
|
||||
'npm:react-native-dialog': {
|
||||
type: 'npm',
|
||||
name: 'npm:react-native-dialog',
|
||||
data: {
|
||||
version: '1',
|
||||
packageName: 'react-native-dialog',
|
||||
},
|
||||
},
|
||||
'npm:react-native-snackbar': {
|
||||
type: 'npm',
|
||||
name: 'npm:react-native-snackbar',
|
||||
data: {
|
||||
version: '1',
|
||||
packageName: 'react-native-snackbar',
|
||||
},
|
||||
},
|
||||
'npm:@nx/react-native': {
|
||||
type: 'npm',
|
||||
name: 'npm:@nx/react-native',
|
||||
data: {
|
||||
version: '1',
|
||||
packageName: '@nx/react-native',
|
||||
},
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
myapp: [
|
||||
{ type: DependencyType.static, source: 'myapp', target: 'lib1' },
|
||||
{ type: DependencyType.static, source: 'myapp', target: 'lib2' },
|
||||
{
|
||||
type: DependencyType.static,
|
||||
source: 'myapp',
|
||||
target: 'npm:react-native-image-picker',
|
||||
},
|
||||
{
|
||||
type: DependencyType.static,
|
||||
source: 'myapp',
|
||||
target: 'npm:@nx/react-native',
|
||||
},
|
||||
],
|
||||
lib1: [
|
||||
{ type: DependencyType.static, source: 'lib1', target: 'lib2' },
|
||||
{
|
||||
type: DependencyType.static,
|
||||
source: 'lib3',
|
||||
target: 'npm:react-native-snackbar',
|
||||
},
|
||||
],
|
||||
lib2: [{ type: DependencyType.static, source: 'lib2', target: 'lib3' }],
|
||||
lib3: [
|
||||
{
|
||||
type: DependencyType.static,
|
||||
source: 'lib3',
|
||||
target: 'npm:react-native-dialog',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = findAllNpmDependencies(graph, 'myapp');
|
||||
|
||||
expect(result).toEqual([
|
||||
'react-native-dialog',
|
||||
'react-native-snackbar',
|
||||
'react-native-image-picker',
|
||||
]);
|
||||
});
|
||||
@ -1,33 +0,0 @@
|
||||
import { ProjectGraph } from '@nx/devkit';
|
||||
|
||||
export function findAllNpmDependencies(
|
||||
graph: ProjectGraph,
|
||||
projectName: string,
|
||||
list: string[] = [],
|
||||
seen = new Set<string>()
|
||||
) {
|
||||
// In case of bad circular dependencies
|
||||
if (seen.has(projectName)) {
|
||||
return list;
|
||||
}
|
||||
seen.add(projectName);
|
||||
|
||||
const node = graph.externalNodes[projectName];
|
||||
|
||||
// Don't want to include '@nx/react-native' because React Native
|
||||
// autolink will warn that the package has no podspec file for iOS.
|
||||
if (node) {
|
||||
if (
|
||||
node.name !== `npm:@nx/react-native` &&
|
||||
node.name !== `npm:@nrwl/react-native`
|
||||
) {
|
||||
list.push(node.data.packageName);
|
||||
}
|
||||
} else {
|
||||
// it's workspace project, search for it's dependencies
|
||||
graph.dependencies[projectName]?.forEach((dep) =>
|
||||
findAllNpmDependencies(graph, dep.target, list, seen)
|
||||
);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
37
packages/expo/src/utils/resolve-eas.ts
Normal file
37
packages/expo/src/utils/resolve-eas.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export function resolveEas(workspaceRoot: string): string {
|
||||
try {
|
||||
execSync('eas --version');
|
||||
} catch {
|
||||
throw new Error(
|
||||
'EAS is not installed. Please run `npm install --global eas-cli` or `yarn global add eas-cli`.'
|
||||
);
|
||||
}
|
||||
|
||||
let npmGlobalPath: string, yarnGlobalPath: string;
|
||||
try {
|
||||
npmGlobalPath = execSync('npm root -g')
|
||||
?.toString()
|
||||
?.trim()
|
||||
?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
|
||||
} catch {}
|
||||
try {
|
||||
yarnGlobalPath = execSync('yarn global dir')
|
||||
?.toString()
|
||||
?.trim()
|
||||
?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
return require.resolve('eas-cli/bin/run', {
|
||||
paths: [npmGlobalPath, yarnGlobalPath, workspaceRoot].filter(
|
||||
Boolean
|
||||
) as string[],
|
||||
});
|
||||
} catch {
|
||||
throw new Error(
|
||||
'Can not resolve EAS. Please run `npm install --global eas-cli` or `yarn global add eas-cli`.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import { ensureNodeModulesSymlink } from './ensure-node-modules-symlink';
|
||||
import * as chalk from 'chalk';
|
||||
import { GeneratorCallback, logger } from '@nx/devkit';
|
||||
|
||||
export function runSymlink(
|
||||
workspaceRoot: string,
|
||||
projectRoot: string
|
||||
): GeneratorCallback {
|
||||
return () => {
|
||||
logger.info(`creating symlinks for ${chalk.bold(projectRoot)}`);
|
||||
try {
|
||||
ensureNodeModulesSymlink(workspaceRoot, projectRoot);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Failed to create symlinks for ${chalk.bold(projectRoot)}`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,26 +1,25 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const expoVersion = '49.0.16';
|
||||
export const expoMetroConfigVersion = '~0.10.7';
|
||||
export const expoSplashScreenVersion = '~0.20.5';
|
||||
export const expoStatusBarVersion = '~1.6.0';
|
||||
export const expoCliVersion = '~0.10.13'; // @expo/cli
|
||||
export const easCliVersion = '~5.2.0';
|
||||
export const babelPresetExpoVersion = '~9.5.2';
|
||||
export const expoVersion = '~50.0.3';
|
||||
export const expoSplashScreenVersion = '~0.26.1';
|
||||
export const expoStatusBarVersion = '~1.11.1';
|
||||
export const expoCliVersion = '~0.16.5'; // @expo/cli
|
||||
export const babelPresetExpoVersion = '~10.0.0';
|
||||
|
||||
export const reactVersion = '18.2.0';
|
||||
export const reactDomVersion = '18.2.0';
|
||||
export const reactTestRendererVersion = '18.2.0';
|
||||
export const typesReactVersion = '18.0.28';
|
||||
export const typesReactVersion = '~18.2.45';
|
||||
|
||||
export const reactNativeVersion = '0.72.6';
|
||||
export const reactNativeVersion = '0.73.2';
|
||||
export const reactNativeWebVersion = '~0.19.9';
|
||||
|
||||
export const reactNativeSvgTransformerVersion = '1.0.0';
|
||||
export const reactNativeSvgVersion = '13.9.0';
|
||||
export const expoMetroConfigVersion = '~0.17.3';
|
||||
export const expoMetroRuntimeVersion = '~3.1.1';
|
||||
|
||||
export const metroVersion = '0.76.8';
|
||||
export const reactNativeSvgTransformerVersion = '1.2.0';
|
||||
export const reactNativeSvgVersion = '14.1.0';
|
||||
|
||||
export const testingLibraryReactNativeVersion = '~12.3.0';
|
||||
export const testingLibraryReactNativeVersion = '~12.4.2';
|
||||
export const testingLibraryJestNativeVersion = '~5.4.3';
|
||||
export const jestExpoVersion = '~49.0.0';
|
||||
export const jestExpoVersion = '~50.0.1';
|
||||
|
||||
@ -32,7 +32,12 @@
|
||||
"react-native",
|
||||
// These are in ensurePackage
|
||||
"@nx/rollup",
|
||||
"@nx/storybook"
|
||||
"@nx/storybook",
|
||||
"@nx/vite",
|
||||
"@nx/webpack",
|
||||
"@nx/detox",
|
||||
"@nx/cypress",
|
||||
"@nx/playwright"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -49,6 +49,11 @@
|
||||
"implementation": "./src/executors/pod-install/pod-install.impl",
|
||||
"schema": "./src/executors/pod-install/schema.json",
|
||||
"description": "Run `pod install` in the `ios` directory."
|
||||
},
|
||||
"upgrade": {
|
||||
"implementation": "./src/executors/upgrade/upgrade.impl",
|
||||
"schema": "./src/executors/upgrade/schema.json",
|
||||
"description": "upgrade executor"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init#reactNativeInitGenerator",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Initialize the `@nrwl/react-native` plugin.",
|
||||
"description": "Initialize the `@nx/react-native` plugin.",
|
||||
"hidden": true
|
||||
},
|
||||
"application": {
|
||||
@ -32,26 +32,27 @@
|
||||
"storybook-configuration": {
|
||||
"factory": "./src/generators/storybook-configuration/configuration#storybookConfigurationGenerator",
|
||||
"schema": "./src/generators/storybook-configuration/schema.json",
|
||||
"description": "Set up Storybook for a React-native application or library.",
|
||||
"hidden": false
|
||||
"description": "Set up Storybook for a React Native application or library."
|
||||
},
|
||||
"component-story": {
|
||||
"factory": "./src/generators/component-story/component-story#componentStoryGenerator",
|
||||
"schema": "./src/generators/component-story/schema.json",
|
||||
"description": "Generate Storybook story for a React-native component.",
|
||||
"hidden": false
|
||||
"description": "Generate Storybook story for a React Native component."
|
||||
},
|
||||
"stories": {
|
||||
"factory": "./src/generators/stories/stories#storiesGenerator",
|
||||
"schema": "./src/generators/stories/schema.json",
|
||||
"description": "Create stories/specs for all components declared in an application or library.",
|
||||
"hidden": false
|
||||
"description": "Create stories for all components declared in an application or library."
|
||||
},
|
||||
"upgrade-native": {
|
||||
"factory": "./src/generators/upgrade-native/upgrade-native#reactNativeUpgradeNativeGenerator",
|
||||
"schema": "./src/generators/upgrade-native/schema.json",
|
||||
"description": "Destructive command to upgrade native iOS and Android code to latest.",
|
||||
"hidden": false
|
||||
"description": "Destructive command to upgrade native iOS and Android code to latest."
|
||||
},
|
||||
"web-configuration": {
|
||||
"factory": "./src/generators/web-configuration/web-configuration#webConfigurationGenerator",
|
||||
"schema": "./src/generators/web-configuration/schema.json",
|
||||
"description": "Set up web configuration for a React Native app"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,6 +41,42 @@
|
||||
"version": "16.9.0-beta.1",
|
||||
"description": "Remove @types/react-native from package.json",
|
||||
"implementation": "./src/migrations/update-16-9-0/remove-types-react-native"
|
||||
},
|
||||
"update-18-0-0-add-web-configuration": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Add web configuration to react native projects",
|
||||
"implementation": "./src/migrations/update-18-0-0/add-web-configuration"
|
||||
},
|
||||
"update-18-0-0-change-storybook-targets": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Upgrade react native storybook target to use web",
|
||||
"implementation": "./src/migrations/update-18-0-0/change-storybook-targets"
|
||||
},
|
||||
"update-18-0-0-remove-block-list": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove blockList in metro.config.js.",
|
||||
"implementation": "./src/migrations/update-18-0-0/remove-block-list"
|
||||
},
|
||||
"update-18-0-0-remove-metro": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove metro-* and @react-native-community/cli-* from package.json devDependencies",
|
||||
"implementation": "./src/migrations/update-18-0-0/remove-metro"
|
||||
},
|
||||
"update-18-0-0-remove-symlink-target": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Remove ensure-symlink target",
|
||||
"implementation": "./src/migrations/update-18-0-0/remove-symlink-target"
|
||||
},
|
||||
"update-18-0-0-add-upgrade-target": {
|
||||
"cli": "nx",
|
||||
"version": "18.0.0-beta.0",
|
||||
"description": "Add upgrade target to react native projects",
|
||||
"implementation": "./src/migrations/update-18-0-0/add-upgrade-target"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
@ -551,7 +587,43 @@
|
||||
"version": "17.3.0-beta.3",
|
||||
"packages": {
|
||||
"@types/node": {
|
||||
"version": "18.16.9",
|
||||
"version": "18.16.9"
|
||||
}
|
||||
}
|
||||
},
|
||||
"18.0.0": {
|
||||
"version": "18.0.0-beta.0",
|
||||
"packages": {
|
||||
"react-native": {
|
||||
"version": "0.73.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@react-native/babel-preset": {
|
||||
"version": "^0.73.18",
|
||||
"addToPackageJson": "devDependencies"
|
||||
},
|
||||
"@react-native/metro-config": {
|
||||
"version": "^0.73.2",
|
||||
"addToPackageJson": "devDependencies"
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "~18.2.45",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/react-native": {
|
||||
"version": "~12.4.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg-transformer": {
|
||||
"version": "1.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "14.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@react-native-community/cli-platform-android": {
|
||||
"version": "12.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,13 +30,11 @@
|
||||
"fs-extra": "^11.1.0",
|
||||
"glob": "7.1.4",
|
||||
"ignore": "^5.0.4",
|
||||
"metro-config": "~0.76.8",
|
||||
"metro-resolver": "~0.76.8",
|
||||
"minimatch": "9.0.3",
|
||||
"metro-config": "~0.80.4",
|
||||
"metro-resolver": "~0.80.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"tslib": "^2.3.0",
|
||||
"@nx/detox": "file:../detox",
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/jest": "file:../jest",
|
||||
"@nx/js": "file:../js",
|
||||
@ -44,9 +42,6 @@
|
||||
"@nx/react": "file:../react",
|
||||
"@nx/workspace": "file:../workspace"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": ">= 0.72.0 < 0.73.0"
|
||||
},
|
||||
"executors": "./executors.json",
|
||||
"ng-update": {
|
||||
"requirements": {},
|
||||
|
||||
@ -45,6 +45,11 @@ export function getResolveRequest(extensions: string[]) {
|
||||
if (resolvedPath) {
|
||||
return resolvedPath;
|
||||
}
|
||||
if (debug) {
|
||||
console.log(
|
||||
chalk.red(`[Nx] Unable to resolve with any resolver: ${realModuleName}`)
|
||||
);
|
||||
}
|
||||
throw new Error(`Cannot resolve ${chalk.bold(realModuleName)}`);
|
||||
};
|
||||
}
|
||||
@ -53,7 +58,7 @@ function resolveRequestFromContext(
|
||||
resolveRequest: Function,
|
||||
context: any,
|
||||
realModuleName: string,
|
||||
platform: string,
|
||||
platform: string | null,
|
||||
debug: boolean
|
||||
) {
|
||||
try {
|
||||
@ -75,7 +80,7 @@ function resolveRequestFromContext(
|
||||
function defaultMetroResolver(
|
||||
context: any,
|
||||
realModuleName: string,
|
||||
platform: string,
|
||||
platform: string | null,
|
||||
debug: boolean
|
||||
) {
|
||||
try {
|
||||
@ -130,7 +135,7 @@ function tsconfigPathsResolver(
|
||||
context: any,
|
||||
extensions: string[],
|
||||
realModuleName: string,
|
||||
platform: string,
|
||||
platform: string | null,
|
||||
debug: boolean
|
||||
) {
|
||||
try {
|
||||
@ -145,7 +150,7 @@ function tsconfigPathsResolver(
|
||||
} catch {
|
||||
if (debug) {
|
||||
console.log(
|
||||
chalk.red(`[Nx] Failed to resolve ${chalk.bold(realModuleName)}`)
|
||||
chalk.cyan(`[Nx] Failed to resolve ${chalk.bold(realModuleName)}`)
|
||||
);
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { joinPathFragments, workspaceRoot } from '@nx/devkit';
|
||||
import { mergeConfig } from 'metro-config';
|
||||
import type { MetroConfig } from 'metro-config';
|
||||
import { existsSync } from 'fs-extra';
|
||||
import { existsSync, readdirSync, statSync } from 'fs-extra';
|
||||
|
||||
import { getResolveRequest } from './metro-resolver';
|
||||
|
||||
@ -19,16 +19,26 @@ export async function withNxMetro(
|
||||
if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true';
|
||||
if (opts.extensions) extensions.push(...opts.extensions);
|
||||
|
||||
let watchFolders = [workspaceRoot];
|
||||
let watchFolders = readdirSync(workspaceRoot)
|
||||
.filter(
|
||||
(fileName) =>
|
||||
!['dist', 'e2e'].includes(fileName) && !fileName.startsWith('.')
|
||||
)
|
||||
.map((fileName) => joinPathFragments(workspaceRoot, fileName))
|
||||
.filter((filePath) => statSync(filePath).isDirectory());
|
||||
|
||||
if (opts.watchFolders?.length) {
|
||||
watchFolders = watchFolders.concat(opts.watchFolders);
|
||||
}
|
||||
|
||||
watchFolders = watchFolders.filter((folder) => existsSync(folder));
|
||||
watchFolders = [...new Set(watchFolders)].filter((folder) =>
|
||||
existsSync(folder)
|
||||
);
|
||||
|
||||
const nxConfig: MetroConfig = {
|
||||
resolver: {
|
||||
resolveRequest: getResolveRequest(extensions),
|
||||
nodeModulesPaths: [joinPathFragments(workspaceRoot, 'node_modules')],
|
||||
},
|
||||
watchFolders,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user