configuru
Version:
Manage the configuration of your Nodejs application with multiple environments and custom preferences, utilizing Configuru in CI and development as well!
88 lines (81 loc) • 2.45 kB
text/typescript
import { readFileSync, statSync } from 'fs'
import { format, join, parse, resolve } from 'path'
import { JSONC } from './helpers'
import { ConfigLoaderOptions } from './loader'
const fileExistsSync = (path: string) => {
try {
return statSync(path).isFile()
} catch (_error) {
return false
}
}
const fromPairs = (pairs: Array<[keyof any, any]>) =>
Object.assign({}, ...Array.from(pairs, ([k, v]) => ({ [k]: v })))
const uniq = <T>(xs: T[]) => Array.from(new Set(xs))
const { keys } = Object
const loadInline = (secrets: string) => {
try {
return JSONC.parse(secrets)
} catch {
throw new Error('Invalid inline secrets')
}
}
const loadFile = (filePath?: string) => {
if (!filePath) return {}
const { dir, name } = parse(filePath)
const testPaths = uniq([
filePath,
format({ dir, name, ext: '.json' }),
format({ dir, name, ext: '.jsonc' }),
])
const resolvedPath = testPaths.find(fileExistsSync)
if (!resolvedPath) {
throw new Error(
`File path set, but none of the following tested derivations exist:\n${testPaths
.map(p => ` - ${join(resolve('.'), p)}`)
.join('\n')}`
)
}
try {
return JSONC.parse(readFileSync(resolvedPath, 'utf-8'))
} catch (_error) {
throw new Error(
`Invalid config file in ${join(resolve('.'), resolvedPath)}`
)
}
}
const isInlineSecret = (
secretsOrFilename?: string
): secretsOrFilename is string => {
return secretsOrFilename?.trim().startsWith('{') ?? false
}
const loadSecrets = (secretsOrFilename?: string) => {
if (isInlineSecret(secretsOrFilename)) {
return loadInline(secretsOrFilename)
}
return loadFile(secretsOrFilename)
}
export const createConfigStorage = (
opts: Pick<
ConfigLoaderOptions,
'defaultConfigPath' | 'userConfigPath' | 'envMode'
>
): Record<any, any> => {
const defaultConfig = loadSecrets(opts.defaultConfigPath)
const userConfig = loadSecrets(opts.userConfigPath)
let envConfig: any = {}
if (opts.envMode === 'default' || opts.envMode === 'merged') {
const configKeys = [
...keys(defaultConfig),
...(opts.envMode === 'merged' ? keys(userConfig) : []),
]
envConfig = fromPairs(
configKeys
.map(k => [k, process.env[k]] as [string, any])
.filter(x => x[1] !== undefined)
)
} else if (opts.envMode === 'all') {
envConfig = process.env
}
return { ...defaultConfig, ...userConfig, ...envConfig }
}