react-native-config-ultimate 0.0.1 → 0.0.4

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 CHANGED
@@ -26,7 +26,7 @@ _Config that works_
26
26
 
27
27
  | react-native-config-ultimate | gradle |
28
28
  | ---------------------------- | ------ |
29
- | ^7 | 8 |
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
- | ^7 | >=0.60 <1.x | >=19 | ✅ TurboModules |
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. [Contributor notes](./docs/contributor-notes.md)
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: [java](./docs/api.md#java) and [objective-c](./docs/api.md#objective-c)
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 (83 tests)
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
- | javascript |
126
+ | javascript / typescript |
127
127
  | |
128
128
  |-------------------------------------------------------|
129
129
  | | |
130
- | objective-c | java |
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.
@@ -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 {
@@ -6,7 +6,7 @@ buildscript {
6
6
  mavenCentral()
7
7
  }
8
8
  dependencies {
9
- classpath group: 'org.yaml', name: 'snakeyaml', version: '1.19'
9
+ classpath group: 'org.yaml', name: 'snakeyaml', version: '1.33'
10
10
  }
11
11
  }
12
12
 
@@ -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
- true, // hasConstants constants are the primary API
46
- false, // isCxxModule
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
+ }
@@ -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
- @Override
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
- public Map<String, Object> getConstants() {
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
- String keys = (String) act.getField("__RNUC_KEYS").get(act);
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
- const { UltimateConfig } = react_native_1.NativeModules;
9
- exports.default = Object.assign(Object.assign({}, UltimateConfig), override);
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 { ConfigValue } from './src/NativeUltimateConfig';
1
+ import { TurboModuleRegistry, NativeModules } from 'react-native';
2
+ import type { Spec } from './src/NativeUltimateConfig';
3
3
 
4
- export type { ConfigValue, Spec } from './src/NativeUltimateConfig';
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
- const { UltimateConfig } = NativeModules;
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
- ...(UltimateConfig as Config | undefined),
35
+ ...nativeConstants,
17
36
  ...override,
18
37
  } as Config;
@@ -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.1",
4
+ "version": "0.0.4",
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}",
@@ -55,8 +60,13 @@
55
60
  "xcconfig",
56
61
  "gradle",
57
62
  "objective-c",
63
+ "swift",
58
64
  "java",
59
- "typescript"
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.60.0-rc.0 <1.0.x"
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
- # Don't install the dependencies when we run `pod install` in the old architecture.
28
- if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
29
- s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
30
- s.pod_target_xcconfig = {
31
- "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
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-config
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
- readonly getConstants: () => Record<string, ConfigValue>;
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', { paths: [project_root] });
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
- 'react-native-config-ultimate/package.json',
84
- { paths: [project_root] }
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,
package/src/load-env.ts CHANGED
@@ -16,9 +16,7 @@ function detect_format(config_path: string): FileFormat {
16
16
  function read_yaml(config_path: string): EnvData {
17
17
  const data = yaml.load(fs.readFileSync(config_path).toString());
18
18
  if (typeof data === 'undefined' || data === null || typeof data !== 'object') {
19
- throw new Error(
20
- `Expected to read object from ${config_path}, but got '${data}'`
21
- );
19
+ throw new Error(`Expected to read object from ${config_path}, but got '${data}'`);
22
20
  }
23
21
  return data as EnvData;
24
22
  }
@@ -47,9 +45,7 @@ export default function load_env(config_paths: string | string[]): EnvData {
47
45
  const paths = Array.isArray(config_paths) ? config_paths : [config_paths];
48
46
 
49
47
  if (paths.length === 0) {
50
- throw new Error(
51
- 'No env file specified. Usage: rncu <env-file> [env-file2 ...]'
52
- );
48
+ throw new Error('No env file specified. Usage: rncu <env-file> [env-file2 ...]');
53
49
  }
54
50
 
55
51
  const formats = paths.map(detect_format);
package/src/render-env.js CHANGED
@@ -54,7 +54,11 @@ function is_boolean(value) {
54
54
  }
55
55
  function escape(value) {
56
56
  if (is_string(value)) {
57
- return value.replace(/"/gm, '\\"');
57
+ return value
58
+ .replace(/\\/gm, '\\\\')
59
+ .replace(/"/gm, '\\"')
60
+ .replace(/\n/gm, '\\n')
61
+ .replace(/\r/gm, '\\r');
58
62
  }
59
63
  return value;
60
64
  }
@@ -98,8 +102,7 @@ function render_env(project_root, lib_root, env, rc) {
98
102
  // Only write xcconfig if the project has an ios folder.
99
103
  // All RN apps have it; some react-native-web apps may not.
100
104
  if (fs.existsSync(path.join(project_root, 'ios'))) {
101
- map[path.join(project_root, 'ios', `${config_file_name}.xcconfig`)] =
102
- render_template('rncu.xcconfig', ios);
105
+ map[path.join(project_root, 'ios', `${config_file_name}.xcconfig`)] = render_template('rncu.xcconfig', ios);
103
106
  }
104
107
  const js_override = rc && typeof rc.js_override === 'boolean' && rc.js_override;
105
108
  map[path.join(lib_root, 'override.js')] = render_template('override.js', {