react-native-config-ultimate 0.0.1
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/LICENSE +21 -0
- package/README.md +138 -0
- package/android/build.gradle +180 -0
- package/android/rncu.gradle +132 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/reactnativeultimateconfig/UltimateConfigModule.java +56 -0
- package/android/src/main/java/com/reactnativeultimateconfig/UltimateConfigPackage.java +53 -0
- package/bin.js +5 -0
- package/index.js +9 -0
- package/index.ts +18 -0
- package/ios/ConfigValues.h +1 -0
- package/ios/UltimateConfig.h +12 -0
- package/ios/UltimateConfig.mm +27 -0
- package/ios/UltimateConfig.xcodeproj/project.pbxproj +274 -0
- package/override.js +1 -0
- package/package.json +110 -0
- package/react-native-config-ultimate.podspec +41 -0
- package/src/NativeUltimateConfig.js +4 -0
- package/src/NativeUltimateConfig.ts +15 -0
- package/src/bin.spec.ts +36 -0
- package/src/cli.js +177 -0
- package/src/cli.spec.ts +224 -0
- package/src/cli.ts +166 -0
- package/src/flatten.js +22 -0
- package/src/flatten.spec.ts +16 -0
- package/src/flatten.ts +26 -0
- package/src/load-env.js +107 -0
- package/src/load-env.spec.ts +163 -0
- package/src/load-env.ts +84 -0
- package/src/main.js +34 -0
- package/src/main.spec.ts +171 -0
- package/src/main.ts +39 -0
- package/src/render-env.js +110 -0
- package/src/render-env.ts +115 -0
- package/src/resolve-env.js +12 -0
- package/src/resolve-env.spec.ts +25 -0
- package/src/resolve-env.ts +45 -0
- package/src/templates/ConfigValues.h.handlebars +24 -0
- package/src/templates/index.d.ts.handlebars +18 -0
- package/src/templates/index.web.js.handlebars +1 -0
- package/src/templates/override.js.handlebars +16 -0
- package/src/templates/rncu.xcconfig.handlebars +4 -0
- package/src/templates/rncu.yaml.handlebars +7 -0
- package/src/validate-env.js +53 -0
- package/src/validate-env.spec.ts +164 -0
- package/src/validate-env.ts +68 -0
- package/src/write-env.js +99 -0
- package/src/write-env.spec.ts +105 -0
- package/src/write-env.ts +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Maksym Komarychev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# react-native-config-ultimate
|
|
2
|
+
|
|
3
|
+
_Config that works_
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/react-native-config-ultimate)
|
|
6
|
+
[](https://www.npmjs.com/package/react-native-config-ultimate)
|
|
7
|
+
[](https://www.npmjs.com/package/react-native-config-ultimate)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
> **This is a community-maintained fork** of
|
|
12
|
+
> [`react-native-ultimate-config`](https://github.com/maxkomarychev/react-native-ultimate-config)
|
|
13
|
+
> originally created by [Max Komarychev](https://github.com/maxkomarychev).
|
|
14
|
+
>
|
|
15
|
+
> The original library has not received updates since September 2023 and does not
|
|
16
|
+
> support React Native's New Architecture (TurboModules), React 19, or modern
|
|
17
|
+
> tooling. This fork picks up where it left off.
|
|
18
|
+
>
|
|
19
|
+
> **Full credit and gratitude to Max** for the original design, architecture, and
|
|
20
|
+
> years of maintenance. This project would not exist without his work.
|
|
21
|
+
> The MIT license is preserved in its entirety.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Gradle compatibility
|
|
26
|
+
|
|
27
|
+
| react-native-config-ultimate | gradle |
|
|
28
|
+
| ---------------------------- | ------ |
|
|
29
|
+
| ^7 | 8 |
|
|
30
|
+
|
|
31
|
+
> For older versions see the original package [`react-native-ultimate-config`](https://github.com/maxkomarychev/react-native-ultimate-config).
|
|
32
|
+
|
|
33
|
+
## React Native compatibility
|
|
34
|
+
|
|
35
|
+
| react-native-config-ultimate | react-native | react | New Architecture |
|
|
36
|
+
| ---------------------------- | ------------ | ------ | ---------------- |
|
|
37
|
+
| ^7 | >=0.60 <1.x | >=19 | ✅ TurboModules |
|
|
38
|
+
|
|
39
|
+
## TL;DR usage
|
|
40
|
+
|
|
41
|
+
1. install
|
|
42
|
+
| npm | yarn |
|
|
43
|
+
|-|-|
|
|
44
|
+
|`npm install react-native-config-ultimate` | `yarn add react-native-config-ultimate`|
|
|
45
|
+
2. [one-off setup for native projects](./docs/quickstart.md)
|
|
46
|
+
3. initialize env
|
|
47
|
+
| npm | yarn |
|
|
48
|
+
|-|-|
|
|
49
|
+
|`npx rncu .env`|`yarn rncu .env`|
|
|
50
|
+
4. build! `react-native run-{ios,android}`
|
|
51
|
+
|
|
52
|
+
## ☝❗Approach to versioning and breaking changes
|
|
53
|
+
|
|
54
|
+
This library is using [semver](https://semver.org/) and heavily relying on codegeneration. Many new features and/or bugfixes will require these files to be regenerated. Changes to codegenerated files will not be considered breaking
|
|
55
|
+
unless they affect behavior of API or CLI.
|
|
56
|
+
|
|
57
|
+
Therefore every time this library is updated all files MUST be regenerated using `rncu` command.
|
|
58
|
+
|
|
59
|
+
## Table of contents
|
|
60
|
+
|
|
61
|
+
1. [Features 🎆](#features)
|
|
62
|
+
1. [Mission 🥾](#mission)
|
|
63
|
+
1. [Quickstart Guide 🏃](./docs/quickstart.md)
|
|
64
|
+
1. [API 🧰](./docs/api.md)
|
|
65
|
+
1. [Testing Guide 🧪](./docs/testing.md)
|
|
66
|
+
1. [Changelog 📓](./packages/react-native-ultimate-config/CHANGELOG.md)
|
|
67
|
+
1. [Cookbook 🥦](./docs/cookbook.md)
|
|
68
|
+
1. [Troubleshooting 🎱](./docs/troubleshooting.md)
|
|
69
|
+
1. [Contributor notes](./docs/contributor-notes.md)
|
|
70
|
+
1. [Alternatives](./docs/alternatives.md)
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
1. Simple one-off [setup](./docs/quickstart.md) for native projects
|
|
75
|
+
1. No need to mess with xcode schemes or android flavors
|
|
76
|
+
1. Access from [javascript](./docs/api.md#javascript)
|
|
77
|
+
1. Access from native code: [java](./docs/api.md#java) and [objective-c](./docs/api.md#objective-c)
|
|
78
|
+
1. Access in build tools: [xcode](./docs/api.md#ios), [gradle](./docs/api.md#buildgradle) and [AndroidManifest.xml](./docs/api.md#androidmanifestxml)
|
|
79
|
+
1. [Web support](./docs/api.md#web) (works with React Native for Web)
|
|
80
|
+
1. [Hooks](./docs/api.md#hooks)
|
|
81
|
+
1. [Monorepo support](./docs/monorepo-tips.md) (yarn workspaces or lerna)
|
|
82
|
+
1. **[New Architecture](./docs/api.md#new-architecture)** — TurboModules support (RN 0.68+), fully backward-compatible with old arch
|
|
83
|
+
1. **[Multi-env file merging](./docs/api.md#multi-env-file-merging)** — `rncu .env.base .env.staging` (v7+)
|
|
84
|
+
1. **[Dotenv variable expansion](./docs/api.md#dotenv-variable-expansion)** — `API_URL=$BASE_URL/v1` (v7+)
|
|
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 (83 tests)
|
|
87
|
+
1. Written in TypeScript with strict mode — [exact typings](./docs/api.md#typescript) generated for your env vars
|
|
88
|
+
1. Supports [dotenv and yaml](./docs/api.md#files)
|
|
89
|
+
1. [Fully typed](./docs/api.md#note-about-types) values available when using yaml config
|
|
90
|
+
1. Configure values [per platform](./docs/api.md#per-platform-values) in one file
|
|
91
|
+
|
|
92
|
+
## Mission
|
|
93
|
+
|
|
94
|
+
React-Native brings together 3 platforms: ios, android, javascript each of
|
|
95
|
+
which have different conventions and approaches how to manage environment
|
|
96
|
+
settings.
|
|
97
|
+
|
|
98
|
+
A typical app is usually operating in some environment defined by server urls
|
|
99
|
+
various api keys or feature flags. When dealing with react-native such things
|
|
100
|
+
often need to exist in 3 places: ios, android and js code. Even managing things
|
|
101
|
+
as simple as application name or bundle id needs to be done in 2 places:
|
|
102
|
+
`Info.plist` and `AndroidManifest.xml`
|
|
103
|
+
|
|
104
|
+
`react-native-config-ultimate` tries to reduce friction in managing these things
|
|
105
|
+
by abstracting away from nuances of native projects.
|
|
106
|
+
|
|
107
|
+
With `react-native-config-ultimate` it is possible to [consume](./docs/api.md) variables in
|
|
108
|
+
every place of a typical react-native app:
|
|
109
|
+
|
|
110
|
+
- javascript
|
|
111
|
+
- native code
|
|
112
|
+
- java
|
|
113
|
+
- objective-c
|
|
114
|
+
- native build configuration
|
|
115
|
+
- ios
|
|
116
|
+
- build settings
|
|
117
|
+
- infoplist
|
|
118
|
+
- android
|
|
119
|
+
- build config
|
|
120
|
+
- string resources
|
|
121
|
+
- project.ext
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
|-------------------------------------------------------|
|
|
125
|
+
| |
|
|
126
|
+
| javascript |
|
|
127
|
+
| |
|
|
128
|
+
|-------------------------------------------------------|
|
|
129
|
+
| | |
|
|
130
|
+
| objective-c | java |
|
|
131
|
+
| | |
|
|
132
|
+
|-------------------------------------------------------|
|
|
133
|
+
| | |
|
|
134
|
+
| build settings | AndroidManifest.xml |
|
|
135
|
+
| infoplist | build.gradle |
|
|
136
|
+
| | |
|
|
137
|
+
|-------------------------------------------------------|
|
|
138
|
+
```
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath "com.android.tools.build:gradle:7.2.1"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def safeExtGet(prop, fallback) {
|
|
13
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def resolveReactNativeDirectory() {
|
|
17
|
+
def reactNativeLocation = safeExtGet("REACT_NATIVE_NODE_MODULES_DIR", null)
|
|
18
|
+
if (reactNativeLocation != null) {
|
|
19
|
+
println "Using react-native dir from REACT_NATIVE_NODE_MODULES_DIR:" + reactNativeLocation
|
|
20
|
+
return file(reactNativeLocation)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// monorepo workaround
|
|
24
|
+
// react-native can be hoisted or in project's own node_modules
|
|
25
|
+
def reactNativeFromProjectNodeModules = file("${rootProject.projectDir}/../node_modules/react-native")
|
|
26
|
+
println "Using react-native dir from default location:" + reactNativeFromProjectNodeModules
|
|
27
|
+
if (reactNativeFromProjectNodeModules.exists()) {
|
|
28
|
+
return reactNativeFromProjectNodeModules
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw new Exception(
|
|
32
|
+
"[react-native-config-ultimate] Unable to resolve react-native location in " +
|
|
33
|
+
"node_modules. You should add project extension property (in app/build.gradle) " +
|
|
34
|
+
"`REACT_NATIVE_NODE_MODULES_DIR` with path to react-native."
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def isNewArchitectureEnabled() {
|
|
39
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
apply plugin: 'com.android.library'
|
|
43
|
+
apply plugin: 'maven-publish'
|
|
44
|
+
|
|
45
|
+
// Apply the React Native Gradle Plugin for New Architecture support (TurboModules, Codegen).
|
|
46
|
+
// When newArchEnabled=true, this plugin runs Codegen to generate NativeUltimateConfigSpec.
|
|
47
|
+
if (isNewArchitectureEnabled()) {
|
|
48
|
+
apply plugin: "com.facebook.react"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def getExtOrDefault(name) {
|
|
52
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["UltimateConfig_" + name]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def getExtOrIntegerDefault(name) {
|
|
56
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["UltimateConfig_" + name]).toInteger()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def REACT_NATIVE_DIR = resolveReactNativeDirectory()
|
|
60
|
+
|
|
61
|
+
def reactProperties = new Properties()
|
|
62
|
+
file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
|
|
63
|
+
|
|
64
|
+
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
|
|
65
|
+
def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
|
|
66
|
+
|
|
67
|
+
android {
|
|
68
|
+
// 'namespace' is required by Android Gradle Plugin 8.0+.
|
|
69
|
+
// For AGP < 8.0 this property is silently ignored.
|
|
70
|
+
namespace "com.reactnativeultimateconfig"
|
|
71
|
+
|
|
72
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
73
|
+
|
|
74
|
+
defaultConfig {
|
|
75
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
76
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
77
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
78
|
+
}
|
|
79
|
+
buildTypes {
|
|
80
|
+
release {
|
|
81
|
+
minifyEnabled false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
lintOptions {
|
|
86
|
+
disable "GradleCompatible"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Java 11 is the minimum for React Native 0.73+; Java 17 is recommended for RN 0.74+.
|
|
90
|
+
compileOptions {
|
|
91
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
92
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
repositories {
|
|
97
|
+
mavenCentral()
|
|
98
|
+
google()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
dependencies {
|
|
103
|
+
// For < 0.71, this will be from the local maven repo.
|
|
104
|
+
// For >= 0.71, version is substituted by the React Native Gradle Plugin (RNGP).
|
|
105
|
+
//noinspection GradleDynamicVersion
|
|
106
|
+
if (REACT_NATIVE_MINOR_VERSION >= 71) {
|
|
107
|
+
implementation "com.facebook.react:react-android:+" // version substituted by RNGP
|
|
108
|
+
} else {
|
|
109
|
+
implementation 'com.facebook.react:react-native:+' // from node_modules
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Codegen configuration — only runs when New Architecture is enabled.
|
|
114
|
+
// Reads NativeUltimateConfig.ts (in src/) and generates NativeUltimateConfigSpec.java.
|
|
115
|
+
if (isNewArchitectureEnabled()) {
|
|
116
|
+
react {
|
|
117
|
+
jsRootDir = file("../src/")
|
|
118
|
+
libraryName = "UltimateConfig"
|
|
119
|
+
codegenJavaPackageName = "com.reactnativeultimateconfig"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
afterEvaluate { project ->
|
|
124
|
+
task androidSourcesJar(type: Jar) {
|
|
125
|
+
archiveClassifier = 'sources'
|
|
126
|
+
from android.sourceSets.main.java.srcDirs
|
|
127
|
+
include '**/*.java'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
android.libraryVariants.all { variant ->
|
|
131
|
+
def name = variant.name.capitalize()
|
|
132
|
+
def javaCompileTask = variant.javaCompileProvider.get()
|
|
133
|
+
|
|
134
|
+
task "jar${name}"(type: Jar, dependsOn: javaCompileTask) {
|
|
135
|
+
from javaCompileTask.destinationDir
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
artifacts {
|
|
140
|
+
archives androidSourcesJar
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
publishing {
|
|
144
|
+
publications {
|
|
145
|
+
maven(MavenPublication) {
|
|
146
|
+
def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text)
|
|
147
|
+
|
|
148
|
+
pom {
|
|
149
|
+
name = packageJson.title
|
|
150
|
+
artifactId = packageJson.name
|
|
151
|
+
version = packageJson.version
|
|
152
|
+
group = "com.reactnativeultimateconfig"
|
|
153
|
+
description packageJson.description
|
|
154
|
+
url = packageJson.repository.url
|
|
155
|
+
|
|
156
|
+
licenses {
|
|
157
|
+
license {
|
|
158
|
+
name = packageJson.license
|
|
159
|
+
url = packageJson.repository.baseUrl + '/blob/master/' + packageJson.licenseFilename
|
|
160
|
+
distribution = 'repo'
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
developers {
|
|
165
|
+
developer {
|
|
166
|
+
id = packageJson.author.username
|
|
167
|
+
name = packageJson.author.name
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
repositories {
|
|
175
|
+
maven {
|
|
176
|
+
url "file://${projectDir}/../android/maven"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// DO NOT COMMIT OR EDIT THIS FILE
|
|
2
|
+
import java.nio.file.Paths
|
|
3
|
+
|
|
4
|
+
buildscript {
|
|
5
|
+
repositories {
|
|
6
|
+
mavenCentral()
|
|
7
|
+
}
|
|
8
|
+
dependencies {
|
|
9
|
+
classpath group: 'org.yaml', name: 'snakeyaml', version: '1.19'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def readYaml(yamlPath) {
|
|
15
|
+
def yamlFile = new File(yamlPath.toAbsolutePath().toString())
|
|
16
|
+
if (!yamlFile.exists()) {
|
|
17
|
+
throw new GradleException("yaml file at path ${yamlPath.toAbsolutePath().toString()} does not exist")
|
|
18
|
+
}
|
|
19
|
+
def cfg = new org.yaml.snakeyaml.Yaml().load(yamlFile.newInputStream())
|
|
20
|
+
if (
|
|
21
|
+
cfg instanceof String
|
|
22
|
+
|| cfg instanceof Integer
|
|
23
|
+
|| cfg instanceof Double
|
|
24
|
+
|| cfg instanceof Boolean
|
|
25
|
+
) {
|
|
26
|
+
throw new GradleException("could not read object from ${yamlPath} but got '${cfg.class.name}'")
|
|
27
|
+
} else if (cfg == null) {
|
|
28
|
+
throw new GradleException("could not read object from ${yamlPath} but got 'null'")
|
|
29
|
+
}
|
|
30
|
+
return cfg
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def buildRootConfig() {
|
|
34
|
+
def flavorMappingExists = project.ext.has('flavorEnvMapping')
|
|
35
|
+
if (flavorMappingExists) {
|
|
36
|
+
def config = [:]
|
|
37
|
+
def flavorMapping = project.ext.get('flavorEnvMapping')
|
|
38
|
+
println "Flavor mapping detected: ${flavorMapping}"
|
|
39
|
+
flavorMapping.each { flavor, yamlPath ->
|
|
40
|
+
def absolutePath = Paths.get(project.rootDir.getAbsolutePath(), yamlPath)
|
|
41
|
+
config.put(flavor, readYaml(absolutePath))
|
|
42
|
+
}
|
|
43
|
+
return config
|
|
44
|
+
} else {
|
|
45
|
+
println "No flavor mapping detected. Reading injected file"
|
|
46
|
+
def cfg = readYaml(Paths.get(buildscript.sourceFile.getParent(), "rncu.yaml"))
|
|
47
|
+
return cfg
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def flattenConfig(cfg) {
|
|
52
|
+
def result = [:]
|
|
53
|
+
cfg.each { k,v ->
|
|
54
|
+
if (
|
|
55
|
+
v instanceof String
|
|
56
|
+
|| v instanceof Integer
|
|
57
|
+
|| v instanceof Double
|
|
58
|
+
|| v instanceof Boolean
|
|
59
|
+
) {
|
|
60
|
+
result.put(k,v)
|
|
61
|
+
} else {
|
|
62
|
+
def androidValue = v.get("android")
|
|
63
|
+
if (androidValue == null) {
|
|
64
|
+
throw new GradleException("key ${k} is missing android value")
|
|
65
|
+
}
|
|
66
|
+
result.put(k, v.get("android"))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
def rootConfig = buildRootConfig()
|
|
73
|
+
println "Root config ${rootConfig}"
|
|
74
|
+
|
|
75
|
+
def configPerFlavor(flavorName, rootConfig) {
|
|
76
|
+
def mapping = project.ext.has('flavorEnvMapping')
|
|
77
|
+
if (mapping && !flavorName.isEmpty()) {
|
|
78
|
+
println "mapping exists"
|
|
79
|
+
def f = rootConfig.get(flavorName)
|
|
80
|
+
return f
|
|
81
|
+
} else {
|
|
82
|
+
println "mapping does not exist or flavor is not defined"
|
|
83
|
+
return rootConfig
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def escape_double(str) {
|
|
88
|
+
return str.replace('"', '\\"')
|
|
89
|
+
}
|
|
90
|
+
def escape_single(str) {
|
|
91
|
+
return str.replace("'", "\\'")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
android {
|
|
95
|
+
// patch all variants with config values
|
|
96
|
+
applicationVariants.all { variant ->
|
|
97
|
+
println "processing variant ${variant.name}"
|
|
98
|
+
def flavorName = variant.flavorName
|
|
99
|
+
|
|
100
|
+
def cfg = flattenConfig(configPerFlavor(flavorName, rootConfig))
|
|
101
|
+
println "config per flavor ${flavorName} ${cfg}"
|
|
102
|
+
def keySet = cfg.keySet().collect()
|
|
103
|
+
println "all keys ${keySet}"
|
|
104
|
+
def rncuKeys = java.lang.String.join(",", keySet)
|
|
105
|
+
println "all keys as string ${rncuKeys}"
|
|
106
|
+
variant.buildConfigField "String", "__RNCU_KEYS", "\"${rncuKeys}\""
|
|
107
|
+
variant.mergedFlavor.manifestPlaceholders.putAll(cfg)
|
|
108
|
+
|
|
109
|
+
cfg.each { k,v ->
|
|
110
|
+
if (v instanceof java.lang.String) {
|
|
111
|
+
variant.buildConfigField "String", k, "\"${escape_double(v)}\""
|
|
112
|
+
variant.resValue "string", k, "${escape_single(v)}"
|
|
113
|
+
} else if (v instanceof Integer) {
|
|
114
|
+
variant.buildConfigField "int", k, "${v}"
|
|
115
|
+
variant.resValue "integer", k, "${v}"
|
|
116
|
+
} else if (v instanceof Double) {
|
|
117
|
+
variant.buildConfigField "double", k, "${v}"
|
|
118
|
+
variant.resValue "string", k, "${v}"
|
|
119
|
+
} else if (v instanceof Boolean) {
|
|
120
|
+
variant.buildConfigField "boolean", k, "${v}"
|
|
121
|
+
variant.resValue "bool", k, "${v}"
|
|
122
|
+
} else {
|
|
123
|
+
throw new GradleException("unknown type for key ${k} in flavor ${flavorName}: ${v.getClass()}")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
defaultConfig {
|
|
129
|
+
project.ext.set("config", rootConfig)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
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.bridge.ReactContextBaseJavaModule;
|
|
10
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
11
|
+
|
|
12
|
+
import java.util.HashMap;
|
|
13
|
+
import java.util.Map;
|
|
14
|
+
|
|
15
|
+
@ReactModule(name = UltimateConfigModule.NAME)
|
|
16
|
+
public class UltimateConfigModule extends ReactContextBaseJavaModule {
|
|
17
|
+
public static final String NAME = "UltimateConfig";
|
|
18
|
+
|
|
19
|
+
@Nullable
|
|
20
|
+
private static Class<?> _buildConfig;
|
|
21
|
+
|
|
22
|
+
public static void setBuildConfig(Class<?> buildConfig) {
|
|
23
|
+
_buildConfig = buildConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public UltimateConfigModule(ReactApplicationContext reactContext) {
|
|
27
|
+
super(reactContext);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Override
|
|
31
|
+
@NonNull
|
|
32
|
+
public String getName() {
|
|
33
|
+
return NAME;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Override
|
|
37
|
+
@NonNull
|
|
38
|
+
public Map<String, Object> getConstants() {
|
|
39
|
+
final Map<String, Object> constants = new HashMap<>();
|
|
40
|
+
try {
|
|
41
|
+
Class<?> act = _buildConfig;
|
|
42
|
+
if (act == null) return constants;
|
|
43
|
+
String keys = (String) act.getField("__RNUC_KEYS").get(act);
|
|
44
|
+
if (keys == null || keys.isEmpty()) return constants;
|
|
45
|
+
for (String k : keys.split(",")) {
|
|
46
|
+
Object value = act.getField(k).get(act);
|
|
47
|
+
if (value != null) {
|
|
48
|
+
constants.put(k, value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (Exception e) {
|
|
52
|
+
Log.w(NAME, "Failed to read config constants from BuildConfig: " + e.getMessage());
|
|
53
|
+
}
|
|
54
|
+
return constants;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package com.reactnativeultimateconfig;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
6
|
+
import com.facebook.react.TurboReactPackage;
|
|
7
|
+
import com.facebook.react.bridge.NativeModule;
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
9
|
+
import com.facebook.react.module.model.ReactModuleInfo;
|
|
10
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
11
|
+
|
|
12
|
+
import java.util.HashMap;
|
|
13
|
+
import java.util.Map;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* React Native package for UltimateConfig.
|
|
17
|
+
*
|
|
18
|
+
* Extends TurboReactPackage to support both the Old Architecture (Bridge)
|
|
19
|
+
* and the New Architecture (TurboModules / JSI). When IS_NEW_ARCHITECTURE_ENABLED
|
|
20
|
+
* is true the module is registered as a TurboModule; otherwise it runs as a
|
|
21
|
+
* classic bridge module.
|
|
22
|
+
*/
|
|
23
|
+
public class UltimateConfigPackage extends TurboReactPackage {
|
|
24
|
+
|
|
25
|
+
@Nullable
|
|
26
|
+
@Override
|
|
27
|
+
public NativeModule getModule(@NonNull String name, @NonNull ReactApplicationContext reactContext) {
|
|
28
|
+
if (UltimateConfigModule.NAME.equals(name)) {
|
|
29
|
+
return new UltimateConfigModule(reactContext);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Override
|
|
35
|
+
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
36
|
+
return () -> {
|
|
37
|
+
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
|
|
38
|
+
moduleInfos.put(
|
|
39
|
+
UltimateConfigModule.NAME,
|
|
40
|
+
new ReactModuleInfo(
|
|
41
|
+
UltimateConfigModule.NAME,
|
|
42
|
+
UltimateConfigModule.NAME,
|
|
43
|
+
false, // canOverrideExistingModule
|
|
44
|
+
false, // needsEagerInit
|
|
45
|
+
true, // hasConstants — constants are the primary API
|
|
46
|
+
false, // isCxxModule
|
|
47
|
+
BuildConfig.IS_NEW_ARCHITECTURE_ENABLED // isTurboModule
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
return moduleInfos;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
package/bin.js
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_native_1 = require("react-native");
|
|
4
|
+
// override.js is dynamically generated by the rncu CLI.
|
|
5
|
+
// It contains platform-specific overrides. Do not commit override.js to git.
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
7
|
+
const override = require('./override');
|
|
8
|
+
const { UltimateConfig } = react_native_1.NativeModules;
|
|
9
|
+
exports.default = Object.assign(Object.assign({}, UltimateConfig), override);
|
package/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
import type { ConfigValue } from './src/NativeUltimateConfig';
|
|
3
|
+
|
|
4
|
+
export type { ConfigValue, Spec } from './src/NativeUltimateConfig';
|
|
5
|
+
|
|
6
|
+
type Config = Record<string, ConfigValue>;
|
|
7
|
+
|
|
8
|
+
// override.js is dynamically generated by the rncu CLI.
|
|
9
|
+
// It contains platform-specific overrides. Do not commit override.js to git.
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
11
|
+
const override: Config = require('./override');
|
|
12
|
+
|
|
13
|
+
const { UltimateConfig } = NativeModules;
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
...(UltimateConfig as Config | undefined),
|
|
17
|
+
...override,
|
|
18
|
+
} as Config;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#error "invoke bin.js with env file before compiling native project"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#import "UltimateConfig.h"
|
|
2
|
+
#import "ConfigValues.h"
|
|
3
|
+
|
|
4
|
+
@implementation UltimateConfig
|
|
5
|
+
RCT_EXPORT_MODULE()
|
|
6
|
+
|
|
7
|
+
+ (BOOL)requiresMainQueueSetup
|
|
8
|
+
{
|
|
9
|
+
// getConstants() reads from a static in-memory struct — no UI or main thread access needed.
|
|
10
|
+
return NO;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
- (NSDictionary *)constantsToExport
|
|
14
|
+
{
|
|
15
|
+
return getValues();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Don't compile this code when we build for the old architecture.
|
|
19
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
20
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
21
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
22
|
+
{
|
|
23
|
+
return std::make_shared<facebook::react::NativeUltimateConfigSpecJSI>(params);
|
|
24
|
+
}
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
@end
|