react-native-config-ultimate 0.0.1 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -11
- package/android/build.gradle +13 -0
- package/android/rncu.gradle +24 -4
- package/android/src/main/java/com/reactnativeultimateconfig/UltimateConfigPackage.java +3 -3
- package/android/src/newarch/java/com/reactnativeultimateconfig/UltimateConfigModule.java +102 -0
- package/android/src/{main → oldarch}/java/com/reactnativeultimateconfig/UltimateConfigModule.java +38 -3
- package/index.js +21 -2
- package/index.ts +24 -5
- package/ios/ConfigValues.h +2 -1
- package/ios/UltimateConfig.mm +16 -2
- package/package.json +15 -5
- package/react-native-config-ultimate.podspec +5 -15
- package/src/NativeUltimateConfig.ts +10 -4
- package/src/cli.js +4 -5
- package/src/cli.ts +5 -13
- package/src/load-env.ts +2 -6
- package/src/render-env.js +6 -3
- package/src/render-env.ts +12 -16
- package/src/validate-env.js +8 -2
- package/src/validate-env.ts +15 -12
- package/src/write-env.js +7 -4
- package/src/write-env.ts +11 -4
- package/src/bin.spec.ts +0 -36
- package/src/cli.spec.ts +0 -224
- package/src/flatten.spec.ts +0 -16
- package/src/load-env.spec.ts +0 -163
- package/src/main.spec.ts +0 -171
- package/src/resolve-env.spec.ts +0 -25
- package/src/validate-env.spec.ts +0 -164
- package/src/write-env.spec.ts +0 -105
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ _Config that works_
|
|
|
26
26
|
|
|
27
27
|
| react-native-config-ultimate | gradle |
|
|
28
28
|
| ---------------------------- | ------ |
|
|
29
|
-
|
|
|
29
|
+
| 0.0.x | 8 |
|
|
30
30
|
|
|
31
31
|
> For older versions see the original package [`react-native-ultimate-config`](https://github.com/maxkomarychev/react-native-ultimate-config).
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ _Config that works_
|
|
|
34
34
|
|
|
35
35
|
| react-native-config-ultimate | react-native | react | New Architecture |
|
|
36
36
|
| ---------------------------- | ------------ | ------ | ---------------- |
|
|
37
|
-
|
|
|
37
|
+
| 0.0.x | >=0.73 | >=18 | ✅ TurboModules |
|
|
38
38
|
|
|
39
39
|
## TL;DR usage
|
|
40
40
|
|
|
@@ -61,12 +61,12 @@ Therefore every time this library is updated all files MUST be regenerated using
|
|
|
61
61
|
1. [Features 🎆](#features)
|
|
62
62
|
1. [Mission 🥾](#mission)
|
|
63
63
|
1. [Quickstart Guide 🏃](./docs/quickstart.md)
|
|
64
|
+
1. [Migration Guide 🚀](./docs/migration.md) — from `react-native-ultimate-config` or `react-native-config`
|
|
64
65
|
1. [API 🧰](./docs/api.md)
|
|
65
66
|
1. [Testing Guide 🧪](./docs/testing.md)
|
|
66
|
-
1. [Changelog 📓](./packages/react-native-ultimate-config/CHANGELOG.md)
|
|
67
67
|
1. [Cookbook 🥦](./docs/cookbook.md)
|
|
68
68
|
1. [Troubleshooting 🎱](./docs/troubleshooting.md)
|
|
69
|
-
1. [
|
|
69
|
+
1. [Contributing 🤝](./CONTRIBUTING.md)
|
|
70
70
|
1. [Alternatives](./docs/alternatives.md)
|
|
71
71
|
|
|
72
72
|
## Features
|
|
@@ -74,7 +74,7 @@ Therefore every time this library is updated all files MUST be regenerated using
|
|
|
74
74
|
1. Simple one-off [setup](./docs/quickstart.md) for native projects
|
|
75
75
|
1. No need to mess with xcode schemes or android flavors
|
|
76
76
|
1. Access from [javascript](./docs/api.md#javascript)
|
|
77
|
-
1. Access from native code: [
|
|
77
|
+
1. Access from native code: [Java](./docs/api.md#java), [Kotlin](./docs/api.md#kotlin), [Objective-C](./docs/api.md#objective-c), and [Swift](./docs/api.md#swift)
|
|
78
78
|
1. Access in build tools: [xcode](./docs/api.md#ios), [gradle](./docs/api.md#buildgradle) and [AndroidManifest.xml](./docs/api.md#androidmanifestxml)
|
|
79
79
|
1. [Web support](./docs/api.md#web) (works with React Native for Web)
|
|
80
80
|
1. [Hooks](./docs/api.md#hooks)
|
|
@@ -83,7 +83,7 @@ Therefore every time this library is updated all files MUST be regenerated using
|
|
|
83
83
|
1. **[Multi-env file merging](./docs/api.md#multi-env-file-merging)** — `rncu .env.base .env.staging` (v7+)
|
|
84
84
|
1. **[Dotenv variable expansion](./docs/api.md#dotenv-variable-expansion)** — `API_URL=$BASE_URL/v1` (v7+)
|
|
85
85
|
1. **[Schema validation](./docs/api.md#schema-validation)** — fail at build time on missing or invalid vars (v7+)
|
|
86
|
-
1. Unit tested with jest (
|
|
86
|
+
1. Unit tested with jest (136 tests, 93%+ coverage)
|
|
87
87
|
1. Written in TypeScript with strict mode — [exact typings](./docs/api.md#typescript) generated for your env vars
|
|
88
88
|
1. Supports [dotenv and yaml](./docs/api.md#files)
|
|
89
89
|
1. [Fully typed](./docs/api.md#note-about-types) values available when using yaml config
|
|
@@ -107,10 +107,10 @@ by abstracting away from nuances of native projects.
|
|
|
107
107
|
With `react-native-config-ultimate` it is possible to [consume](./docs/api.md) variables in
|
|
108
108
|
every place of a typical react-native app:
|
|
109
109
|
|
|
110
|
-
- javascript
|
|
110
|
+
- javascript / typescript
|
|
111
111
|
- native code
|
|
112
|
-
- java
|
|
113
|
-
- objective-c
|
|
112
|
+
- java / kotlin
|
|
113
|
+
- objective-c / swift
|
|
114
114
|
- native build configuration
|
|
115
115
|
- ios
|
|
116
116
|
- build settings
|
|
@@ -123,11 +123,11 @@ every place of a typical react-native app:
|
|
|
123
123
|
```
|
|
124
124
|
|-------------------------------------------------------|
|
|
125
125
|
| |
|
|
126
|
-
|
|
|
126
|
+
| javascript / typescript |
|
|
127
127
|
| |
|
|
128
128
|
|-------------------------------------------------------|
|
|
129
129
|
| | |
|
|
130
|
-
|
|
|
130
|
+
| objective-c / swift | java / kotlin |
|
|
131
131
|
| | |
|
|
132
132
|
|-------------------------------------------------------|
|
|
133
133
|
| | |
|
|
@@ -136,3 +136,15 @@ every place of a typical react-native app:
|
|
|
136
136
|
| | |
|
|
137
137
|
|-------------------------------------------------------|
|
|
138
138
|
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Releases
|
|
143
|
+
|
|
144
|
+
This project uses [release-please](https://github.com/googleapis/release-please) for automated releases. Every merge to `master` with conventional commits (`feat:`, `fix:`, etc.) will automatically:
|
|
145
|
+
|
|
146
|
+
1. Create/update a Release PR with changelog
|
|
147
|
+
2. When merged, create a GitHub Release
|
|
148
|
+
3. Publish to npm
|
|
149
|
+
|
|
150
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for commit conventions.
|
package/android/build.gradle
CHANGED
|
@@ -91,6 +91,19 @@ android {
|
|
|
91
91
|
sourceCompatibility JavaVersion.VERSION_17
|
|
92
92
|
targetCompatibility JavaVersion.VERSION_17
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
// Select the correct source set based on architecture.
|
|
96
|
+
// - newarch/: extends NativeUltimateConfigSpec (Codegen-generated TurboModule base)
|
|
97
|
+
// - oldarch/: extends ReactContextBaseJavaModule (classic bridge)
|
|
98
|
+
sourceSets {
|
|
99
|
+
main {
|
|
100
|
+
if (isNewArchitectureEnabled()) {
|
|
101
|
+
java.srcDirs += 'src/newarch/java'
|
|
102
|
+
} else {
|
|
103
|
+
java.srcDirs += 'src/oldarch/java'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
repositories {
|
package/android/rncu.gradle
CHANGED
|
@@ -6,15 +6,35 @@ buildscript {
|
|
|
6
6
|
mavenCentral()
|
|
7
7
|
}
|
|
8
8
|
dependencies {
|
|
9
|
-
classpath group: 'org.yaml', name: 'snakeyaml', version: '1.
|
|
9
|
+
classpath group: 'org.yaml', name: 'snakeyaml', version: '1.33'
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def rncuError(String msg) {
|
|
15
|
+
throw new GradleException(
|
|
16
|
+
"\n\n" +
|
|
17
|
+
"════════════════════════════════════════════════════════════\n" +
|
|
18
|
+
" react-native-config-ultimate — Android build error\n" +
|
|
19
|
+
"════════════════════════════════════════════════════════════\n" +
|
|
20
|
+
" ${msg}\n\n" +
|
|
21
|
+
" Fix: run this from your React Native project root:\n\n" +
|
|
22
|
+
" npx rncu .env\n\n" +
|
|
23
|
+
" Docs: https://github.com/javier545dev/react-native-config-ultimate\n" +
|
|
24
|
+
"════════════════════════════════════════════════════════════\n"
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
def readYaml(yamlPath) {
|
|
15
29
|
def yamlFile = new File(yamlPath.toAbsolutePath().toString())
|
|
16
30
|
if (!yamlFile.exists()) {
|
|
17
|
-
|
|
31
|
+
rncuError(
|
|
32
|
+
"rncu.yaml not found at:\n" +
|
|
33
|
+
" ${yamlPath.toAbsolutePath()}\n\n" +
|
|
34
|
+
" This file is generated by the rncu CLI. It is missing\n" +
|
|
35
|
+
" because you have not run rncu yet (or ran npm install\n" +
|
|
36
|
+
" after the last rncu run, which resets the file)."
|
|
37
|
+
)
|
|
18
38
|
}
|
|
19
39
|
def cfg = new org.yaml.snakeyaml.Yaml().load(yamlFile.newInputStream())
|
|
20
40
|
if (
|
|
@@ -23,9 +43,9 @@ def readYaml(yamlPath) {
|
|
|
23
43
|
|| cfg instanceof Double
|
|
24
44
|
|| cfg instanceof Boolean
|
|
25
45
|
) {
|
|
26
|
-
|
|
46
|
+
rncuError("rncu.yaml is not a valid key-value map (got '${cfg.class.name}'). Re-run: npx rncu .env")
|
|
27
47
|
} else if (cfg == null) {
|
|
28
|
-
|
|
48
|
+
rncuError("rncu.yaml is empty. Re-run: npx rncu .env")
|
|
29
49
|
}
|
|
30
50
|
return cfg
|
|
31
51
|
}
|
|
@@ -35,6 +35,7 @@ public class UltimateConfigPackage extends TurboReactPackage {
|
|
|
35
35
|
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
36
36
|
return () -> {
|
|
37
37
|
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
|
|
38
|
+
boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
|
38
39
|
moduleInfos.put(
|
|
39
40
|
UltimateConfigModule.NAME,
|
|
40
41
|
new ReactModuleInfo(
|
|
@@ -42,9 +43,8 @@ public class UltimateConfigPackage extends TurboReactPackage {
|
|
|
42
43
|
UltimateConfigModule.NAME,
|
|
43
44
|
false, // canOverrideExistingModule
|
|
44
45
|
false, // needsEagerInit
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
BuildConfig.IS_NEW_ARCHITECTURE_ENABLED // isTurboModule
|
|
46
|
+
isTurboModule, // isCxxModule — true for TurboModules
|
|
47
|
+
isTurboModule // isTurboModule
|
|
48
48
|
)
|
|
49
49
|
);
|
|
50
50
|
return moduleInfos;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package com.reactnativeultimateconfig;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
import androidx.annotation.Nullable;
|
|
7
|
+
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
9
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
10
|
+
|
|
11
|
+
import org.json.JSONObject;
|
|
12
|
+
|
|
13
|
+
import java.util.HashMap;
|
|
14
|
+
import java.util.Map;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* New Architecture (TurboModules) implementation of UltimateConfig.
|
|
18
|
+
*
|
|
19
|
+
* Extends the Codegen-generated NativeUltimateConfigSpec which provides
|
|
20
|
+
* the TurboModule binding. Implements getAll() which returns config
|
|
21
|
+
* values as a JSON string (matching the TypeScript spec).
|
|
22
|
+
*/
|
|
23
|
+
@ReactModule(name = UltimateConfigModule.NAME)
|
|
24
|
+
public class UltimateConfigModule extends NativeUltimateConfigSpec {
|
|
25
|
+
public static final String NAME = "UltimateConfig";
|
|
26
|
+
|
|
27
|
+
@Nullable
|
|
28
|
+
private static Class<?> _buildConfig;
|
|
29
|
+
|
|
30
|
+
public static void setBuildConfig(Class<?> buildConfig) {
|
|
31
|
+
_buildConfig = buildConfig;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public UltimateConfigModule(ReactApplicationContext reactContext) {
|
|
35
|
+
super(reactContext);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Override
|
|
39
|
+
@NonNull
|
|
40
|
+
public String getName() {
|
|
41
|
+
return NAME;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read config values from the app's BuildConfig class.
|
|
46
|
+
* The rncu CLI generates fields in BuildConfig via rncu.yaml → build.gradle.
|
|
47
|
+
*/
|
|
48
|
+
@NonNull
|
|
49
|
+
private Map<String, Object> readConfigValues() {
|
|
50
|
+
final Map<String, Object> constants = new HashMap<>();
|
|
51
|
+
try {
|
|
52
|
+
Class<?> act = _buildConfig;
|
|
53
|
+
if (act == null) return constants;
|
|
54
|
+
|
|
55
|
+
// Try both key names for backwards compatibility
|
|
56
|
+
String keys = null;
|
|
57
|
+
try {
|
|
58
|
+
keys = (String) act.getField("__RNCU_KEYS").get(act);
|
|
59
|
+
} catch (NoSuchFieldException e1) {
|
|
60
|
+
try {
|
|
61
|
+
keys = (String) act.getField("__RNUC_KEYS").get(act);
|
|
62
|
+
} catch (NoSuchFieldException e2) {
|
|
63
|
+
String msg = "react-native-config-ultimate: Neither __RNCU_KEYS nor __RNUC_KEYS found in BuildConfig. " +
|
|
64
|
+
"Did you run 'npx rncu <env-file>' and rebuild?";
|
|
65
|
+
if (BuildConfig.DEBUG) {
|
|
66
|
+
throw new RuntimeException(msg);
|
|
67
|
+
} else {
|
|
68
|
+
Log.e(NAME, msg);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (keys == null || keys.isEmpty()) return constants;
|
|
74
|
+
for (String k : keys.split(",")) {
|
|
75
|
+
Object value = act.getField(k).get(act);
|
|
76
|
+
if (value != null) {
|
|
77
|
+
constants.put(k, value);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (Exception e) {
|
|
81
|
+
Log.w(NAME, "Failed to read config constants from BuildConfig: " + e.getMessage());
|
|
82
|
+
}
|
|
83
|
+
return constants;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* TurboModule method: returns all config values as a JSON string.
|
|
88
|
+
* This matches the TypeScript spec: getAll(): string
|
|
89
|
+
*/
|
|
90
|
+
@Override
|
|
91
|
+
@NonNull
|
|
92
|
+
public String getAll() {
|
|
93
|
+
Map<String, Object> values = readConfigValues();
|
|
94
|
+
try {
|
|
95
|
+
JSONObject json = new JSONObject(values);
|
|
96
|
+
return json.toString();
|
|
97
|
+
} catch (Exception e) {
|
|
98
|
+
Log.w(NAME, "Failed to serialize config to JSON: " + e.getMessage());
|
|
99
|
+
return "{}";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
package/android/src/{main → oldarch}/java/com/reactnativeultimateconfig/UltimateConfigModule.java
RENAMED
|
@@ -9,9 +9,17 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
|
|
9
9
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
10
10
|
import com.facebook.react.module.annotations.ReactModule;
|
|
11
11
|
|
|
12
|
+
import org.json.JSONObject;
|
|
13
|
+
|
|
12
14
|
import java.util.HashMap;
|
|
13
15
|
import java.util.Map;
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Old Architecture (Bridge) implementation of UltimateConfig.
|
|
19
|
+
*
|
|
20
|
+
* This module exposes config values via getConstants() which React Native's
|
|
21
|
+
* bridge reads at module initialization time.
|
|
22
|
+
*/
|
|
15
23
|
@ReactModule(name = UltimateConfigModule.NAME)
|
|
16
24
|
public class UltimateConfigModule extends ReactContextBaseJavaModule {
|
|
17
25
|
public static final String NAME = "UltimateConfig";
|
|
@@ -33,14 +41,35 @@ public class UltimateConfigModule extends ReactContextBaseJavaModule {
|
|
|
33
41
|
return NAME;
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Read config values from the app's BuildConfig class.
|
|
46
|
+
* The rncu CLI generates fields in BuildConfig via rncu.yaml → build.gradle.
|
|
47
|
+
*/
|
|
37
48
|
@NonNull
|
|
38
|
-
|
|
49
|
+
private Map<String, Object> readConfigValues() {
|
|
39
50
|
final Map<String, Object> constants = new HashMap<>();
|
|
40
51
|
try {
|
|
41
52
|
Class<?> act = _buildConfig;
|
|
42
53
|
if (act == null) return constants;
|
|
43
|
-
|
|
54
|
+
|
|
55
|
+
// Try both key names for backwards compatibility
|
|
56
|
+
String keys = null;
|
|
57
|
+
try {
|
|
58
|
+
keys = (String) act.getField("__RNCU_KEYS").get(act);
|
|
59
|
+
} catch (NoSuchFieldException e1) {
|
|
60
|
+
try {
|
|
61
|
+
keys = (String) act.getField("__RNUC_KEYS").get(act);
|
|
62
|
+
} catch (NoSuchFieldException e2) {
|
|
63
|
+
String msg = "react-native-config-ultimate: Neither __RNCU_KEYS nor __RNUC_KEYS found in BuildConfig. " +
|
|
64
|
+
"Did you run 'npx rncu <env-file>' and rebuild?";
|
|
65
|
+
if (BuildConfig.DEBUG) {
|
|
66
|
+
throw new RuntimeException(msg);
|
|
67
|
+
} else {
|
|
68
|
+
Log.e(NAME, msg);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
44
73
|
if (keys == null || keys.isEmpty()) return constants;
|
|
45
74
|
for (String k : keys.split(",")) {
|
|
46
75
|
Object value = act.getField(k).get(act);
|
|
@@ -53,4 +82,10 @@ public class UltimateConfigModule extends ReactContextBaseJavaModule {
|
|
|
53
82
|
}
|
|
54
83
|
return constants;
|
|
55
84
|
}
|
|
85
|
+
|
|
86
|
+
@Override
|
|
87
|
+
@NonNull
|
|
88
|
+
public Map<String, Object> getConstants() {
|
|
89
|
+
return readConfigValues();
|
|
90
|
+
}
|
|
56
91
|
}
|
package/index.js
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var _a;
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
const react_native_1 = require("react-native");
|
|
4
5
|
// override.js is dynamically generated by the rncu CLI.
|
|
5
6
|
// It contains platform-specific overrides. Do not commit override.js to git.
|
|
6
7
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
7
8
|
const override = require('./override');
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
// New Architecture: TurboModules are accessed via TurboModuleRegistry.
|
|
10
|
+
// The spec exposes getAll(): string — returns config values as JSON.
|
|
11
|
+
// Old Architecture: constants are exposed directly on NativeModules via constantsToExport.
|
|
12
|
+
const turboModule = react_native_1.TurboModuleRegistry.get('UltimateConfig');
|
|
13
|
+
let nativeConstants;
|
|
14
|
+
if (turboModule != null && typeof turboModule.getAll === 'function') {
|
|
15
|
+
// New Arch: call getAll() and parse JSON
|
|
16
|
+
try {
|
|
17
|
+
const raw = turboModule.getAll();
|
|
18
|
+
nativeConstants = JSON.parse(raw);
|
|
19
|
+
}
|
|
20
|
+
catch (_b) {
|
|
21
|
+
nativeConstants = {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
// Old Arch / interop bridge: constants are properties on NativeModules.UltimateConfig
|
|
26
|
+
nativeConstants = (_a = react_native_1.NativeModules.UltimateConfig) !== null && _a !== void 0 ? _a : {};
|
|
27
|
+
}
|
|
28
|
+
exports.default = Object.assign(Object.assign({}, nativeConstants), override);
|
package/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { NativeModules } from 'react-native';
|
|
2
|
-
import type {
|
|
1
|
+
import { TurboModuleRegistry, NativeModules } from 'react-native';
|
|
2
|
+
import type { Spec } from './src/NativeUltimateConfig';
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type ConfigValue = string | number | boolean;
|
|
5
|
+
export type { Spec } from './src/NativeUltimateConfig';
|
|
5
6
|
|
|
6
7
|
type Config = Record<string, ConfigValue>;
|
|
7
8
|
|
|
@@ -10,9 +11,27 @@ type Config = Record<string, ConfigValue>;
|
|
|
10
11
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
11
12
|
const override: Config = require('./override');
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
// New Architecture: TurboModules are accessed via TurboModuleRegistry.
|
|
15
|
+
// The spec exposes getAll(): string — returns config values as JSON.
|
|
16
|
+
// Old Architecture: constants are exposed directly on NativeModules via constantsToExport.
|
|
17
|
+
const turboModule = TurboModuleRegistry.get<Spec>('UltimateConfig');
|
|
18
|
+
|
|
19
|
+
let nativeConstants: Config;
|
|
20
|
+
|
|
21
|
+
if (turboModule != null && typeof turboModule.getAll === 'function') {
|
|
22
|
+
// New Arch: call getAll() and parse JSON
|
|
23
|
+
try {
|
|
24
|
+
const raw = turboModule.getAll();
|
|
25
|
+
nativeConstants = JSON.parse(raw) as Config;
|
|
26
|
+
} catch {
|
|
27
|
+
nativeConstants = {};
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
// Old Arch / interop bridge: constants are properties on NativeModules.UltimateConfig
|
|
31
|
+
nativeConstants = (NativeModules.UltimateConfig as Config | undefined) ?? {};
|
|
32
|
+
}
|
|
14
33
|
|
|
15
34
|
export default {
|
|
16
|
-
...
|
|
35
|
+
...nativeConstants,
|
|
17
36
|
...override,
|
|
18
37
|
} as Config;
|
package/ios/ConfigValues.h
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
#error "
|
|
1
|
+
#error "react-native-config-ultimate: ConfigValues.h not generated. Run: npx rncu .env from your project root before building iOS.
|
|
2
|
+
"
|
package/ios/UltimateConfig.mm
CHANGED
|
@@ -6,7 +6,6 @@ RCT_EXPORT_MODULE()
|
|
|
6
6
|
|
|
7
7
|
+ (BOOL)requiresMainQueueSetup
|
|
8
8
|
{
|
|
9
|
-
// getConstants() reads from a static in-memory struct — no UI or main thread access needed.
|
|
10
9
|
return NO;
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -15,8 +14,23 @@ RCT_EXPORT_MODULE()
|
|
|
15
14
|
return getValues();
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
// Don't compile this code when we build for the old architecture.
|
|
19
17
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
18
|
+
// New Architecture: TurboModule method exposed via Codegen-generated spec.
|
|
19
|
+
// getAll() returns all config values as a JSON-encoded string.
|
|
20
|
+
// Using string avoids Codegen limitations with dynamic/indexed object types.
|
|
21
|
+
- (NSString *)getAll
|
|
22
|
+
{
|
|
23
|
+
NSDictionary *values = getValues();
|
|
24
|
+
NSError *error = nil;
|
|
25
|
+
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:values
|
|
26
|
+
options:0
|
|
27
|
+
error:&error];
|
|
28
|
+
if (error || !jsonData) {
|
|
29
|
+
return @"{}";
|
|
30
|
+
}
|
|
31
|
+
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
|
32
|
+
}
|
|
33
|
+
|
|
20
34
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
21
35
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
22
36
|
{
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-config-ultimate",
|
|
3
3
|
"title": "React Native Config Ultimate",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"description": "Config that works. A community-maintained fork of react-native-ultimate-config.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"react-native": "index.js",
|
|
8
8
|
"browser": "index.web.js",
|
|
9
9
|
"types": "index.d.ts",
|
|
10
10
|
"files": [
|
|
11
|
+
"bin.js",
|
|
12
|
+
"index.js",
|
|
13
|
+
"index.d.ts",
|
|
14
|
+
"index.web.js",
|
|
11
15
|
"src/*.js",
|
|
16
|
+
"!src/*.spec.js",
|
|
12
17
|
"src/*.ts",
|
|
18
|
+
"!src/*.spec.ts",
|
|
13
19
|
"src/templates",
|
|
14
|
-
"index.js",
|
|
15
20
|
"index.ts",
|
|
16
21
|
"override.js",
|
|
17
22
|
"ios/UltimateConfig.{h,mm}",
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
"typecheck": "tsc --noEmit",
|
|
28
33
|
"pretest": "tsc",
|
|
29
34
|
"test": "jest",
|
|
30
|
-
"prepack": "
|
|
35
|
+
"prepack": "printf '#error \"react-native-config-ultimate: ConfigValues.h not generated. Run: npx rncu .env from your project root before building iOS.\\n\"' > ios/ConfigValues.h && echo 'module.exports = {};' > override.js",
|
|
31
36
|
"lint": "eslint src/**/*.ts index.ts --max-warnings=0",
|
|
32
37
|
"format": "prettier --write src/**/*.ts index.ts"
|
|
33
38
|
},
|
|
@@ -55,8 +60,13 @@
|
|
|
55
60
|
"xcconfig",
|
|
56
61
|
"gradle",
|
|
57
62
|
"objective-c",
|
|
63
|
+
"swift",
|
|
58
64
|
"java",
|
|
59
|
-
"
|
|
65
|
+
"kotlin",
|
|
66
|
+
"typescript",
|
|
67
|
+
"yaml",
|
|
68
|
+
"new-architecture",
|
|
69
|
+
"turbomodules"
|
|
60
70
|
],
|
|
61
71
|
"author": {
|
|
62
72
|
"name": "Javier Fuentes",
|
|
@@ -71,7 +81,7 @@
|
|
|
71
81
|
],
|
|
72
82
|
"license": "MIT",
|
|
73
83
|
"peerDependencies": {
|
|
74
|
-
"react-native": ">=0.
|
|
84
|
+
"react-native": ">=0.73.0 <1.0.x"
|
|
75
85
|
},
|
|
76
86
|
"devDependencies": {
|
|
77
87
|
"@types/jest": "^29.5.0",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
-
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
4
|
|
|
6
5
|
Pod::Spec.new do |s|
|
|
7
6
|
s.name = "react-native-config-ultimate"
|
|
@@ -24,18 +23,9 @@ Pod::Spec.new do |s|
|
|
|
24
23
|
|
|
25
24
|
s.dependency "React-Core"
|
|
26
25
|
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
33
|
-
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
34
|
-
}
|
|
35
|
-
s.dependency "React-Codegen"
|
|
36
|
-
s.dependency "RCT-Folly"
|
|
37
|
-
s.dependency "RCTRequired"
|
|
38
|
-
s.dependency "RCTTypeSafety"
|
|
39
|
-
s.dependency "ReactCommon/turbomodule/core"
|
|
40
|
-
end
|
|
26
|
+
# install_modules_dependencies handles all New Architecture dependencies automatically:
|
|
27
|
+
# React-Codegen, RCT-Folly, RCTRequired, RCTTypeSafety, ReactCommon/turbomodule/core,
|
|
28
|
+
# React-NativeModulesApple (provides RCTTurboModule.h), and sets compiler flags.
|
|
29
|
+
# This is the canonical approach since RN 0.71.
|
|
30
|
+
install_modules_dependencies(s)
|
|
41
31
|
end
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TurboModule spec for react-native-ultimate
|
|
2
|
+
* TurboModule spec for react-native-config-ultimate
|
|
3
3
|
* This file is read by React Native Codegen to generate native bindings.
|
|
4
4
|
* Do not rename or move this file - Codegen relies on the "Native" prefix.
|
|
5
|
+
*
|
|
6
|
+
* NOTE: We use getAll(): string instead of getConstants(): {} because:
|
|
7
|
+
* - Codegen requires getConstants() to have a concrete return type with named fields
|
|
8
|
+
* - Config keys are dynamic (generated from .env at build time), so a fixed type is impossible
|
|
9
|
+
* - getAll() returns all config values as a JSON-encoded string, which Codegen handles perfectly
|
|
10
|
+
* - This gives us a working TurboModule binding for New Architecture
|
|
5
11
|
*/
|
|
6
12
|
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
|
|
7
13
|
import { TurboModuleRegistry } from 'react-native';
|
|
8
14
|
|
|
9
|
-
export type ConfigValue = string | number | boolean;
|
|
10
|
-
|
|
11
15
|
export interface Spec extends TurboModule {
|
|
12
|
-
|
|
16
|
+
// Returns all config values as a JSON-encoded string.
|
|
17
|
+
// Using string avoids Codegen limitations with dynamic/indexed object types.
|
|
18
|
+
readonly getAll: () => string;
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
export default TurboModuleRegistry.get<Spec>('UltimateConfig');
|
package/src/cli.js
CHANGED
|
@@ -105,7 +105,9 @@ async function cli() {
|
|
|
105
105
|
return conventional;
|
|
106
106
|
// Otherwise, try require.resolve to handle hoisted workspaces.
|
|
107
107
|
try {
|
|
108
|
-
const pkg_json = require.resolve('react-native-config-ultimate/package.json', {
|
|
108
|
+
const pkg_json = require.resolve('react-native-config-ultimate/package.json', {
|
|
109
|
+
paths: [project_root],
|
|
110
|
+
});
|
|
109
111
|
return path.dirname(pkg_json);
|
|
110
112
|
}
|
|
111
113
|
catch (_a) {
|
|
@@ -153,10 +155,7 @@ async function cli() {
|
|
|
153
155
|
// Initial run (errors are caught — we still want to start watching).
|
|
154
156
|
await run();
|
|
155
157
|
// Files to watch: env files + RC file (if it exists).
|
|
156
|
-
const files_to_watch = [
|
|
157
|
-
...env_files,
|
|
158
|
-
...(fs.existsSync(rc_file) ? [rc_file] : []),
|
|
159
|
-
];
|
|
158
|
+
const files_to_watch = [...env_files, ...(fs.existsSync(rc_file) ? [rc_file] : [])];
|
|
160
159
|
const watcher = (0, chokidar_1.watch)(files_to_watch, {
|
|
161
160
|
ignoreInitial: true,
|
|
162
161
|
persistent: true,
|
package/src/cli.ts
CHANGED
|
@@ -67,11 +67,7 @@ export default async function cli(): Promise<void> {
|
|
|
67
67
|
const lib_root: string = (() => {
|
|
68
68
|
if (argv.libRoot) return argv.libRoot;
|
|
69
69
|
|
|
70
|
-
const conventional = path.join(
|
|
71
|
-
project_root,
|
|
72
|
-
'node_modules',
|
|
73
|
-
'react-native-config-ultimate'
|
|
74
|
-
);
|
|
70
|
+
const conventional = path.join(project_root, 'node_modules', 'react-native-config-ultimate');
|
|
75
71
|
|
|
76
72
|
// If the directory already exists at the conventional location, use it.
|
|
77
73
|
// This handles standard installs and the integration test temp-dir setup.
|
|
@@ -79,10 +75,9 @@ export default async function cli(): Promise<void> {
|
|
|
79
75
|
|
|
80
76
|
// Otherwise, try require.resolve to handle hoisted workspaces.
|
|
81
77
|
try {
|
|
82
|
-
const pkg_json = require.resolve(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
78
|
+
const pkg_json = require.resolve('react-native-config-ultimate/package.json', {
|
|
79
|
+
paths: [project_root],
|
|
80
|
+
});
|
|
86
81
|
return path.dirname(pkg_json);
|
|
87
82
|
} catch {
|
|
88
83
|
// Last resort: return the conventional path and let write-env create it.
|
|
@@ -135,10 +130,7 @@ export default async function cli(): Promise<void> {
|
|
|
135
130
|
await run();
|
|
136
131
|
|
|
137
132
|
// Files to watch: env files + RC file (if it exists).
|
|
138
|
-
const files_to_watch = [
|
|
139
|
-
...env_files,
|
|
140
|
-
...(fs.existsSync(rc_file) ? [rc_file] : []),
|
|
141
|
-
];
|
|
133
|
+
const files_to_watch = [...env_files, ...(fs.existsSync(rc_file) ? [rc_file] : [])];
|
|
142
134
|
|
|
143
135
|
const watcher = watch(files_to_watch, {
|
|
144
136
|
ignoreInitial: true,
|