ReactNative项目构建分析与思考之 cli-config

本文深入分析了React Native项目的构建过程,重点讲解了RN CLI的运行流程,包括`getReactNativeConfig`方法的执行,如何获取配置信息,以及`cli-config`命令的作用。通过`loadConfig`方法,解析`package.json`和`react-native.config.js`,获取项目的关键信息,如依赖、版本、平台配置等。此外,文章还阐述了`react-native-gradle-plugin`和`native-modules.gradle`在构建过程中的作用,以及前端CLI如何与Gradle脚本配合,实现第三方库的编译和集成。
摘要由CSDN通过智能技术生成

前言

在上一篇 native_modules.gradle分析 文章中,我们对三方库是如何依赖到Android项目有了一个大致的了解。
native_modules.gradle 的 getReactNativeConfig
方法中,执行了一个node命令,然后得到了非常关键的 reactNativeConfigOutput后续的流程才能正常走下去。

String[] reactNativeConfigCommand = ["node", cliPath, "config"]
def reactNativeConfigOutput = this.getCommandOutput(reactNativeConfigCommand, this.root)

那么,reactNativeConfigOutput 这个数据到底是怎么获取到的呢?
下面我们来重点看下前端CLI的逻辑。

RN CLI

reactNativeConfigCommand 这个方法入手,本质上就是执行了 一个node命令,如下

node xxx/node_modules/@react-native-community/cli/build/bin.js config

我们可以直接在项目中执行下该命令,看看结果是什么。

{
   
  "root": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis",
  "reactNativePath": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/node_modules/react-native",
  "reactNativeVersion": "0.73",
  "dependencies": {
   
    "react-native-device-info": {
   
      "root": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/node_modules/react-native-device-info",
      "name": "react-native-device-info",
      "platforms": {
   
        "ios": {
   
          "podspecPath": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/node_modules/react-native-device-info/RNDeviceInfo.podspec",
          "version": "10.12.0",
          "configurations": [],
          "scriptPhases": []
        },
        "android": {
   
          "sourceDir": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/node_modules/react-native-device-info/android",
          "packageImportPath": "import com.learnium.RNDeviceInfo.RNDeviceInfo;",
          "packageInstance": "new RNDeviceInfo()",
          "buildTypes": [],
          "componentDescriptors": [],
          "cmakeListsPath": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/node_modules/react-native-device-info/android/build/generated/source/codegen/jni/CMakeLists.txt"
        }
      }
    }
  },
  "commands": [
    {
   
      "name": "log-ios",
      "description": "starts iOS device syslog tail",
      "options": [
        {
   
          "name": "--interactive",
          "description": "Explicitly select simulator to tail logs from. By default it will tail logs from the first booted and available simulator."
        }
      ]
    },
    {
   
      "name": "run-ios",
      "description": "builds your app and starts it on iOS simulator",
      "examples": [
        {
   
          "desc": "Run on a different simulator, e.g. iPhone SE (2nd generation)",
          "cmd": "npx react-native run-ios --simulator \"iPhone SE (2nd generation)\""
        },
        {
   
          "desc": "Run on a connected device, e.g. Max's iPhone",
          "cmd": "npx react-native run-ios --device \"Max's iPhone\""
        },
        {
   
          "desc": "Run on the AppleTV simulator",
          "cmd": "npx react-native run-ios --simulator \"Apple TV\"  --scheme \"helloworld-tvOS\""
        }
      ],
      "options": [
        {
   
          "name": "--mode <string>",
          "description": "Explicitly set the scheme configuration to use. This option is case sensitive."
        },
        {
   
          "name": "--scheme <string>",
          "description": "Explicitly set Xcode scheme to use"
        },
        {
   
          "name": "--destination <string>",
          "description": "Explicitly extend destination e.g. \"arch=x86_64\""
        },
        {
   
          "name": "--verbose",
          "description": "Do not use xcbeautify or xcpretty even if installed"
        },
        {
   
          "name": "--xcconfig [string]",
          "description": "Explicitly set xcconfig to use"
        },
        {
   
          "name": "--buildFolder <string>",
          "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\"."
        },
        {
   
          "name": "--extra-params <string>",
          "description": "Custom params that will be passed to xcodebuild command."
        },
        {
   
          "name": "--target <string>",
          "description": "Explicitly set Xcode target to use."
        },
        {
   
          "name": "--interactive",
          "description": "Explicitly select which scheme and configuration to use before running a build"
        },
        {
   
          "name": "--force-pods",
          "description": "Force CocoaPods installation"
        },
        {
   
          "name": "--no-packager",
          "description": "Do not launch packager while running the app"
        },
        {
   
          "name": "--port <number>",
          "default": 8081
        },
        {
   
          "name": "--terminal <string>",
          "description": "Launches the Metro Bundler in a new window using the specified terminal path."
        },
        {
   
          "name": "--binary-path <string>",
          "description": "Path relative to project root where pre-built .app binary lives."
        },
        {
   
          "name": "--list-devices",
          "description": "List all available iOS devices and simulators and let you choose one to run the app. "
        },
        {
   
          "name": "--simulator <string>",
          "description": "Explicitly set the simulator to use. Optionally set the iOS version between parentheses at the end to match an exact version: \"iPhone 15 (17.0)\""
        },
        {
   
          "name": "--device <string>",
          "description": "Explicitly set the device to use by name. The value is not required if you have a single device connected."
        },
        {
   
          "name": "--udid <string>",
          "description": "Explicitly set the device to use by UDID"
        }
      ]
    },
    {
   
      "name": "build-ios",
      "description": "builds your app for iOS platform",
      "examples": [
        {
   
          "desc": "Build the app for all iOS devices in Release mode",
          "cmd": "npx react-native build-ios --mode \"Release\""
        }
      ],
      "options": [
        {
   
          "name": "--mode <string>",
          "description": "Explicitly set the scheme configuration to use. This option is case sensitive."
        },
        {
   
          "name": "--scheme <string>",
          "description": "Explicitly set Xcode scheme to use"
        },
        {
   
          "name": "--destination <string>",
          "description": "Explicitly extend destination e.g. \"arch=x86_64\""
        },
        {
   
          "name": "--verbose",
          "description": "Do not use xcbeautify or xcpretty even if installed"
        },
        {
   
          "name": "--xcconfig [string]",
          "description": "Explicitly set xcconfig to use"
        },
        {
   
          "name": "--buildFolder <string>",
          "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\"."
        },
        {
   
          "name": "--extra-params <string>",
          "description": "Custom params that will be passed to xcodebuild command."
        },
        {
   
          "name": "--target <string>",
          "description": "Explicitly set Xcode target to use."
        },
        {
   
          "name": "--interactive",
          "description": "Explicitly select which scheme and configuration to use before running a build"
        },
        {
   
          "name": "--force-pods",
          "description": "Force CocoaPods installation"
        }
      ]
    },
    {
   
      "name": "log-android",
      "description": "starts logkitty"
    },
    {
   
      "name": "run-android",
      "description": "builds your app and starts it on a connected Android emulator or device",
      "options": [
        {
   
          "name": "--mode <string>",
          "description": "Specify your app's build variant"
        },
        {
   
          "name": "--tasks <list>",
          "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments."
        },
        {
   
          "name": "--active-arch-only",
          "description": "Build native libraries only for the current device architecture for debug builds.",
          "default": false
        },
        {
   
          "name": "--extra-params <string>",
          "description": "Custom params passed to gradle build command"
        },
        {
   
          "name": "--interactive",
          "description": "Explicitly select build type and flavour to use before running a build"
        },
        {
   
          "name": "--no-packager",
          "description": "Do not launch packager while running the app"
        },
        {
   
          "name": "--port <number>",
          "default": 8081
        },
        {
   
          "name": "--terminal <string>",
          "description": "Launches the Metro Bundler in a new window using the specified terminal path."
        },
        {
   
          "name": "--appId <string>",
          "description": "Specify an applicationId to launch after build. If not specified, `package` from AndroidManifest.xml will be used.",
          "default": ""
        },
        {
   
          "name": "--appIdSuffix <string>",
          "description": "Specify an applicationIdSuffix to launch after build.",
          "default": ""
        },
        {
   
          "name": "--main-activity <string>",
          "description": "Name of the activity to start"
        },
        {
   
          "name": "--deviceId <string>",
          "description": "builds your app and starts it on a specific device/simulator with the given device id (listed by running \"adb devices\" on the command line)."
        },
        {
   
          "name": "--list-devices",
          "description": "Lists all available Android devices and simulators and let you choose one to run the app",
          "default": false
        },
        {
   
          "name": "--binary-path <string>",
          "description": "Path relative to project root where pre-built .apk binary lives."
        },
        {
   
          "name": "--user <number>",
          "description": "Id of the User Profile you want to install the app on."
        }
      ]
    },
    {
   
      "name": "build-android",
      "description": "builds your app",
      "options": [
        {
   
          "name": "--mode <string>",
          "description": "Specify your app's build variant"
        },
        {
   
          "name": "--tasks <list>",
          "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments."
        },
        {
   
          "name": "--active-arch-only",
          "description": "Build native libraries only for the current device architecture for debug builds.",
          "default": false
        },
        {
   
          "name": "--extra-params <string>",
          "description": "Custom params passed to gradle build command"
        },
        {
   
          "name": "--interactive",
          "description": "Explicitly select build type and flavour to use before running a build"
        }
      ]
    },
    {
   
      "name": "bundle",
      "description": "Build the bundle for the provided JavaScript entry file.",
      "options": [
        {
   
          "name": "--entry-file <path>",
          "description": "Path to the root JS file, either absolute or relative to JS root"
        },
        {
   
          "name": "--platform <string>",
          "description": "Either \"ios\" or \"android\"",
          "default": "ios"
        },
        {
   
          "name": "--transformer <string>",
          "description": "Specify a custom transformer to be used"
        },
        {
   
          "name": "--dev [boolean]",
          "description": "If false, warnings are disabled and the bundle is minified",
          "default": true
        },
        {
   
          "name": "--minify [boolean]",
          "description": "Allows overriding whether bundle is minified. This defaults to false if dev is true, and true if dev is false. Disabling minification can be useful for speeding up production builds for testing purposes."
        },
        {
   
          "name": "--bundle-output <string>",
          "description": "File name where to store the resulting bundle, ex. /tmp/groups.bundle"
        },
        {
   
          "name": "--bundle-encoding <string>",
          "description": "Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer).",
          "default": "utf8"
        },
        {
   
          "name": "--max-workers <number>",
          "description": "Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine."
        },
        {
   
          "name": "--sourcemap-output <string>",
          "description": "File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map"
        },
        {
   
          "name": "--sourcemap-sources-root <string>",
          "description": "Path to make sourcemap's sources entries relative to, ex. /root/dir"
        },
        {
   
          "name": "--sourcemap-use-absolute-path",
          "description": "Report SourceMapURL using its full path",
          "default": false
        },
        {
   
          "name": "--assets-dest <string>",
          "description": "Directory name where to store assets referenced in the bundle"
        },
        {
   
          "name": "--unstable-transform-profile <string>",
          "description": "Experimental, transform JS for a specific JS engine. Currently supported: hermes, hermes-canary, default",
          "default": "default"
        },
        {
   
          "name": "--asset-catalog-dest [string]",
          "description": "Path where to create an iOS Asset Catalog for images"
        },
        {
   
          "name": "--reset-cache",
          "description": "Removes cached files",
          "default": false
        },
        {
   
          "name": "--read-global-cache",
          "description": "Try to fetch transformed JS code from the global cache, if configured.",
          "default": false
        },
        {
   
          "name": "--config <string>",
          "description": "Path to the CLI configuration file"
        }
      ]
    },
    {
   
      "name": "ram-bundle",
      "description": "Build the RAM bundle for the provided JavaScript entry file. See https://reactnative.dev/docs/ram-bundles-inline-requires.",
      "options": [
        {
   
          "name": "--entry-file <path>",
          "description": "Path to the root JS file, either absolute or relative to JS root"
        },
        {
   
          "name": "--platform <string>",
          "description": "Either \"ios\" or \"android\"",
          "default": "ios"
        },
        {
   
          "name": "--transformer <string>",
          "description": "Specify a custom transformer to be used"
        },
        {
   
          "name": "--dev [boolean]",
          "description": "If false, warnings are disabled and the bundle is minified",
          "default": true
        },
        {
   
          "name": "--minify [boolean]",
          "description": "Allows overriding whether bundle is minified. This defaults to false if dev is true, and true if dev is false. Disabling minification can be useful for speeding up production builds for testing purposes."
        },
        {
   
          "name": "--bundle-output <string>",
          "description": "File name where to store the resulting bundle, ex. /tmp/groups.bundle"
        },
        {
   
          "name": "--bundle-encoding <string>",
          "description": "Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer).",
          "default": "utf8"
        },
        {
   
          "name": "--max-workers <number>",
          "description": "Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine."
        },
        {
   
          "name": "--sourcemap-output <string>",
          "description": "File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map"
        },
        {
   
          "name": "--sourcemap-sources-root <string>",
          "description": "Path to make sourcemap's sources entries relative to, ex. /root/dir"
        },
        {
   
          "name": "--sourcemap-use-absolute-path",
          "description": "Report SourceMapURL using its full path",
          "default": false
        },
        {
   
          "name": "--assets-dest <string>",
          "description": "Directory name where to store assets referenced in the bundle"
        },
        {
   
          "name": "--unstable-transform-profile <string>",
          "description": "Experimental, transform JS for a specific JS engine. Currently supported: hermes, hermes-canary, default",
          "default": "default"
        },
        {
   
          "name": "--asset-catalog-dest [string]",
          "description": "Path where to create an iOS Asset Catalog for images"
        },
        {
   
          "name": "--reset-cache",
          "description": "Removes cached files",
          "default": false
        },
        {
   
          "name": "--read-global-cache",
          "description": "Try to fetch transformed JS code from the global cache, if configured.",
          "default": false
        },
        {
   
          "name": "--config <string>",
          "description": "Path to the CLI configuration file"
        },
        {
   
          "name": "--indexed-ram-bundle",
          "description": "Force the \"Indexed RAM\" bundle file format, even when building for android",
          "default": false
        }
      ]
    },
    {
   
      "name": "start",
      "description": "Start the React Native development server.",
      "options": [
        {
   
          "name": "--port <number>"
        },
        {
   
          "name": "--host <string>",
          "default": ""
        },
        {
   
          "name": "--projectRoot <path>",
          "description": "Path to a custom project root"
        },
        {
   
          "name": "--watchFolders <list>",
          "description": "Specify any additional folders to be added to the watch list"
        },
        {
   
          "name": "--assetPlugins <list>",
          "description": "Specify any additional asset plugins to be used by the packager by full filepath"
        },
        {
   
          "name": "--sourceExts <list>",
          "description": "Specify any additional source extensions to be used by the packager"
        },
        {
   
          "name": "--max-workers <number>",
          "description": "Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine."
        },
        {
   
          "name": "--transformer <string>",
          "description": "Specify a custom transformer to be used"
        },
        {
   
          "name": "--reset-cache, --resetCache",
          "description": "Removes cached files"
        },
        {
   
          "name": "--custom-log-reporter-path, --customLogReporterPath <string>",
          "description": "Path to a JavaScript file that exports a log reporter as a replacement for TerminalReporter"
        },
        {
   
          "name": "--https",
          "description": "Enables https connections to the server"
        },
        {
   
          "name": "--key <path>",
          "description": "Path to custom SSL key"
        },
        {
   
          "name": "--cert <path>",
          "description": "Path to custom SSL cert"
        },
        {
   
          "name": "--config <string>",
          "description": "Path to the CLI configuration file"
        },
        {
   
          "name": "--no-interactive",
          "description": "Disables interactive mode"
        },
        {
   
          "name": "--experimental-debugger",
          "description": "[Experimental] Enable the new debugger experience and 'j' to debug. This enables the new frontend experience only: connection reliability and some basic features are unstable in this release."
        }
      ]
    }
  ],
  "healthChecks": [],
  "platforms": {
   
    "ios": {
   },
    "android": {
   }
  },
  "project": {
   
    "ios": {
   
      "sourceDir": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/ios",
      "xcodeProject": {
   
        "name": "MyProject.xcodeproj",
        "isWorkspace": false
      }
    },
    "android": {
   
      "sourceDir": "/Users/yuzhiqiang/workspace/RN/personal/RNProjectAnalysis/android",
      "appName": "app",
      "packageName": "com.yzq.rn_project_analysis",
      "applicationId": "com.yzq.rn_project_analysis",
      "mainActivity": ".MainActivity"
    }
  }
}

可以看到json数据中包含了

  • reactNativePath
  • reactNativeVersion
  • dependencies
  • commands
  • healthChecks
  • platforms
  • project
    这些关键信息,我们的脚本主要也是用到了这些信息,来自动添加依赖,生成PackageList.java文件等。

下面我们来详细看下这些关键信息是如何获取的。

入口在 xxx/node_modules/@react-native-community/cli/build/bin.js 文件中,我们先来看下这个文件的源码

if (_semver().default.satisfies(process.version, _cliDoctor().versionRanges.NODE_JS)) {
   
    const {
   
        run
    } = require('./');
    run();
} else {
   
    console.error(`${
     _chalk().default.red(`React Native needs Node.js ${
       _cliDoctor().versionRanges.NODE_JS}. You're currently on version ${
       process.version}. Please upgrade Node.js to a supported version and try again.`)}`);
}

bin.js的最终执行了一个当前目录下的index.js文件的一个run方法,这个run方法位于同级目录中index.js 中。

async function run(platformName) {
   
    try {
   
        await setupAndRun(platformName);
    } catch (e) {
   
        handleError(e);
    }
}

随后调用了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喻志强(Xeon)

码字不易,鼓励随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值