-
Notifications
You must be signed in to change notification settings - Fork 375
/
env.js
155 lines (135 loc) · 5.5 KB
/
env.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* eslint-env node */
/*
* Env file to load and validate env variables
* Be cautious; this file should not be imported into your source folder.
* We split the env variables into two parts:
* 1. Client variables: These variables are used in the client-side code (src folder).
* 2. Build-time variables: These variables are used in the build process (app.config.ts file).
* Import this file into the `app.config.ts` file to use environment variables during the build process. The client variables can then be passed to the client-side using the extra field in the `app.config.ts` file.
* To access the client environment variables in your `src` folder, you can import them from `@env`. For example: `import Env from '@env'`.
*/
/**
* 1st part: Import packages and Load your env variables
* we use dotenv to load the correct variables from the .env file based on the APP_ENV variable (default is development)
* APP_ENV is passed as an inline variable while executing the command, for example: APP_ENV=staging pnpm build:android
*/
const z = require('zod');
const packageJSON = require('./package.json');
const path = require('path');
const APP_ENV = process.env.APP_ENV ?? 'development';
const envPath = path.resolve(__dirname, `.env.${APP_ENV}`);
require('dotenv').config({
path: envPath,
});
/**
* 2nd part: Define some static variables for the app
* Such as: bundle id, package name, app name.
*
* You can add them to the .env file but we think it's better to keep them here as as we use prefix to generate this values based on the APP_ENV
* for example: if the APP_ENV is staging, the bundle id will be com.obytes.staging
*/
// TODO: Replace these values with your own
const BUNDLE_ID = 'com.obytes'; // ios bundle id
const PACKAGE = 'com.obytes'; // android package name
const NAME = 'ObytesApp'; // app name
const EXPO_ACCOUNT_OWNER = 'obytes'; // expo account owner
const EAS_PROJECT_ID = 'c3e1075b-6fe7-4686-aa49-35b46a229044'; // eas project id
const SCHEME = 'obytesApp'; // app scheme
/**
* We declare a function withEnvSuffix that will add a suffix to the variable name based on the APP_ENV
* Add a suffix to variable env based on APP_ENV
* @param {string} name
* @returns {string}
*/
const withEnvSuffix = (name) => {
return APP_ENV === 'production' ? name : `${name}.${APP_ENV}`;
};
/**
* 2nd part: Define your env variables schema
* we use zod to define our env variables schema
*
* we split the env variables into two parts:
* 1. client: These variables are used in the client-side code (`src` folder).
* 2. buildTime: These variables are used in the build process (app.config.ts file). You can think of them as server-side variables.
*
* Main rules:
* 1. If you need your variable on the client-side, you should add it to the client schema; otherwise, you should add it to the buildTime schema.
* 2. Whenever you want to add a new variable, you should add it to the correct schema based on the previous rule, then you should add it to the corresponding object (_clientEnv or _buildTimeEnv).
*
* Note: `z.string()` means that the variable exists and can be an empty string, but not `undefined`.
* If you want to make the variable required, you should use `z.string().min(1)` instead.
* Read more about zod here: https://zod.dev/?id=strings
*
*/
const client = z.object({
APP_ENV: z.enum(['development', 'staging', 'production']),
NAME: z.string(),
SCHEME: z.string(),
BUNDLE_ID: z.string(),
PACKAGE: z.string(),
VERSION: z.string(),
// ADD YOUR CLIENT ENV VARS HERE
API_URL: z.string(),
VAR_NUMBER: z.number(),
VAR_BOOL: z.boolean(),
});
const buildTime = z.object({
EXPO_ACCOUNT_OWNER: z.string(),
EAS_PROJECT_ID: z.string(),
// ADD YOUR BUILD TIME ENV VARS HERE
SECRET_KEY: z.string(),
});
/**
* @type {Record<keyof z.infer<typeof client> , unknown>}
*/
const _clientEnv = {
APP_ENV,
NAME: NAME,
SCHEME: SCHEME,
BUNDLE_ID: withEnvSuffix(BUNDLE_ID),
PACKAGE: withEnvSuffix(PACKAGE),
VERSION: packageJSON.version,
// ADD YOUR ENV VARS HERE TOO
API_URL: process.env.API_URL,
VAR_NUMBER: Number(process.env.VAR_NUMBER),
VAR_BOOL: process.env.VAR_BOOL === 'true',
};
/**
* @type {Record<keyof z.infer<typeof buildTime> , unknown>}
*/
const _buildTimeEnv = {
EXPO_ACCOUNT_OWNER,
EAS_PROJECT_ID,
// ADD YOUR ENV VARS HERE TOO
SECRET_KEY: process.env.SECRET_KEY,
};
/**
* 3rd part: Merge and Validate your env variables
* We use zod to validate our env variables based on the schema we defined above
* If the validation fails we throw an error and log the error to the console with a detailed message about missed variables
* If the validation passes we export the merged and parsed env variables to be used in the app.config.ts file as well as a ClientEnv object to be used in the client-side code
**/
const _env = {
..._clientEnv,
..._buildTimeEnv,
};
const merged = buildTime.merge(client);
const parsed = merged.safeParse(_env);
if (parsed.success === false) {
console.error(
'❌ Invalid environment variables:',
parsed.error.flatten().fieldErrors,
`\n❌ Missing variables in .env.${APP_ENV} file, Make sure all required variables are defined in the .env.${APP_ENV} file.`,
`\n💡 Tip: If you recently updated the .env.${APP_ENV} file and the error still persists, try restarting the server with the -c flag to clear the cache.`
);
throw new Error(
'Invalid environment variables, Check terminal for more details '
);
}
const Env = parsed.data;
const ClientEnv = client.parse(_clientEnv);
module.exports = {
Env,
ClientEnv,
withEnvSuffix,
};