[ReactNative笔记]Codegen原理解析

893 阅读5分钟

0.69 libray Codegen

0.70官方文档:reactnative.dev/docs/0.70/t…

以下基于 react-native@0.69.4 分析

graph TD
    subgraph Android Codegen
        react-native-gradle-plugin --> generateCodegenSchemaFromJavaScript
        react-native-gradle-plugin --> generateCodegenArtifactsFromSchema
    end
    generateCodegenSchemaFromJavaScript --> combine-js-to-schema-cli.js
    generateCodegenArtifactsFromSchema --> generate-specs-cli.js
    subgraph iOS Codegen
        react_native_pods.rb
        script_phases.sh
    end
    script_phases.sh -. noDiscovery .-> combine-js-to-schema-cli.js
    script_phases.sh -. noDiscovery .-> generate-specs-cli.js
    script_phases.sh -- discovery --> generate-artifacts.js
    generate-artifacts.js --> generate-specs-cli.js
    generate-artifacts.js --> generate-provider-cli.js
    react_native_pods.rb -- discovery --> generate-artifacts.js
    react_native_pods.rb -. noDiscovery .-> generate-provider-cli.js
    subgraph RNCodegen.js
        generate
        generateFromSchemas
        generateViewConfig
    end
    generate-specs-cli.js --> generate
    generate-provider-cli.js -->|fabric| generateFromSchemas
    subgraph JavaScript Codegen
        babel-plugin-codegen
    end
    babel-plugin-codegen --> generateViewConfig

library Codegen for Android

示例APP的settings.gradle,github.com/Sunbreak/re…

includeBuild('../node_modules/react-native-gradle-plugin')

集成react-native-gradle-plugin,即可

$ tree ${NODE_MODULES}/${LIBRARY}/android/build/generated/source/codegen
├── java
│   └── com
│       └── turboonly
│           └── NativeTurboOnlySpec.java
├── jni
│   ├── Android.mk
│   ├── CMakeLists.txt
│   ├── RNTurboOnlySpec-generated.cpp
│   ├── RNTurboOnlySpec.h
└── schema.json
  • 为Fabric Component静态生成ComponentDescriptor.hEventEmitter.h/cppProps.h/cppShadowNode.h/cppInterface.javaDelegate.javagithub.com/Sunbreak/re…
$ tree ${NODE_MODULES}/${LIBRARY}/android/build/generated/source/codegen
├── java
│   └── com
│       └── facebook
│           └── react
│               └── viewmanagers
│                   ├── FabricOnlyViewManagerDelegate.java
│                   └── FabricOnlyViewManagerInterface.java
├── jni
│   ├── Android.mk
│   ├── CMakeLists.txt
│   └── react
│       └── renderer
│           └── components
│               └── RNFabricOnlyViewSpec
│                   ├── ComponentDescriptors.h
│                   ├── EventEmitters.cpp
│                   ├── EventEmitters.h
│                   ├── Props.cpp
│                   ├── Props.h
│                   ├── ShadowNodes.cpp
│                   └── ShadowNodes.h
└── schema.json

classDiagram
    class buildCodegenCLI {
        +生成CodegenCLI工具
    }
    buildCodegenCLI <|-- generateCodegenSchemaFromJavaScript
    class generateCodegenSchemaFromJavaScript {
        +生成schema.json文件
    }
    generateCodegenSchemaFromJavaScript <|-- generateCodegenArtifactsFromSchema
    class generateCodegenArtifactsFromSchema {
        +生成Java和C++、CMake/Android.mk文件
    }
    generateCodegenArtifactsFromSchema <|-- preBuild
    class preBuild {
        +Gradle内置任务
    }

buildCodegenCLI

  • react-native-gradle-plugin的BuildCodegenCLITask.ktnode_modules/react-native-codegen中没有scriptssrc,单独运行./gradlew buildCodegenCLI会报错
  @get:InputFiles
  val input: FileCollection by lazy {
    codegenDir.get().files("scripts", "src", "package.json", ".babelrc", ".prettierrc")
  }
  ...
  init {
    // We need this condition as we want a single instance of BuildCodegenCLITask to execute
    // per project. Therefore we can safely skip the task if the lib/cli/ folder is available.
    onlyIf {
      val cliDir = codegenDir.file("lib/cli/").get().asFile
      !cliDir.exists() || cliDir.listFiles()?.size == 0
    }
  }
  • react-native的package.jsonyarn install安装react-native-codegen@0.69.1之后,node_modules/react-native-codegen包含lib/cli,根据BuildCodegenCLITaskonlyIf,codegenCLI并非BuildCodegenCLITask运行时生成,而是从NPM拉取预编译好的包
react-native-codegen@^0.69.1:
  version "0.69.1"
  "scripts": {
    "build": "yarn clean && node scripts/build.js --verbose",
    "clean": "rm -rf lib",
    "prepare": "yarn run build"
  },

generateCodegenSchemaFromJavaScript

  • react-native-gradle-plugin的GenerateCodegenSchemaTask.kt:监控jsRootDir下的**/*.js文件,一旦有变动,立即调用combine-js-to-schema-cli.js,读取jsRootDir中的文件,生成schema.json
  @get:InputFiles val jsInputFiles = project.fileTree(jsRootDir) { it.include("**/*.js") }

  @get:OutputFile
  val generatedSchemaFile: Provider<RegularFile> = generatedSrcDir.file("schema.json")
  ...
            codegenDir
                .file("lib/cli/combine/combine-js-to-schema-cli.js")
                .get()
                .asFile.absolutePath,
            generatedSchemaFile.get().asFile.absolutePath,
            jsRootDir.asFile.get().absolutePath,
            ...

generateCodegenArtifactsFromSchema

  • react-native-gradle-plugin的GenerateCodegenArtifactsTask.kt:监控schema.json,一旦改动,立即调用generate-specs-cli.js,读取schema.json,生成specs*系列文件
  @get:InputFile
  val generatedSchemaFile: Provider<RegularFile> = generatedSrcDir.file("schema.json")
            ...
            reactNativeDir.file("scripts/generate-specs-cli.js").get().asFile.absolutePath,
            "--platform",
            "android",
            "--schemaPath",
            generatedSchemaFile.get().asFile.absolutePath,
            "--outputDir",
            generatedSrcDir.get().asFile.absolutePath,
            "--libraryName",
            libraryName.get(),
            "--javaPackageName",
            codegenJavaPackageName.get()))
            ...

library Codegen for iOS

集成react_native_pods.rb,即可

  • 为Turbo Module静态生成ObjC++的.h/mm
$ tree ${APP}/ios/build/generated/ios/${LIBRARY} 
├── RNTurboOnlySpec-generated.mm
└── RNTurboOnlySpec.h
  • 为Fabric Component静态生成ComponentDescriptor.hEventEmitter.h/cppProps.h/cppShadowNode.h/cppRCTComponentViewHelpers.hRCTThirdPartyFabricComponentsProvider.h/mm
$ tree ${APP}/ios/build/generated/ios 
├── RCTThirdPartyFabricComponentsProvider.h
├── RCTThirdPartyFabricComponentsProvider.mm
└── react
    └── renderer
        └── components
            ├── RNFabricOnlyViewSpec
            │   ├── ComponentDescriptors.h
            │   ├── EventEmitters.cpp
            │   ├── EventEmitters.h
            │   ├── Props.cpp
            │   ├── Props.h
            │   ├── RCTComponentViewHelpers.h
            │   ├── ShadowNodes.cpp
            │   └── ShadowNodes.h

graph TD
    script_phases.sh -. noDiscovery .-> combine-js-to-schema-cli.js
    script_phases.sh -. noDiscovery .-> generate-specs-cli.js
    script_phases.sh -- discovery --> generate-artifacts.js
    generate-artifacts.js --> generate-provider-cli.js
    generate-artifacts.js --> generate-specs-cli.js
    react_native_pods.rb -- discovery --> generate-artifacts.js
    react_native_pods.rb -. noDiscovery .-> generate-provider-cli.js
    subgraph RNCodegen.js
        generate
        generateFromSchemas
    end
    generate-specs-cli.js --> generate
    generate-provider-cli.js -->|fabric| generateFromSchemas

USE_CODEGEN_DISCOVERY为true

install时自动添加sh、生成spec、生成全部代码

pod install时,use_react_native_codegen_discovery自动找到podspec,将script_phases.sh插入,生成React-Codegen库的spec,生成所有codegen代码

graph TD
    Podfile --> use_react_native
    subgraph react_native_pods.rb
        use_react_native --> use_react_native_codegen_discovery
        use_react_native_codegen_discovery --> get_react_codegen_script_phases
        use_react_native_codegen_discovery --> generate_react_codegen_podspec
    end
    get_react_codegen_script_phases --> get_script_phases_with_codegen_discovery
    subgraph script_phases.rb
        get_script_phases_with_codegen_discovery
    end
    generate_react_codegen_podspec --> 写入podspec.json
    use_react_native_codegen_discovery --> generate-artifacts.js
def use_react_native! (options={})
  if ENV['USE_CODEGEN_DISCOVERY'] == '1'
    use_react_native_codegen_discovery!({...})
    ...
  else
  ...
end

...

def get_react_codegen_script_phases(options={})
  return {
    ...
    'script': get_script_phases_with_codegen_discovery(...),
  }
end

...

def use_react_native_codegen_discovery!(options={})
  ...
  # Generate React-Codegen podspec here to add the script phases.
  script_phases = get_react_codegen_script_phases(options)
  react_codegen_spec = get_react_codegen_spec(fabric_enabled: fabric_enabled, script_phases: script_phases)
  generate_react_codegen_podspec!(react_codegen_spec)

  out = Pod::Executable.execute_command(
    'node',
    [
      "#{relative_installation_root}/#{react_native_path}/scripts/generate-artifacts.js",
  ...
end
def get_script_phases_with_codegen_discovery(options)
    export_vars = {
      ...
      'RCT_SCRIPT_TYPE' => "withCodegenDiscovery",
    }
    return get_script_template(options[:react_native_path], export_vars)
end

compile时script_phases.sh

graph TD
    podspec([x.podspec]) --> withCodgenDiscovery
    subgraph script_phases.sh
        withCodgenDiscovery --> generateArtifacts
    end
    generateArtifacts --> generate-artifacts.js
generateArtifacts () {
        ...
        "$NODE_BINARY" "scripts/generate-artifacts.js" --path "$RCT_SCRIPT_APP_PATH" --outputPath "$TEMP_OUTPUT_DIR" --fabricEnabled "$RCT_SCRIPT_FABRIC_ENABLED" --configFileDir "$RCT_SCRIPT_CONFIG_FILE_DIR" --nodeBinary "$NODE_BINARY"
        ...
}
...
withCodgenDiscovery () {
    setup_dirs
    find_node
    generateArtifacts
    moveOutputs
}

USE_CODEGEN_DISCOVERY为false

install前手动添加sh

需要在podspec中手动声明use_react_native_codegen,保证install时添加script_phases.sh

graph TD
    podspec([x.podspec]) --> use_react_native_codegen
    subgraph react_native_pods.rb
        use_react_native_codegen
    end
    use_react_native_codegen --> get_script_phases_no_codegen_discovery
    subgraph script_phases.rb
        get_script_phases_no_codegen_discovery
    end
require_relative "#{react_native_path}/scripts/react_native_pods.rb"
...
Pod::Spec.new do |s|
  ...
  # This podspec is used to trigger the codegen, and built files are generated in a different location.
  # We don't want this pod to actually include any files.
  ...
  use_react_native_codegen!(...)
end
def use_react_native_codegen!(spec, options={})
  ...
  spec.script_phase = {
    ...
    :script => get_script_phases_no_codegen_discovery(...)
    ...
  }
  ...
end
def get_script_phases_no_codegen_discovery(options)
    export_vars = {
      ...
      'RCT_SCRIPT_LIBRARY_NAME' => "#{options[:library_name]}",
      ...
    }
    return get_script_template(options[:react_native_path], export_vars)
end

install时生成spec、生成部分代码

graph TD
    Podfile --> use_react_native
    subgraph react_native_pods.rb
        use_react_native --> generate_react_codegen_podspec
        use_react_native -->|fabric| checkAndGenerateEmptyThirdPartyProvider
    end
    generate_react_codegen_podspec --> 写入podspec.json
    checkAndGenerateEmptyThirdPartyProvider --> generate-provider-cli.js
def use_react_native! (options={})
  if ENV['USE_CODEGEN_DISCOVERY'] == '1'
    ...
  else
    # Generate a podspec file for generated files.
    # This gets generated in use_react_native_codegen_discovery when codegen discovery is enabled.
    react_codegen_spec = get_react_codegen_spec(fabric_enabled: fabric_enabled)
    generate_react_codegen_podspec!(react_codegen_spec)
  end
  ...
  if fabric_enabled
    checkAndGenerateEmptyThirdPartyProvider!(prefix)
    ...
  end
  ...
end

...

# This is a temporary supporting function until we enable use_react_native_codegen_discovery by default.
def checkAndGenerateEmptyThirdPartyProvider!(react_native_path)
    return if ENV['USE_CODEGEN_DISCOVERY'] == '1'
    ...
    Pod::UI.puts '[Codegen] generating an empty RCTThirdPartyFabricComponentsProvider'
    Pod::Executable.execute_command(
      'node',
      [
        "#{relative_installation_root}/#{react_native_path}/scripts/generate-provider-cli.js",
    ...
end

compile时script_phases.sh

graph TD
    podspec([x.podspec]) --> noCodegenDiscovery
    subgraph script_phases.sh
        noCodegenDiscovery --> generateCodegenSchemaFromJavaScript
        noCodegenDiscovery --> generateCodegenArtifactsFromSchema
    end
    generateCodegenSchemaFromJavaScript --> combine-js-to-schema-cli.js
    generateCodegenArtifactsFromSchema --> generate-specs-cli.js
runSpecCodegen () {
    "$NODE_BINARY" "scripts/generate-specs-cli.js" --platform ios --schemaPath "$GENERATED_SCHEMA_FILE" --outputDir "$1" --libraryName "$RCT_SCRIPT_LIBRARY_NAME" --libraryType "$2"
}

generateCodegenSchemaFromJavaScript () {
    ...
    "$NODE_BINARY" "$CODEGEN_CLI_PATH/lib/cli/combine/combine-js-to-schema-cli.js" "$GENERATED_SCHEMA_FILE" $JS_SRCS
}

generateCodegenArtifactsFromSchema () {
            ...
            runSpecCodegen "$TEMP_OUTPUT_DIR/$RCT_SCRIPT_CODEGEN_MODULE_DIR/$RCT_SCRIPT_LIBRARY_NAME" "modules"
            runSpecCodegen "$TEMP_OUTPUT_DIR/$RCT_SCRIPT_CODEGEN_COMPONENT_DIR/$RCT_SCRIPT_LIBRARY_NAME" "components"
            ...
}
...
noCodegenDiscovery () {
    setup_dirs
    find_node
    generateCodegenSchemaFromJavaScript
    generateCodegenArtifactsFromSchema
    moveOutputs
}

library Codegen for JavaScript

集成@react-native/babel-plugin-codegen,即可

  • 为Fabric Component静态生成NativeViewConfig.js
...
try {
  flow = require('react-native-codegen/src/parsers/flow');
  RNCodegen = require('react-native-codegen/src/generators/RNCodegen');
} catch (e) {
  // Fallback to lib when source doesn't exit (e.g. when installed as a dev dependency)
  flow = require('react-native-codegen/lib/parsers/flow');
  RNCodegen = require('react-native-codegen/lib/generators/RNCodegen');
}

function generateViewConfig(filename, code) {
  ...
  return RNCodegen.generateViewConfig({
    schema,
    libraryName,
  });
}
...
module.exports = function ({parse, types: t}) {
  return {
    ...
    visitor: {
      ...
      Program: {
        exit(path) {
          if (this.defaultExport) {
            const viewConfig = generateViewConfig(this.filename, this.code);
            ...
const generateViewConfigJs = require('./components/GenerateViewConfigJs.js');
...
module.exports = {
  ...
  generateViewConfig({libraryName, schema}: LibraryOptions): string {
    ...
    const result = generateViewConfigJs
      .generate(libraryName, schema)
      .values()
      .next();
    ...
  },
}

用途

We just introduced TypeScript support in this babel plugin via D39136171 (df0b690). This version bump will allow us to enable the static ViewConfig codegen in TypeScript React Native libraries, like React Native SVG. See PR: software-mansion/react-native-svg#1847

libray Codegen工具脚本

generate-artifacts-cli.js

graph TD
   generate-artifacts.js --> combine-js-to-schema-cli.js
   generate-artifacts.js --> generate-specs-cli.js
   generate-artifacts.js -->|fabric| generate-provider-cli.js
   subgraph RNCodegen.js
       generate
       generateFromSchemas
   end
   generate-specs-cli.js --> generate
   generate-provider-cli.js --> generateFromSchemas
function main(appRootDir, outputPath) {
  ...
  try {
    ...
    libraries.forEach(library => {
      ...
      executeNodeScript(..., 'combine-js-to-schema-cli.js', ...);
      ...
      executeNodeScript(..., 'generate-specs-cli.js', ...);
      ...
      if (CODEGEN_FABRIC_ENABLED) {
        ...
        executeNodeScript(..., 'generate-provider-cli.js', ...);
        ...
      }
    }
  }
  ...
}
try {
  RNCodegen = require('../packages/react-native-codegen/lib/generators/RNCodegen.js');
} catch (e) {
  RNCodegen = require('react-native-codegen/lib/generators/RNCodegen.js');
  ...
}
...
  RNCodegen.generateFromSchemas(
    {
      schemas,
      outputDirectory,
    },
    {
      generators: GENERATORS[platform],
    },
  );
  ...

combine-js-to-schema-cli.js

const combine = require('./combine-js-to-schema');
...
function filterJSFile(file: string) {
  return (
    /^(Native.+|.+NativeComponent)/.test(path.basename(file)) &&
    // NativeUIManager will be deprecated by Fabric UIManager.
    // For now, ignore this spec completely because the types are not fully supported.
    !file.endsWith('NativeUIManager.js') &&
    // NativeSampleTurboModule is for demo purpose. It should be added manually to the
    // app for now.
    !file.endsWith('NativeSampleTurboModule.js') &&
    !file.includes('__tests')
  );
}
...
const combined = combine(allFiles);
  • react-native-codegn的combine-js-to-schema.js:判断export default codegenNativeComponent<或者extends TurboModule,然后使用TypeScriptParser或者FlowParser解析
function combineSchemas(files: Array<string>): SchemaType {
      ...
      if (
        contents &&
        (/export\s+default\s+\(?codegenNativeComponent</.test(contents) ||
          /extends TurboModule/.test(contents))
      ) {
        ...
        const schema = isTypeScript
          ? TypeScriptParser.parseFile(filename)
          : FlowParser.parseFile(filename);
        ...
}

generate-specs-cli.js

try {
  RNCodegen = require('../packages/react-native-codegen/lib/generators/RNCodegen.js');
} catch (e) {
  RNCodegen = require('react-native-codegen/lib/generators/RNCodegen.js');
  ...
}
...
  RNCodegen.generate(
    {
      libraryName,
      schema,
      outputDirectory,
      packageName,
    },
    {
      generators: GENERATORS[libraryType][platform],
    },
  );

RNCodegen.js

react-native-codegn的RNCodegen.js

generate for Android

  • 为Fabric Component静态生成ComponentDescriptor.hEventEmitter.h/cppProps.h/cppShadowNode.h/cpp**Interface.java**Delegate.java
const generateComponentDescriptorH = require('./components/GenerateComponentDescriptorH.js');
// const generateComponentHObjCpp = require('./components/GenerateComponentHObjCpp.js');
const generateEventEmitterCpp = require('./components/GenerateEventEmitterCpp.js');
const generateEventEmitterH = require('./components/GenerateEventEmitterH.js');
const generatePropsCpp = require('./components/GeneratePropsCpp.js');
const generatePropsH = require('./components/GeneratePropsH.js');
...
const generatePropsJavaInterface = require('./components/GeneratePropsJavaInterface.js');
const generatePropsJavaDelegate = require('./components/GeneratePropsJavaDelegate.js');
...
const generateShadowNodeCpp = require('./components/GenerateShadowNodeCpp.js');
const generateShadowNodeH = require('./components/GenerateShadowNodeH.js');
...
const LIBRARY_GENERATORS = {
  // TODO: Refactor this to consolidate various C++ output variation instead of forking per platform.
  componentsAndroid: [
    // JNI/C++ files
    generateComponentDescriptorH.generate,
    generateEventEmitterCpp.generate,
    generateEventEmitterH.generate,
    generatePropsCpp.generate,
    generatePropsH.generate,
    generateShadowNodeCpp.generate,
    generateShadowNodeH.generate,
    // Java files
    generatePropsJavaInterface.generate,
    generatePropsJavaDelegate.generate,
  ],
  ...
};
...
  • 为Turbo JavaModule静态生成Native**Spec.java和JNI的**Spec.hh和**Spec-generated.cpp
  • 为Turbo CxxModule静态生成**SpecJSI.h**SpecJSI-generated.cpp
// const generateModuleObjCpp = require('./modules/GenerateModuleObjCpp');
const GenerateModuleJavaSpec = require('./modules/GenerateModuleJavaSpec.js');
const GenerateModuleJniCpp = require('./modules/GenerateModuleJniCpp.js');
const GenerateModuleJniH = require('./modules/GenerateModuleJniH.js');
...
const LIBRARY_GENERATORS = {
  // TODO: Refactor this to consolidate various C++ output variation instead of forking per platform.
  ...
  modulesAndroid: [
    GenerateModuleJniCpp.generate,
    GenerateModuleJniH.generate,
    generateModuleJavaSpec.generate,
  ],
  modulesCxx: [generateModuleCpp.generate, generateModuleH.generate],
  ...
};
...

generate for iOS

  • 为Fabric Component静态生成ComponentDescriptor.hEventEmitter.h/cppProps.h/cppShadowNode.h/cppRCTComponentViewHelpers.hRCTThirdPartyFabricComponentsProvider.h/mm
const generateComponentDescriptorH = require('./components/GenerateComponentDescriptorH.js');
const generateComponentHObjCpp = require('./components/GenerateComponentHObjCpp.js');
const generateEventEmitterCpp = require('./components/GenerateEventEmitterCpp.js');
const generateEventEmitterH = require('./components/GenerateEventEmitterH.js');
const generatePropsCpp = require('./components/GeneratePropsCpp.js');
const generatePropsH = require('./components/GeneratePropsH.js');
...
// const generatePropsJavaInterface = require('./components/GeneratePropsJavaInterface.js');
// const generatePropsJavaDelegate = require('./components/GeneratePropsJavaDelegate.js');
...
const generateShadowNodeCpp = require('./components/GenerateShadowNodeCpp.js');
const generateShadowNodeH = require('./components/GenerateShadowNodeH.js');
...
const LIBRARY_GENERATORS = {
  // TODO: Refactor this to consolidate various C++ output variation instead of forking per platform.
  ...
  componentsIOS: [
    generateComponentDescriptorH.generate,
    generateEventEmitterCpp.generate,
    generateEventEmitterH.generate,
    generateComponentHObjCpp.generate,
    generatePropsCpp.generate,
    generatePropsH.generate,
    generateShadowNodeCpp.generate,
    generateShadowNodeH.generate,
  ],
  ...
};
...
  • 为Turbo ObjcModule静态生成**Spec.h**Spec-generated.mm
  • 为Turbo CxxModule静态生成**SpecJSI.h**SpecJSI-generated.cpp
const generateModuleH = require('./modules/GenerateModuleH.js');
const generateModuleCpp = require('./modules/GenerateModuleCpp.js');
const generateModuleObjCpp = require('./modules/GenerateModuleObjCpp');
// const generateModuleJavaSpec = require('./modules/GenerateModuleJavaSpec.js');
// const GenerateModuleJniCpp = require('./modules/GenerateModuleJniCpp.js');
// const GenerateModuleJniH = require('./modules/GenerateModuleJniH.js');
...
const LIBRARY_GENERATORS = {
  // TODO: Refactor this to consolidate various C++ output variation instead of forking per platform.
  ...
  modulesCxx: [generateModuleCpp.generate, generateModuleH.generate],
  modulesIOS: [generateModuleObjCpp.generate],
  ...
};

generateFromSchemas for iOS

  • 为Fabric Component静态生成RCTThirdPartyFabricComponentsProvider.h/mm
const generateThirdPartyFabricComponentsProviderObjCpp = require('./components/GenerateThirdPartyFabricComponentsProviderObjCpp.js');
const generateThirdPartyFabricComponentsProviderH = require('./components/GenerateThirdPartyFabricComponentsProviderH.js');
...
const SCHEMAS_GENERATORS = {
  providerIOS: [
    generateThirdPartyFabricComponentsProviderObjCpp.generate,
    generateThirdPartyFabricComponentsProviderH.generate,
  ],
};
...
module.exports = {
  generateFromSchemas(
    ...
  ) {
    ...
    const generatedFiles = [];
    for (const name of generators) {
      for (const generator of SCHEMAS_GENERATORS[name]) {
        generatedFiles.push(...generator(schemas));
      }
    }
    ...
};

generateViewConfig for JavaScript

  • 为Fabric Component静态生成NativeViewConfig.js
const generateViewConfigJs = require('./components/GenerateViewConfigJs.js');
...
module.exports = {
  generateViewConfig({libraryName, schema}: LibraryOptions): string {
    schemaValidator.validate(schema);

    const result = generateViewConfigJs
      .generate(libraryName, schema)
      .values()
      .next();
    ...
};

0.70 APP Autolink

0.70官方文档:reactnative.dev/docs/0.70/n…

  • react-native@0.69.x使用@react-native-community/cli@8.x,Autolink仅生成PackageList.java
  • 以下基于 react-native@0.70.6对应的@react-native-community/cli@9.3.2 分析

APP Autolink for Android

集成@react-native-community/cli-platform-android的native_modules.gradle,即可

  • 根据Autolink生成PackageList.javarncli.h/cpp
$ tree ${APP}/android/app/build/generated/rncli/src/main/
├── java
│   └── com
│       └── facebook
│           └── react
│               └── PackageList.java
└── jni
    ├── Android-rncli.cmake
    ├── Android-rncli.mk
    ├── rncli.cpp
    └── rncli.h

settings.gradle

示例APP的settings.gradle,github.com/Sunbreak/re…

apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
graph TD
    setting.gradle -. apply .-> init
    subgraph ReactNativeModules
        init --> getReactNativeConfig -. init .-> modules[(reactNativeModules)]
        addReactNativeModuleProjects -- include --> modules
    end
    setting.gradle --> applyNativeModulesSettingsGradle --> addReactNativeModuleProjects
  • getReactNativeConfig
  ArrayList<HashMap<String, String>> getReactNativeConfig() {
    ...
    // node @react-native-community/cli/build/bin.js config
    String[] reactNativeConfigCommand = ["node", cliPath, "config"]
    // ... 解析reactNativeConfigOutput ...
    def dependencies = json["dependencies"]
    def project = json["project"]["android"]
    ...
    dependencies.each { name, value ->
      ...
      if (androidConfig != null && androidConfig["sourceDir"] != null) {
        ...
        reactNativeModuleConfig.put("name", name)
        ...
        reactNativeModules.add(reactNativeModuleConfig)
      }
      ...
    }
  • addReactNativeModuleProjects
  void addReactNativeModuleProjects(DefaultSettings defaultSettings) {
    reactNativeModules.forEach { reactNativeModule ->
      String nameCleansed = reactNativeModule["nameCleansed"]
      ...
      defaultSettings.include(":${nameCleansed}")
      ...
    }
  }

build.gradle

示例APP的build.gradle,github.com/Sunbreak/re…

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
graph TD
    build.gradle -. apply .-> init
    subgraph ReactNativeModules
        init --> getReactNativeConfig -. init .-> modules[(reactNativeModules)]
        addReactNativeModuleDependencies -- depend --> modules
        generatePackagesFile
        genNewArch["
            generateAndroidMkFile
            generateCmakeFile
            generateRncliCpp
            generateRncliH
        "]
    end
    build.gradle --> applyNativeModulesAppBuildGradle --> addReactNativeModuleDependencies
    applyNativeModulesAppBuildGradle --> generatePackageList --> generatePackagesFile
    applyNativeModulesAppBuildGradle --> generateNewArchitectureFiles --> genNewArch
  • addReactNativeModuleDependencies
  void addReactNativeModuleDependencies(Project appProject) {
    reactNativeModules.forEach { reactNativeModule ->
      ...
      appProject.dependencies {
        if (reactNativeModulesBuildVariants.containsKey(nameCleansed)) {
          reactNativeModulesBuildVariants.forEach { buildVariant -> 
            "${buildVariant}Implementation" project(path: ":${nameCleansed}") // 根据buildVariant依赖project(path: ":${nameCleansed}")
          }
        } else {
          implementation project(path: ":${nameCleansed}") // appProject直接依赖project(path: ":${nameCleansed}")
        }
      }
    }
  }
  • generatePackagesFile
    • 根据generatedFileContentsTemplate模板生成PackageList.java文件
  • generateAndroidMkFile
    • 根据androidMkTemplate模板生成Android-rncli.mk文件
  • generateCmakeFile
    • 根据cmakeTemplate模板生成Android-rncli.cmake文件
  • generateRncliCpp
    • 根据rncliCppTemplate模板生成rncli.cpp文件
  • generateRncliH
    • 根据rncliHTemplate模板生成rncli.h文件

附录

@react-native/babel-preset

timeline
    2016 : babel-preset-react-native
    2018 : metro-react-native-babel-preset
    2023 : @react-native/babel-preset

Babel presets for React Native applications. React Native itself uses this Babel preset by default when transforming your app's source code.