如何在 Metro 中获取 React Native 变体?

slh*_*hck 3 javascript react-native metro-bundler

我正在开发 React Native 应用程序,其中包含针对不同可能客户端的不同配置,例如src/config/config.js. 这些配置相当复杂。该文件的结构基于客户端名称作为键,值作为对象条目,例如:

export default {
  fooClient: {
    apiUrl: "https://foo.example.com/",
  barClient: {
    apiUrl: "https://bar.example.com/"
  }
}
Run Code Online (Sandbox Code Playgroud)

当然,还有很多其他选项键。

构建应用程序时,我通过指定 Android 构建变体知道要为哪个客户端执行此操作,例如:

ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
Run Code Online (Sandbox Code Playgroud)

出于安全原因,我不希望其他客户端的密钥包含在配置文件中。在构建应用程序并将其发送给客户端之前,我可以选择哪些选项来从此文件中删除所有其他客户端配置?

我考虑了以下内容:我修改了打包程序,以便它删除与当前构建变体不对应的键。

我现在有一个 Metro 变压器插件,可以执行以下操作:

const upstreamTransformer = require('metro-react-native-babel-transformer');

module.exports.transform = function(src, filename, options) {
  if (typeof src === 'object') {
    // handle RN >= 0.46
    ({ src, filename, options } = src);
  }

  if (filename.endsWith('config.js')) {
    console.log('Transforming ' + filename);
    let srcStripped = src.replace(';', '').replace('export default ', '');
    let configObj = JSON.parse(srcStripped);
    // TODO: get the build variant and strip all keys that we do not need from configObj
    return upstreamTransformer.transform({
      src: 'export default ' + JSON.stringify(configObj) + ';',
      filename: filename,
      options
    });
  } else {
    return upstreamTransformer.transform({ src, filename, options });
  }
};
Run Code Online (Sandbox Code Playgroud)

但我如何知道正在使用哪个构建变体?

如果这看起来像是一个 XY 问题,我很乐意探索动态构建配置的替代方案。但是,我不能使用环境变量,因为配置太复杂,不能只是一个.env键列表。

Chr*_*ras 6

你不应该transform这样使用地铁。它不干净,迟早可能会导致配置丢失和/或语法损坏。

我所做并建议你的是在src/config/;下创建 3 个不同的配置文件。一个文件fooClient.js,一个文件barClient.js,最后一个文件具有通用配置client.js。所有文件都将导出默认配置对象,但在每个fooClientand内部barClient,您将使用deepmerge模块来合并client.js配置:

client.js:

export default {
  commonSettingA: "...",
  commonSettings: {
    ...
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

fooClient.js:

import merge from 'deepmerge';
import config from './config';

export default merge.all([
  config,
  {
    apiUrl: "https://foo.example.com/",
  }
]);
Run Code Online (Sandbox Code Playgroud)

barClient.js:

import merge from 'deepmerge';
import config from './config';

export default merge.all([
  config,
  {
    apiUrl: "https://bar.example.com/",
  }
]);
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用环境变量来传递所需的配置并创建适当的 Metro 解析;@react-native-community/cli不将命令行参数传递给 Metro 配置脚本。您可以使用process.argv自己解析它,但这不值得。

以下是如何使用环境变量在内部创建解析metro.config.js

const path = require("path");

module.exports = {
  projectRoot: path.resolve(__dirname),

  resolver: {
    sourceExts: ['js', 'jsx', 'ts', 'tsx'],
    extraNodeModules: {
      // Local aliases
      "@config": path.resolve(__dirname, "src/config", `${process.env.METRO_VARIANT}Client.js`)
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

使用此解析,您必须像这样导入配置:

import config from '@config';
Run Code Online (Sandbox Code Playgroud)

然后添加 2 个package.json脚本,一个用于fooClient,一个用于barClient

{
   ...
  "scripts": {
    "run:android:foo": "METRO_VARIANT=foo ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo",
    "run:android:bar": "METRO_VARIANT=bar ENVFILE=.env npx react-native run-android --variant barDebug --appIdSuffix bar",
    ...
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

然后你只需运行所需的脚本:

{
   ...
  "scripts": {
    "run:android:foo": "METRO_VARIANT=foo ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo",
    "run:android:bar": "METRO_VARIANT=bar ENVFILE=.env npx react-native run-android --variant barDebug --appIdSuffix bar",
    ...
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)
yarn run:android:foo # will build with fooClient.js
Run Code Online (Sandbox Code Playgroud)