-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.js
89 lines (72 loc) · 3.1 KB
/
index.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
'use strict';
const fs = require('fs');
const path = require('path');
const filterObj = require('filter-obj');
const camelcase = require('camelcase');
const camelcaseKeys = require('camelcase-keys');
const loadEnvFile = require('./lib/load-env-file');
const num = fs.constants;
const permissionMask = 0o777;
const ownerReadWrite = num.S_IRUSR | num.S_IWUSR;
const checkMode = (filepath, mask) => {
const status = fs.statSync(filepath);
if (!status.isFile()) {
throw new Error(`Filepath must be a file: ${filepath}`);
}
return status.mode & mask;
};
const assertHidden = (filepath) => {
const filename = path.basename(filepath);
if (!filename.startsWith('.')) {
throw new Error(`Filepath must be hidden, use ".${filename}" instead of "${filename}"`);
}
};
const envy = (input) => {
const envPath = input || '.env';
const examplePath = envPath + '.example';
assertHidden(envPath);
if (checkMode(examplePath, num.S_IWOTH) === num.S_IWOTH) {
throw new Error(`File must not be writable by others. Fix: chmod o-w '${examplePath}'`);
}
const exampleEnv = loadEnvFile(examplePath);
const exampleEnvKeys = Object.keys(exampleEnv);
const camelizedExampleEnvKeys = Object.keys(camelcaseKeys(exampleEnv));
if (exampleEnvKeys.length < 1) {
throw new Error(`At least one entry is required in ${examplePath}`);
}
const exampleHasValues = Object.values(exampleEnv).some((val) => {
return val !== '';
});
if (exampleHasValues) {
throw new Error(`No values are allowed in ${examplePath}, put them in ${envPath} instead`);
}
const camelizedGlobalEnv = camelcaseKeys(process.env);
const camelizedGlobalEnvKeys = Object.keys(camelizedGlobalEnv);
// We treat env vars as case insensitive, like Windows does.
const needsEnvFile = camelizedExampleEnvKeys.some((key) => {
return !camelizedGlobalEnvKeys.includes(key);
});
if (!needsEnvFile) {
return filterObj(camelizedGlobalEnv, camelizedExampleEnvKeys);
}
if (checkMode(envPath, permissionMask) !== ownerReadWrite) {
throw new Error(`File permissions are unsafe. Fix: chmod 600 '${envPath}'`);
}
const camelizedLocalEnv = camelcaseKeys(loadEnvFile(envPath));
const camelizedMergedEnv = Object.assign({}, camelizedLocalEnv, camelizedGlobalEnv);
const camelizedMergedEnvKeys = Object.keys(camelizedMergedEnv);
const camelizedMissingKeys = camelizedExampleEnvKeys.filter((key) => {
return !camelizedMergedEnv[key] || !camelizedMergedEnvKeys.includes(key);
});
if (camelizedMissingKeys.length > 0) {
const missingKeys = camelizedMissingKeys.map((camelizedMissingKey) => {
return exampleEnvKeys.find((exampleKey) => {
return camelcase(exampleKey) === camelizedMissingKey;
});
});
throw new Error(`Environment variables are missing: ${missingKeys.join(', ')}`);
}
const keepKeys = Array.from(new Set([...Object.keys(camelizedLocalEnv), ...camelizedExampleEnvKeys]));
return filterObj(camelizedMergedEnv, keepKeys);
};
module.exports = envy;