react-native-config-ultimate 0.1.0 → 0.2.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/README.md +476 -121
- package/android/build.gradle +1 -1
- package/android/rncu.gradle +19 -15
- package/android/src/main/java/com/reactnativeultimateconfig/UltimateConfigHelper.java +87 -0
- package/android/src/main/java/com/reactnativeultimateconfig/UltimateConfigPackage.java +1 -1
- package/android/src/newarch/java/com/reactnativeultimateconfig/UltimateConfigModule.java +3 -50
- package/android/src/oldarch/java/com/reactnativeultimateconfig/UltimateConfigModule.java +3 -54
- package/ios/UltimateConfig.mm +2 -1
- package/lib/commonjs/cli.js +11 -2
- package/lib/commonjs/cli.js.map +1 -1
- package/lib/commonjs/flatten.js +6 -1
- package/lib/commonjs/flatten.js.map +1 -1
- package/lib/commonjs/load-env.js +14 -0
- package/lib/commonjs/load-env.js.map +1 -1
- package/lib/commonjs/main.js +5 -1
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/render-env.js +1 -1
- package/lib/commonjs/render-env.js.map +1 -1
- package/lib/commonjs/resolve-env.js +5 -1
- package/lib/commonjs/resolve-env.js.map +1 -1
- package/lib/commonjs/templates/override.js.handlebars +1 -10
- package/lib/commonjs/validate-env.js +20 -7
- package/lib/commonjs/validate-env.js.map +1 -1
- package/lib/module/cli.js +11 -2
- package/lib/module/cli.js.map +1 -1
- package/lib/module/flatten.js +6 -1
- package/lib/module/flatten.js.map +1 -1
- package/lib/module/load-env.js +14 -0
- package/lib/module/load-env.js.map +1 -1
- package/lib/module/main.js +6 -2
- package/lib/module/main.js.map +1 -1
- package/lib/module/render-env.js +1 -1
- package/lib/module/render-env.js.map +1 -1
- package/lib/module/resolve-env.js +5 -1
- package/lib/module/resolve-env.js.map +1 -1
- package/lib/module/templates/override.js.handlebars +1 -10
- package/lib/module/validate-env.js +19 -7
- package/lib/module/validate-env.js.map +1 -1
- package/lib/typescript/src/cli.d.ts.map +1 -1
- package/lib/typescript/src/flatten.d.ts.map +1 -1
- package/lib/typescript/src/load-env.d.ts.map +1 -1
- package/lib/typescript/src/main.d.ts +1 -1
- package/lib/typescript/src/main.d.ts.map +1 -1
- package/lib/typescript/src/render-env.d.ts.map +1 -1
- package/lib/typescript/src/resolve-env.d.ts +8 -1
- package/lib/typescript/src/resolve-env.d.ts.map +1 -1
- package/lib/typescript/src/validate-env.d.ts +8 -0
- package/lib/typescript/src/validate-env.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/cli.ts +12 -2
- package/src/flatten.ts +6 -1
- package/src/load-env.ts +18 -0
- package/src/main.ts +6 -2
- package/src/render-env.ts +4 -1
- package/src/resolve-env.ts +16 -2
- package/src/templates/override.js.handlebars +1 -10
- package/src/validate-env.ts +23 -7
- package/android/rnuc.yaml +0 -1
- package/lib/commonjs/bin.spec.js +0 -50
- package/lib/commonjs/bin.spec.js.map +0 -1
- package/lib/commonjs/cli.spec.js +0 -190
- package/lib/commonjs/cli.spec.js.map +0 -1
- package/lib/commonjs/flatten.spec.js +0 -32
- package/lib/commonjs/flatten.spec.js.map +0 -1
- package/lib/commonjs/load-env.spec.js +0 -257
- package/lib/commonjs/load-env.spec.js.map +0 -1
- package/lib/commonjs/main.spec.js +0 -228
- package/lib/commonjs/main.spec.js.map +0 -1
- package/lib/commonjs/render-env.spec.js +0 -397
- package/lib/commonjs/render-env.spec.js.map +0 -1
- package/lib/commonjs/resolve-env.spec.js +0 -31
- package/lib/commonjs/resolve-env.spec.js.map +0 -1
- package/lib/commonjs/validate-env.spec.js +0 -325
- package/lib/commonjs/validate-env.spec.js.map +0 -1
- package/lib/commonjs/write-env.spec.js +0 -115
- package/lib/commonjs/write-env.spec.js.map +0 -1
- package/lib/module/bin.spec.js +0 -49
- package/lib/module/bin.spec.js.map +0 -1
- package/lib/module/cli.spec.js +0 -190
- package/lib/module/cli.spec.js.map +0 -1
- package/lib/module/flatten.spec.js +0 -31
- package/lib/module/flatten.spec.js.map +0 -1
- package/lib/module/load-env.spec.js +0 -257
- package/lib/module/load-env.spec.js.map +0 -1
- package/lib/module/main.spec.js +0 -224
- package/lib/module/main.spec.js.map +0 -1
- package/lib/module/render-env.spec.js +0 -396
- package/lib/module/render-env.spec.js.map +0 -1
- package/lib/module/resolve-env.spec.js +0 -30
- package/lib/module/resolve-env.spec.js.map +0 -1
- package/lib/module/validate-env.spec.js +0 -325
- package/lib/module/validate-env.spec.js.map +0 -1
- package/lib/module/write-env.spec.js +0 -115
- package/lib/module/write-env.spec.js.map +0 -1
- package/lib/typescript/src/bin.spec.d.ts +0 -2
- package/lib/typescript/src/bin.spec.d.ts.map +0 -1
- package/lib/typescript/src/cli.spec.d.ts +0 -14
- package/lib/typescript/src/cli.spec.d.ts.map +0 -1
- package/lib/typescript/src/flatten.spec.d.ts +0 -2
- package/lib/typescript/src/flatten.spec.d.ts.map +0 -1
- package/lib/typescript/src/load-env.spec.d.ts +0 -6
- package/lib/typescript/src/load-env.spec.d.ts.map +0 -1
- package/lib/typescript/src/main.spec.d.ts +0 -2
- package/lib/typescript/src/main.spec.d.ts.map +0 -1
- package/lib/typescript/src/render-env.spec.d.ts +0 -2
- package/lib/typescript/src/render-env.spec.d.ts.map +0 -1
- package/lib/typescript/src/resolve-env.spec.d.ts +0 -2
- package/lib/typescript/src/resolve-env.spec.d.ts.map +0 -1
- package/lib/typescript/src/validate-env.spec.d.ts +0 -2
- package/lib/typescript/src/validate-env.spec.d.ts.map +0 -1
- package/lib/typescript/src/write-env.spec.d.ts +0 -9
- package/lib/typescript/src/write-env.spec.d.ts.map +0 -1
package/src/resolve-env.ts
CHANGED
|
@@ -26,7 +26,14 @@ export interface SchemaField {
|
|
|
26
26
|
export type Schema = Record<string, SchemaField>;
|
|
27
27
|
|
|
28
28
|
export interface RC {
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Hook to transform env data before validation and rendering.
|
|
31
|
+
* Return the modified env object, or `undefined`/`void` to keep the original.
|
|
32
|
+
*
|
|
33
|
+
* **Important:** if you mutate `env` in place without returning it, the
|
|
34
|
+
* mutations are ignored. Always return the modified object.
|
|
35
|
+
*/
|
|
36
|
+
on_env?: (env: EnvData) => EnvData | undefined | void | Promise<EnvData | undefined | void>;
|
|
30
37
|
js_override?: boolean;
|
|
31
38
|
/**
|
|
32
39
|
* Optional schema for build-time validation of env vars.
|
|
@@ -38,7 +45,14 @@ export interface RC {
|
|
|
38
45
|
export default async function resolve_env(env: EnvData, rc?: RC): Promise<EnvData> {
|
|
39
46
|
if (rc && rc.on_env) {
|
|
40
47
|
const patched_env = await rc.on_env(env);
|
|
41
|
-
|
|
48
|
+
if (typeof patched_env === 'undefined') return env;
|
|
49
|
+
if (!patched_env || typeof patched_env !== 'object' || Array.isArray(patched_env)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`[rncu] on_env hook must return an object (or undefined to keep the original), ` +
|
|
52
|
+
`but got ${typeof patched_env}: ${String(patched_env)}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return patched_env as EnvData;
|
|
42
56
|
} else {
|
|
43
57
|
return env;
|
|
44
58
|
}
|
|
@@ -4,13 +4,4 @@ const IOS_DATA = {{{toJSON @root.ios}}}
|
|
|
4
4
|
|
|
5
5
|
const ANDROID_DATA = {{{toJSON @root.android}}}
|
|
6
6
|
|
|
7
|
-
module.exports = {
|
|
8
|
-
{{#each @root.ios}}
|
|
9
|
-
get {{{@key}}}() {
|
|
10
|
-
return RN.Platform.select({
|
|
11
|
-
ios: IOS_DATA["{{{@key}}}"],
|
|
12
|
-
android: ANDROID_DATA["{{{@key}}}"],
|
|
13
|
-
});
|
|
14
|
-
},
|
|
15
|
-
{{/each}}
|
|
16
|
-
}
|
|
7
|
+
module.exports = RN.Platform.select({ ios: IOS_DATA, android: ANDROID_DATA }) || {};
|
package/src/validate-env.ts
CHANGED
|
@@ -3,16 +3,14 @@ import type { EnvData, Schema } from './resolve-env';
|
|
|
3
3
|
const VALID_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Validate env
|
|
7
|
-
*
|
|
6
|
+
* Validate that all env key names are valid C/Java/JS identifiers.
|
|
7
|
+
* This runs UNCONDITIONALLY (even without a schema) to prevent template
|
|
8
|
+
* injection in generated native files (ConfigValues.h, xcconfig, override.js).
|
|
8
9
|
*
|
|
9
|
-
* Throws with a human-readable error listing ALL
|
|
10
|
-
* (not just the first one), so users can fix everything in one pass.
|
|
10
|
+
* Throws with a human-readable error listing ALL invalid keys at once.
|
|
11
11
|
*/
|
|
12
|
-
export function
|
|
12
|
+
export function validate_keys(env: EnvData): void {
|
|
13
13
|
const errors: string[] = [];
|
|
14
|
-
|
|
15
|
-
// Validate all env key names are valid identifiers
|
|
16
14
|
for (const key of Object.keys(env)) {
|
|
17
15
|
if (!VALID_KEY_PATTERN.test(key)) {
|
|
18
16
|
errors.push(
|
|
@@ -20,6 +18,24 @@ export function validate_env(env: EnvData, schema: Schema): void {
|
|
|
20
18
|
);
|
|
21
19
|
}
|
|
22
20
|
}
|
|
21
|
+
if (errors.length > 0) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`\n\n❌ react-native-config-ultimate: invalid key names:\n` +
|
|
24
|
+
errors.map((e) => ` • ${e}`).join('\n') +
|
|
25
|
+
'\n'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate env data against a schema defined in `.rncurc.js`.
|
|
32
|
+
* Called after `on_env` so the hook can add/transform vars before validation.
|
|
33
|
+
*
|
|
34
|
+
* Throws with a human-readable error listing ALL failures at once
|
|
35
|
+
* (not just the first one), so users can fix everything in one pass.
|
|
36
|
+
*/
|
|
37
|
+
export function validate_env(env: EnvData, schema: Schema): void {
|
|
38
|
+
const errors: string[] = [];
|
|
23
39
|
|
|
24
40
|
// Pre-compile all regex patterns once, before iterating over env values.
|
|
25
41
|
// This avoids re-compiling the same pattern for every validated key.
|
package/android/rnuc.yaml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
HELLO: "world"
|
package/lib/commonjs/bin.spec.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _child_process = _interopRequireDefault(require("child_process"));
|
|
4
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
5
|
-
var _path = _interopRequireDefault(require("path"));
|
|
6
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
|
-
const {
|
|
9
|
-
files_to_assert
|
|
10
|
-
} = require('./main.spec');
|
|
11
|
-
|
|
12
|
-
// TODO: Re-enable after fixing integration test paths for bob build output
|
|
13
|
-
describe.skip.each`
|
|
14
|
-
extension | env_test_content
|
|
15
|
-
${''} | ${'hello=world'}
|
|
16
|
-
${'.yaml'} | ${'hello: world'}
|
|
17
|
-
${'.yml'} | ${'hello: world'}
|
|
18
|
-
`('test codegen', ({
|
|
19
|
-
extension,
|
|
20
|
-
env_test_content
|
|
21
|
-
}) => {
|
|
22
|
-
let project_root;
|
|
23
|
-
beforeAll(() => {
|
|
24
|
-
project_root = _path.default.join(process.cwd(), _fs.default.mkdtempSync('rncu-jest'));
|
|
25
|
-
for (const file_path of files_to_assert) {
|
|
26
|
-
const {
|
|
27
|
-
dir
|
|
28
|
-
} = _path.default.parse(file_path);
|
|
29
|
-
const folder = _path.default.join(project_root, dir);
|
|
30
|
-
_fs.default.mkdirSync(folder, {
|
|
31
|
-
recursive: true
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
afterAll(() => {
|
|
36
|
-
_fs.default.rmSync(project_root, {
|
|
37
|
-
recursive: true,
|
|
38
|
-
force: true
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
it.each(files_to_assert.map(k => [k]))('creates file at path %s', file_path => {
|
|
42
|
-
const env_file_path = _path.default.join(project_root, `.env${extension}`);
|
|
43
|
-
_fs.default.writeFileSync(env_file_path, env_test_content);
|
|
44
|
-
_child_process.default.execFileSync('node', [_path.default.join(process.cwd(), 'lib/commonjs/cli.js'), env_file_path], {
|
|
45
|
-
cwd: project_root
|
|
46
|
-
});
|
|
47
|
-
expect(_fs.default.existsSync(_path.default.join(project_root, file_path))).toEqual(true);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
//# sourceMappingURL=bin.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_child_process","_interopRequireDefault","require","_fs","_path","e","__esModule","default","files_to_assert","describe","skip","each","extension","env_test_content","project_root","beforeAll","path","join","process","cwd","fs","mkdtempSync","file_path","dir","parse","folder","mkdirSync","recursive","afterAll","rmSync","force","it","map","k","env_file_path","writeFileSync","cp","execFileSync","expect","existsSync","toEqual"],"sourceRoot":"../../src","sources":["bin.spec.ts"],"mappings":";;AAAA,IAAAA,cAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,GAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,KAAA,GAAAH,sBAAA,CAAAC,OAAA;AAAwB,SAAAD,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AACxB;AACA,MAAM;EAAEG;AAAgB,CAAC,GAAGN,OAAO,CAAC,aAAa,CAAkC;;AAEnF;AACAO,QAAQ,CAACC,IAAI,CAACC,IAAI;AAClB;AACA,IAAI,EAAE,WAAW,aAAa;AAC9B,IAAI,OAAO,MAAM,cAAc;AAC/B,IAAI,MAAM,OAAO,cAAc;AAC/B,CAAC,CACC,cAAc,EACd,CAAC;EAAEC,SAAS;EAAEC;AAAkE,CAAC,KAAK;EACpF,IAAIC,YAAoB;EACxBC,SAAS,CAAC,MAAM;IACdD,YAAY,GAAGE,aAAI,CAACC,IAAI,CAACC,OAAO,CAACC,GAAG,CAAC,CAAC,EAAEC,WAAE,CAACC,WAAW,CAAC,WAAW,CAAC,CAAC;IACpE,KAAK,MAAMC,SAAS,IAAId,eAAe,EAAE;MACvC,MAAM;QAAEe;MAAI,CAAC,GAAGP,aAAI,CAACQ,KAAK,CAACF,SAAS,CAAC;MACrC,MAAMG,MAAM,GAAGT,aAAI,CAACC,IAAI,CAACH,YAAY,EAAES,GAAG,CAAC;MAC3CH,WAAE,CAACM,SAAS,CAACD,MAAM,EAAE;QAAEE,SAAS,EAAE;MAAK,CAAC,CAAC;IAC3C;EACF,CAAC,CAAC;EACFC,QAAQ,CAAC,MAAM;IACbR,WAAE,CAACS,MAAM,CAACf,YAAY,EAAE;MAAEa,SAAS,EAAE,IAAI;MAAEG,KAAK,EAAE;IAAK,CAAC,CAAC;EAC3D,CAAC,CAAC;EACFC,EAAE,CAACpB,IAAI,CAACH,eAAe,CAACwB,GAAG,CAAEC,CAAC,IAAK,CAACA,CAAC,CAAC,CAAC,CAAC,CAAC,yBAAyB,EAAGX,SAAS,IAAK;IACjF,MAAMY,aAAa,GAAGlB,aAAI,CAACC,IAAI,CAACH,YAAY,EAAE,OAAOF,SAAS,EAAE,CAAC;IACjEQ,WAAE,CAACe,aAAa,CAACD,aAAa,EAAErB,gBAAgB,CAAC;IACjDuB,sBAAE,CAACC,YAAY,CAAC,MAAM,EAAE,CAACrB,aAAI,CAACC,IAAI,CAACC,OAAO,CAACC,GAAG,CAAC,CAAC,EAAE,qBAAqB,CAAC,EAAEe,aAAa,CAAC,EAAE;MACxFf,GAAG,EAAEL;IACP,CAAC,CAAC;IACFwB,MAAM,CAAClB,WAAE,CAACmB,UAAU,CAACvB,aAAI,CAACC,IAAI,CAACH,YAAY,EAAEQ,SAAmB,CAAC,CAAC,CAAC,CAACkB,OAAO,CAAC,IAAI,CAAC;EACnF,CAAC,CAAC;AACJ,CACF,CAAC","ignoreList":[]}
|
package/lib/commonjs/cli.spec.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// All mocks must be declared before the module is required.
|
|
4
|
-
const mock_main = jest.fn();
|
|
5
|
-
jest.doMock('./main', () => ({
|
|
6
|
-
__esModule: true,
|
|
7
|
-
default: mock_main
|
|
8
|
-
}));
|
|
9
|
-
const mock_exists_sync = jest.fn();
|
|
10
|
-
jest.doMock('fs', () => ({
|
|
11
|
-
existsSync: mock_exists_sync
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
// Mock watcher returned by chokidar.watch()
|
|
15
|
-
const mock_watcher_on = jest.fn();
|
|
16
|
-
const mock_watcher_close = jest.fn().mockResolvedValue(undefined);
|
|
17
|
-
const mock_watcher = {
|
|
18
|
-
on: mock_watcher_on.mockReturnThis(),
|
|
19
|
-
close: mock_watcher_close
|
|
20
|
-
};
|
|
21
|
-
const mock_chokidar_watch = jest.fn().mockReturnValue(mock_watcher);
|
|
22
|
-
jest.doMock('chokidar', () => ({
|
|
23
|
-
watch: mock_chokidar_watch
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
|
-
const cli = require('./cli').default;
|
|
28
|
-
|
|
29
|
-
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
function set_argv(...args) {
|
|
32
|
-
process.argv = ['node', 'rncu', ...args];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Grab the handler registered for a given chokidar event. */
|
|
36
|
-
function get_watcher_handler(event) {
|
|
37
|
-
const call = mock_watcher_on.mock.calls.find(([ev]) => ev === event);
|
|
38
|
-
return call?.[1];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ─── test suite ──────────────────────────────────────────────────────────────
|
|
42
|
-
|
|
43
|
-
describe('cli', () => {
|
|
44
|
-
const original_argv = process.argv;
|
|
45
|
-
const stdin_resume_spy = jest.spyOn(process.stdin, 'resume').mockImplementation(() => process.stdin);
|
|
46
|
-
const process_on_spy = jest.spyOn(process, 'on').mockImplementation(() => process);
|
|
47
|
-
beforeEach(() => {
|
|
48
|
-
mock_main.mockReset().mockResolvedValue(undefined);
|
|
49
|
-
// Default: env files exist, RC file does NOT exist.
|
|
50
|
-
// This mimics the real-world baseline: user passes a valid .env file,
|
|
51
|
-
// but hasn't created a .rncurc.js config yet.
|
|
52
|
-
mock_exists_sync.mockReset().mockImplementation(p => !p.endsWith('.rncurc.js'));
|
|
53
|
-
mock_chokidar_watch.mockReset().mockReturnValue(mock_watcher);
|
|
54
|
-
mock_watcher_on.mockReset().mockReturnThis();
|
|
55
|
-
mock_watcher_close.mockReset().mockResolvedValue(undefined);
|
|
56
|
-
stdin_resume_spy.mockClear();
|
|
57
|
-
process_on_spy.mockClear();
|
|
58
|
-
});
|
|
59
|
-
afterAll(() => {
|
|
60
|
-
process.argv = original_argv;
|
|
61
|
-
stdin_resume_spy.mockRestore();
|
|
62
|
-
process_on_spy.mockRestore();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// ── normal (non-watch) mode ─────────────────────────────────────────────
|
|
66
|
-
|
|
67
|
-
describe('normal mode', () => {
|
|
68
|
-
it('runs main once with the env file and exits', async () => {
|
|
69
|
-
set_argv('.env');
|
|
70
|
-
await cli();
|
|
71
|
-
expect(mock_main).toHaveBeenCalledTimes(1);
|
|
72
|
-
expect(mock_main).toHaveBeenCalledWith(expect.any(String), expect.any(String), ['.env'], undefined);
|
|
73
|
-
expect(mock_chokidar_watch).not.toHaveBeenCalled();
|
|
74
|
-
});
|
|
75
|
-
it('passes multiple env files to main', async () => {
|
|
76
|
-
set_argv('.env.base', '.env.staging');
|
|
77
|
-
await cli();
|
|
78
|
-
expect(mock_main).toHaveBeenCalledWith(expect.any(String), expect.any(String), ['.env.base', '.env.staging'], undefined);
|
|
79
|
-
});
|
|
80
|
-
it('loads and passes RC file when it exists', async () => {
|
|
81
|
-
set_argv('.env');
|
|
82
|
-
// All files exist (env file + RC file). The RC file can't actually be
|
|
83
|
-
// resolved via require() in test env, so cli() is expected to throw.
|
|
84
|
-
mock_exists_sync.mockReturnValue(true);
|
|
85
|
-
await expect(cli()).rejects.toThrow();
|
|
86
|
-
});
|
|
87
|
-
it('propagates errors from main in non-watch mode', async () => {
|
|
88
|
-
set_argv('.env');
|
|
89
|
-
mock_main.mockRejectedValueOnce(new Error('missing var'));
|
|
90
|
-
await expect(cli()).rejects.toThrow('missing var');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// ── watch mode ──────────────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
describe('--watch mode', () => {
|
|
97
|
-
it('starts chokidar watcher on the env files', async () => {
|
|
98
|
-
set_argv('.env', '--watch');
|
|
99
|
-
await cli();
|
|
100
|
-
expect(mock_chokidar_watch).toHaveBeenCalledWith(['.env'], expect.objectContaining({
|
|
101
|
-
ignoreInitial: true,
|
|
102
|
-
persistent: true
|
|
103
|
-
}));
|
|
104
|
-
});
|
|
105
|
-
it('also watches .rncurc.js when it exists', async () => {
|
|
106
|
-
set_argv('.env', '--watch');
|
|
107
|
-
// All files exist (env + RC). We suppress the require() error below
|
|
108
|
-
// so the test can verify that .rncurc.js is added to the watcher list.
|
|
109
|
-
mock_exists_sync.mockReturnValue(true);
|
|
110
|
-
// Suppress require() failure for missing rc file in initial run
|
|
111
|
-
mock_main.mockResolvedValue(undefined);
|
|
112
|
-
try {
|
|
113
|
-
await cli();
|
|
114
|
-
} catch {
|
|
115
|
-
// ignore require error for non-existent RC in test env
|
|
116
|
-
}
|
|
117
|
-
const watched_files = mock_chokidar_watch.mock.calls[0]?.[0];
|
|
118
|
-
expect(watched_files.some(f => f.endsWith('.rncurc.js'))).toBe(true);
|
|
119
|
-
});
|
|
120
|
-
it('runs main once immediately on start', async () => {
|
|
121
|
-
set_argv('.env', '--watch');
|
|
122
|
-
await cli();
|
|
123
|
-
expect(mock_main).toHaveBeenCalledTimes(1);
|
|
124
|
-
});
|
|
125
|
-
it('registers change and add event handlers', async () => {
|
|
126
|
-
set_argv('.env', '--watch');
|
|
127
|
-
await cli();
|
|
128
|
-
const events = mock_watcher_on.mock.calls.map(([ev]) => ev);
|
|
129
|
-
expect(events).toContain('change');
|
|
130
|
-
expect(events).toContain('add');
|
|
131
|
-
});
|
|
132
|
-
it('re-runs main when a file changes', async () => {
|
|
133
|
-
set_argv('.env', '--watch');
|
|
134
|
-
await cli();
|
|
135
|
-
expect(mock_main).toHaveBeenCalledTimes(1);
|
|
136
|
-
const on_change = get_watcher_handler('change');
|
|
137
|
-
expect(on_change).toBeDefined();
|
|
138
|
-
await on_change?.('.env');
|
|
139
|
-
expect(mock_main).toHaveBeenCalledTimes(2);
|
|
140
|
-
});
|
|
141
|
-
it('re-runs main when a file is added', async () => {
|
|
142
|
-
set_argv('.env', '--watch');
|
|
143
|
-
await cli();
|
|
144
|
-
const on_add = get_watcher_handler('add');
|
|
145
|
-
await on_add?.('.env.local');
|
|
146
|
-
expect(mock_main).toHaveBeenCalledTimes(2);
|
|
147
|
-
});
|
|
148
|
-
it('catches errors on re-run and keeps watching (does not throw)', async () => {
|
|
149
|
-
set_argv('.env', '--watch');
|
|
150
|
-
await cli();
|
|
151
|
-
mock_main.mockRejectedValueOnce(new Error('validation failed'));
|
|
152
|
-
const on_change = get_watcher_handler('change');
|
|
153
|
-
expect(on_change).toBeDefined();
|
|
154
|
-
|
|
155
|
-
// The handler uses `void run(p)` so it returns undefined synchronously
|
|
156
|
-
// and swallows errors inside run()'s try/catch. We trigger it and then
|
|
157
|
-
// drain the microtask queue to let the async error handling complete.
|
|
158
|
-
expect(() => on_change('.env')).not.toThrow();
|
|
159
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
160
|
-
|
|
161
|
-
// main was called twice: initial run + change handler
|
|
162
|
-
expect(mock_main).toHaveBeenCalledTimes(2);
|
|
163
|
-
});
|
|
164
|
-
it('catches initial run errors and still starts the watcher', async () => {
|
|
165
|
-
set_argv('.env', '--watch');
|
|
166
|
-
mock_main.mockRejectedValueOnce(new Error('initial run failed'));
|
|
167
|
-
|
|
168
|
-
// Should not throw even though initial run fails
|
|
169
|
-
await expect(cli()).resolves.toBeUndefined();
|
|
170
|
-
expect(mock_chokidar_watch).toHaveBeenCalled();
|
|
171
|
-
});
|
|
172
|
-
it('keeps process alive via process.stdin.resume()', async () => {
|
|
173
|
-
set_argv('.env', '--watch');
|
|
174
|
-
await cli();
|
|
175
|
-
expect(stdin_resume_spy).toHaveBeenCalled();
|
|
176
|
-
});
|
|
177
|
-
it('registers a SIGINT handler for graceful shutdown', async () => {
|
|
178
|
-
set_argv('.env', '--watch');
|
|
179
|
-
await cli();
|
|
180
|
-
const sigint_call = process_on_spy.mock.calls.find(([event]) => event === 'SIGINT');
|
|
181
|
-
expect(sigint_call).toBeDefined();
|
|
182
|
-
});
|
|
183
|
-
it('uses -w as alias for --watch', async () => {
|
|
184
|
-
set_argv('.env', '-w');
|
|
185
|
-
await cli();
|
|
186
|
-
expect(mock_chokidar_watch).toHaveBeenCalled();
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
//# sourceMappingURL=cli.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["mock_main","jest","fn","doMock","__esModule","default","mock_exists_sync","existsSync","mock_watcher_on","mock_watcher_close","mockResolvedValue","undefined","mock_watcher","on","mockReturnThis","close","mock_chokidar_watch","mockReturnValue","watch","cli","require","set_argv","args","process","argv","get_watcher_handler","event","call","mock","calls","find","ev","describe","original_argv","stdin_resume_spy","spyOn","stdin","mockImplementation","process_on_spy","beforeEach","mockReset","p","endsWith","mockClear","afterAll","mockRestore","it","expect","toHaveBeenCalledTimes","toHaveBeenCalledWith","any","String","not","toHaveBeenCalled","rejects","toThrow","mockRejectedValueOnce","Error","objectContaining","ignoreInitial","persistent","watched_files","some","f","toBe","events","map","toContain","on_change","toBeDefined","on_add","Promise","resolve","setImmediate","resolves","toBeUndefined","sigint_call"],"sourceRoot":"../../src","sources":["cli.spec.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _flatten = _interopRequireDefault(require("./flatten"));
|
|
4
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
5
|
-
describe('flatten', () => {
|
|
6
|
-
it('flattens config per platform', () => {
|
|
7
|
-
const config = {
|
|
8
|
-
value1: 'abc',
|
|
9
|
-
value2: {
|
|
10
|
-
ios: 'def',
|
|
11
|
-
android: 'xyz',
|
|
12
|
-
web: '123'
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
const flat_ios = (0, _flatten.default)(config, 'ios');
|
|
16
|
-
expect(flat_ios).toEqual({
|
|
17
|
-
value1: 'abc',
|
|
18
|
-
value2: 'def'
|
|
19
|
-
});
|
|
20
|
-
const flat_android = (0, _flatten.default)(config, 'android');
|
|
21
|
-
expect(flat_android).toEqual({
|
|
22
|
-
value1: 'abc',
|
|
23
|
-
value2: 'xyz'
|
|
24
|
-
});
|
|
25
|
-
const flat_web = (0, _flatten.default)(config, 'web');
|
|
26
|
-
expect(flat_web).toEqual({
|
|
27
|
-
value1: 'abc',
|
|
28
|
-
value2: '123'
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
//# sourceMappingURL=flatten.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_flatten","_interopRequireDefault","require","e","__esModule","default","describe","it","config","value1","value2","ios","android","web","flat_ios","flatten","expect","toEqual","flat_android","flat_web"],"sourceRoot":"../../src","sources":["flatten.spec.ts"],"mappings":";;AAAA,IAAAA,QAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAgC,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAEhCG,QAAQ,CAAC,SAAS,EAAE,MAAM;EACxBC,EAAE,CAAC,8BAA8B,EAAE,MAAM;IACvC,MAAMC,MAAM,GAAG;MACbC,MAAM,EAAE,KAAK;MACbC,MAAM,EAAE;QAAEC,GAAG,EAAE,KAAK;QAAEC,OAAO,EAAE,KAAK;QAAEC,GAAG,EAAE;MAAM;IACnD,CAAC;IACD,MAAMC,QAAQ,GAAG,IAAAC,gBAAO,EAACP,MAAM,EAAE,KAAK,CAAC;IACvCQ,MAAM,CAACF,QAAQ,CAAC,CAACG,OAAO,CAAC;MAAER,MAAM,EAAE,KAAK;MAAEC,MAAM,EAAE;IAAM,CAAC,CAAC;IAC1D,MAAMQ,YAAY,GAAG,IAAAH,gBAAO,EAACP,MAAM,EAAE,SAAS,CAAC;IAC/CQ,MAAM,CAACE,YAAY,CAAC,CAACD,OAAO,CAAC;MAAER,MAAM,EAAE,KAAK;MAAEC,MAAM,EAAE;IAAM,CAAC,CAAC;IAC9D,MAAMS,QAAQ,GAAG,IAAAJ,gBAAO,EAACP,MAAM,EAAE,KAAK,CAAC;IACvCQ,MAAM,CAACG,QAAQ,CAAC,CAACF,OAAO,CAAC;MAAER,MAAM,EAAE,KAAK;MAAEC,MAAM,EAAE;IAAM,CAAC,CAAC;EAC5D,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const mockReadFileSync = jest.fn();
|
|
4
|
-
jest.doMock('fs', () => ({
|
|
5
|
-
readFileSync: mockReadFileSync
|
|
6
|
-
}));
|
|
7
|
-
const mockParse = jest.fn();
|
|
8
|
-
jest.doMock('dotenv', () => ({
|
|
9
|
-
parse: mockParse
|
|
10
|
-
}));
|
|
11
|
-
const mockExpand = jest.fn();
|
|
12
|
-
jest.doMock('dotenv-expand', () => ({
|
|
13
|
-
expand: mockExpand
|
|
14
|
-
}));
|
|
15
|
-
const mockYaml = jest.fn();
|
|
16
|
-
jest.doMock('js-yaml', () => ({
|
|
17
|
-
load: mockYaml
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
|
-
const load_env = require('./load-env').default;
|
|
22
|
-
describe('load-env', () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
mockReadFileSync.mockReset();
|
|
25
|
-
mockParse.mockReset();
|
|
26
|
-
mockExpand.mockReset();
|
|
27
|
-
mockYaml.mockReset();
|
|
28
|
-
// Default expand: return parsed as-is (no expansion side effects)
|
|
29
|
-
mockExpand.mockImplementation(input => input);
|
|
30
|
-
});
|
|
31
|
-
describe('dotenv format', () => {
|
|
32
|
-
it('reads a single dotenv file (backward-compatible string arg)', () => {
|
|
33
|
-
mockReadFileSync.mockReturnValueOnce('hello=world');
|
|
34
|
-
mockParse.mockReturnValueOnce({
|
|
35
|
-
hello: 'world'
|
|
36
|
-
});
|
|
37
|
-
const result = load_env('hello');
|
|
38
|
-
expect(mockReadFileSync).toHaveBeenCalledWith('hello', 'utf8');
|
|
39
|
-
expect(mockParse).toHaveBeenCalledWith('hello=world');
|
|
40
|
-
expect(result).toEqual({
|
|
41
|
-
hello: 'world'
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
it('reads a single dotenv file when passed as an array', () => {
|
|
45
|
-
mockReadFileSync.mockReturnValueOnce('hello=world');
|
|
46
|
-
mockParse.mockReturnValueOnce({
|
|
47
|
-
hello: 'world'
|
|
48
|
-
});
|
|
49
|
-
const result = load_env(['hello']);
|
|
50
|
-
expect(mockReadFileSync).toHaveBeenCalledWith('hello', 'utf8');
|
|
51
|
-
expect(result).toEqual({
|
|
52
|
-
hello: 'world'
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
it('merges multiple dotenv files, last file wins for conflicts', () => {
|
|
56
|
-
mockReadFileSync.mockReturnValueOnce('A=base\nB=base').mockReturnValueOnce('B=override\nC=new');
|
|
57
|
-
mockParse.mockReturnValueOnce({
|
|
58
|
-
A: 'base',
|
|
59
|
-
B: 'base'
|
|
60
|
-
}).mockReturnValueOnce({
|
|
61
|
-
B: 'override',
|
|
62
|
-
C: 'new'
|
|
63
|
-
});
|
|
64
|
-
const result = load_env(['.env.base', '.env.staging']);
|
|
65
|
-
expect(mockReadFileSync).toHaveBeenCalledTimes(2);
|
|
66
|
-
// expand is called once with the merged raw object
|
|
67
|
-
expect(mockExpand).toHaveBeenCalledWith({
|
|
68
|
-
parsed: {
|
|
69
|
-
A: 'base',
|
|
70
|
-
B: 'override',
|
|
71
|
-
C: 'new'
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
expect(result).toEqual({
|
|
75
|
-
A: 'base',
|
|
76
|
-
B: 'override',
|
|
77
|
-
C: 'new'
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
it('expands $VAR references using dotenv-expand', () => {
|
|
81
|
-
mockReadFileSync.mockReturnValueOnce('BASE=https://api.com\nURL=$BASE/v1');
|
|
82
|
-
mockParse.mockReturnValueOnce({
|
|
83
|
-
BASE: 'https://api.com',
|
|
84
|
-
URL: '$BASE/v1'
|
|
85
|
-
});
|
|
86
|
-
mockExpand.mockReturnValueOnce({
|
|
87
|
-
parsed: {
|
|
88
|
-
BASE: 'https://api.com',
|
|
89
|
-
URL: 'https://api.com/v1'
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
const result = load_env('.env');
|
|
93
|
-
expect(result).toEqual({
|
|
94
|
-
BASE: 'https://api.com',
|
|
95
|
-
URL: 'https://api.com/v1'
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
it('expands cross-file $VAR references when merging multiple files', () => {
|
|
99
|
-
mockReadFileSync.mockReturnValueOnce('BASE_URL=https://api.com').mockReturnValueOnce('API_URL=$BASE_URL/v1');
|
|
100
|
-
mockParse.mockReturnValueOnce({
|
|
101
|
-
BASE_URL: 'https://api.com'
|
|
102
|
-
}).mockReturnValueOnce({
|
|
103
|
-
API_URL: '$BASE_URL/v1'
|
|
104
|
-
});
|
|
105
|
-
// Expand is called with merged raw — so cross-file reference resolves
|
|
106
|
-
mockExpand.mockReturnValueOnce({
|
|
107
|
-
parsed: {
|
|
108
|
-
BASE_URL: 'https://api.com',
|
|
109
|
-
API_URL: 'https://api.com/v1'
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
const result = load_env(['.env.base', '.env.staging']);
|
|
113
|
-
expect(mockExpand).toHaveBeenCalledWith({
|
|
114
|
-
parsed: {
|
|
115
|
-
BASE_URL: 'https://api.com',
|
|
116
|
-
API_URL: '$BASE_URL/v1'
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
expect(result).toEqual({
|
|
120
|
-
BASE_URL: 'https://api.com',
|
|
121
|
-
API_URL: 'https://api.com/v1'
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
describe('yaml format', () => {
|
|
126
|
-
it.each`
|
|
127
|
-
extension
|
|
128
|
-
${'yml'}
|
|
129
|
-
${'yaml'}
|
|
130
|
-
`("reads yaml when extension is '.$extension'", ({
|
|
131
|
-
extension
|
|
132
|
-
}) => {
|
|
133
|
-
mockReadFileSync.mockReturnValueOnce(Buffer.from('data'));
|
|
134
|
-
mockYaml.mockReturnValueOnce({
|
|
135
|
-
hello: 'world'
|
|
136
|
-
});
|
|
137
|
-
const result = load_env(`hello.${extension}`);
|
|
138
|
-
expect(mockReadFileSync).toHaveBeenCalledWith(`hello.${extension}`);
|
|
139
|
-
expect(mockYaml).toHaveBeenCalledWith('data');
|
|
140
|
-
expect(result).toEqual({
|
|
141
|
-
hello: 'world'
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
it('merges multiple yaml files, last file wins for conflicts', () => {
|
|
145
|
-
mockReadFileSync.mockReturnValueOnce(Buffer.from('A: base\nB: base')).mockReturnValueOnce(Buffer.from('B: override\nC: new'));
|
|
146
|
-
mockYaml.mockReturnValueOnce({
|
|
147
|
-
A: 'base',
|
|
148
|
-
B: 'base'
|
|
149
|
-
}).mockReturnValueOnce({
|
|
150
|
-
B: 'override',
|
|
151
|
-
C: 'new'
|
|
152
|
-
});
|
|
153
|
-
const result = load_env(['base.yaml', 'staging.yaml']);
|
|
154
|
-
expect(mockReadFileSync).toHaveBeenCalledTimes(2);
|
|
155
|
-
expect(result).toEqual({
|
|
156
|
-
A: 'base',
|
|
157
|
-
B: 'override',
|
|
158
|
-
C: 'new'
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
describe.each`
|
|
162
|
-
extension
|
|
163
|
-
${'yml'}
|
|
164
|
-
${'yaml'}
|
|
165
|
-
`("throws when yaml is not an object with extension '.$extension'", ({
|
|
166
|
-
extension
|
|
167
|
-
}) => {
|
|
168
|
-
it.each`
|
|
169
|
-
content
|
|
170
|
-
${'abc:def'}
|
|
171
|
-
${false}
|
|
172
|
-
${true}
|
|
173
|
-
${42}
|
|
174
|
-
${null}
|
|
175
|
-
${undefined}
|
|
176
|
-
`("when content is '$content'", ({
|
|
177
|
-
content
|
|
178
|
-
}) => {
|
|
179
|
-
mockReadFileSync.mockReturnValueOnce(Buffer.from('data'));
|
|
180
|
-
mockYaml.mockReturnValueOnce(content);
|
|
181
|
-
expect(() => {
|
|
182
|
-
load_env(`hello.${extension}`);
|
|
183
|
-
}).toThrow();
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
describe('edge cases', () => {
|
|
188
|
-
it('throws when no files are provided', () => {
|
|
189
|
-
expect(() => load_env([])).toThrow('No env file specified');
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
describe('mixed format (yaml + dotenv)', () => {
|
|
193
|
-
it('merges yaml and dotenv files together', () => {
|
|
194
|
-
// First file is yaml
|
|
195
|
-
mockReadFileSync.mockReturnValueOnce(Buffer.from('YAML_VAR: from_yaml')).mockReturnValueOnce('DOTENV_VAR=from_dotenv');
|
|
196
|
-
mockYaml.mockReturnValueOnce({
|
|
197
|
-
YAML_VAR: 'from_yaml'
|
|
198
|
-
});
|
|
199
|
-
mockParse.mockReturnValueOnce({
|
|
200
|
-
DOTENV_VAR: 'from_dotenv'
|
|
201
|
-
});
|
|
202
|
-
const result = load_env(['config.yaml', '.env']);
|
|
203
|
-
expect(mockYaml).toHaveBeenCalled();
|
|
204
|
-
expect(mockParse).toHaveBeenCalled();
|
|
205
|
-
expect(result).toEqual({
|
|
206
|
-
YAML_VAR: 'from_yaml',
|
|
207
|
-
DOTENV_VAR: 'from_dotenv'
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
it('dotenv file in mixed mode still gets expanded', () => {
|
|
211
|
-
mockReadFileSync.mockReturnValueOnce(Buffer.from('BASE: https://api.com')).mockReturnValueOnce('URL=$BASE/v1');
|
|
212
|
-
mockYaml.mockReturnValueOnce({
|
|
213
|
-
BASE: 'https://api.com'
|
|
214
|
-
});
|
|
215
|
-
mockParse.mockReturnValueOnce({
|
|
216
|
-
URL: '$BASE/v1'
|
|
217
|
-
});
|
|
218
|
-
// In mixed mode, expand is called per-dotenv-file
|
|
219
|
-
mockExpand.mockReturnValueOnce({
|
|
220
|
-
parsed: {
|
|
221
|
-
URL: 'https://api.com/v1'
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
const result = load_env(['config.yaml', '.env']);
|
|
225
|
-
expect(result).toEqual({
|
|
226
|
-
BASE: 'https://api.com',
|
|
227
|
-
URL: 'https://api.com/v1'
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
it('last file wins for conflicting keys in mixed mode', () => {
|
|
231
|
-
mockReadFileSync.mockReturnValueOnce(Buffer.from('SHARED: from_yaml')).mockReturnValueOnce('SHARED=from_dotenv');
|
|
232
|
-
mockYaml.mockReturnValueOnce({
|
|
233
|
-
SHARED: 'from_yaml'
|
|
234
|
-
});
|
|
235
|
-
mockParse.mockReturnValueOnce({
|
|
236
|
-
SHARED: 'from_dotenv'
|
|
237
|
-
});
|
|
238
|
-
const result = load_env(['config.yaml', '.env']);
|
|
239
|
-
expect(result.SHARED).toBe('from_dotenv');
|
|
240
|
-
});
|
|
241
|
-
it('handles dotenv first, then yaml', () => {
|
|
242
|
-
mockReadFileSync.mockReturnValueOnce('DOTENV_VAR=first').mockReturnValueOnce(Buffer.from('YAML_VAR: second'));
|
|
243
|
-
mockParse.mockReturnValueOnce({
|
|
244
|
-
DOTENV_VAR: 'first'
|
|
245
|
-
});
|
|
246
|
-
mockYaml.mockReturnValueOnce({
|
|
247
|
-
YAML_VAR: 'second'
|
|
248
|
-
});
|
|
249
|
-
const result = load_env(['.env', 'config.yml']);
|
|
250
|
-
expect(result).toEqual({
|
|
251
|
-
DOTENV_VAR: 'first',
|
|
252
|
-
YAML_VAR: 'second'
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
//# sourceMappingURL=load-env.spec.js.map
|