如何在 Metro 中获取 React Native 变体?
How to get React Native variant in Metro?
我正在开发 React Native 应用程序,该应用程序在 src/config/config.js
等文件中包含针对不同可能客户端的不同配置。这些配置相当复杂。该文件的结构基于客户端名称作为键,值作为对象条目,例如:
export default {
fooClient: {
apiUrl: "https://foo.example.com/",
barClient: {
apiUrl: "https://bar.example.com/"
}
}
当然还有很多其他的选项键。
在构建应用程序时,我知道我想为哪个客户端执行此操作,方法是指定 Android 构建变体,例如:
ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
出于安全原因,我不希望其他客户端的密钥包含在配置文件中。在我构建应用程序并将其发送给客户端之前,我有什么选择可以从该文件中删除所有其他客户端配置?
我想到了以下几点:我修改了打包程序,以便它删除与当前构建变体不对应的键。
我现在有一个用于 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 });
}
};
但是我怎么知道正在使用哪个构建变体?
如果这看起来像是一个 XY 问题,我很乐意探索动态构建配置的替代方法。但是,我不能使用环境变量,因为配置太复杂了,不可能只是 .env
个键的列表。
你不能只在命令中添加一个 "duplicated" 参数吗?
像这样
METRO_VARIANT=fooDebug ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
并通过 process.env.METRO_VARIANT
获取
您可能不想让您的设置复杂得多。根据不同客户端的数量,您可能希望每个客户端有多个 schemas/variants,但这可能有点矫枉过正。
你不应该这样使用 Metro transform
。它不干净,迟早会导致配置丢失 and/or 语法损坏。
我所做的并建议你,是在 src/config/
下创建 3 个不同的配置文件; fooClient.js
一个文件,barClient.js
一个文件,最后一个具有通用配置的文件 client.js
。所有文件都将导出默认配置对象,但在每个 fooClient
和 barClient
中,您将使用 deepmerge
模块合并 client.js
配置:
client.js
:
export default {
commonSettingA: "...",
commonSettings: {
...
}
...
}
fooClient.js
:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://foo.example.com/",
}
]);
barClient.js
:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://bar.example.com/",
}
]);
然后您可以使用环境变量来传递所需的配置并创建适当的地铁解析; @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`)
}
}
};
使用此解析,您必须像这样导入配置:
import config from '@config';
然后添加 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",
...
}
...
}
那么您只需 运行 所需的脚本:
yarn run:android:foo # will build with fooClient.js
yarn run:android:bar # will build with barClient.js
我正在开发 React Native 应用程序,该应用程序在 src/config/config.js
等文件中包含针对不同可能客户端的不同配置。这些配置相当复杂。该文件的结构基于客户端名称作为键,值作为对象条目,例如:
export default {
fooClient: {
apiUrl: "https://foo.example.com/",
barClient: {
apiUrl: "https://bar.example.com/"
}
}
当然还有很多其他的选项键。
在构建应用程序时,我知道我想为哪个客户端执行此操作,方法是指定 Android 构建变体,例如:
ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
出于安全原因,我不希望其他客户端的密钥包含在配置文件中。在我构建应用程序并将其发送给客户端之前,我有什么选择可以从该文件中删除所有其他客户端配置?
我想到了以下几点:我修改了打包程序,以便它删除与当前构建变体不对应的键。
我现在有一个用于 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 });
}
};
但是我怎么知道正在使用哪个构建变体?
如果这看起来像是一个 XY 问题,我很乐意探索动态构建配置的替代方法。但是,我不能使用环境变量,因为配置太复杂了,不可能只是 .env
个键的列表。
你不能只在命令中添加一个 "duplicated" 参数吗?
像这样
METRO_VARIANT=fooDebug ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
并通过 process.env.METRO_VARIANT
您可能不想让您的设置复杂得多。根据不同客户端的数量,您可能希望每个客户端有多个 schemas/variants,但这可能有点矫枉过正。
你不应该这样使用 Metro transform
。它不干净,迟早会导致配置丢失 and/or 语法损坏。
我所做的并建议你,是在 src/config/
下创建 3 个不同的配置文件; fooClient.js
一个文件,barClient.js
一个文件,最后一个具有通用配置的文件 client.js
。所有文件都将导出默认配置对象,但在每个 fooClient
和 barClient
中,您将使用 deepmerge
模块合并 client.js
配置:
client.js
:
export default {
commonSettingA: "...",
commonSettings: {
...
}
...
}
fooClient.js
:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://foo.example.com/",
}
]);
barClient.js
:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://bar.example.com/",
}
]);
然后您可以使用环境变量来传递所需的配置并创建适当的地铁解析; @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`)
}
}
};
使用此解析,您必须像这样导入配置:
import config from '@config';
然后添加 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",
...
}
...
}
那么您只需 运行 所需的脚本:
yarn run:android:foo # will build with fooClient.js
yarn run:android:bar # will build with barClient.js