-
-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: made adding a new loader easier
Co-authored-by: KilianKilmister <[email protected]>
- Loading branch information
1 parent
4790a82
commit 701a6e5
Showing
4 changed files
with
208 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { Environment } from '../types.js' | ||
|
||
/** | ||
* Parse out all env vars from a given env file string and return an object | ||
*/ | ||
export function parseEnvString(envFileString: string): Environment { | ||
// First thing we do is stripe out all comments | ||
envFileString = stripComments(envFileString.toString()) | ||
|
||
// Next we stripe out all the empty lines | ||
envFileString = stripEmptyLines(envFileString) | ||
|
||
// Merge the file env vars with the current process env vars (the file vars overwrite process vars) | ||
return parseEnvVars(envFileString) | ||
} | ||
|
||
/** | ||
* Parse out all env vars from an env file string | ||
*/ | ||
export function parseEnvVars(envString: string): Environment { | ||
const envParseRegex = /^((.+?)[=](.*))$/gim | ||
const matches: Environment = {} | ||
let match | ||
while ((match = envParseRegex.exec(envString)) !== null) { | ||
// Note: match[1] is the full env=var line | ||
const key = match[2].trim() | ||
let value: string | number | boolean = match[3].trim() | ||
|
||
// remove any surrounding quotes | ||
value = value | ||
.replace(/(^['"]|['"]$)/g, '') | ||
.replace(/\\n/g, '\n') | ||
|
||
// Convert string to JS type if appropriate | ||
if (value !== '' && !isNaN(+value)) { | ||
matches[key] = +value | ||
} | ||
else if (value === 'true') { | ||
matches[key] = true | ||
} | ||
else if (value === 'false') { | ||
matches[key] = false | ||
} | ||
else { | ||
matches[key] = value | ||
} | ||
} | ||
return JSON.parse(JSON.stringify(matches)) as Environment | ||
} | ||
|
||
/** | ||
* Strips out comments from env file string | ||
*/ | ||
export function stripComments(envString: string): string { | ||
const commentsRegex = /(^\s*#.*$)/gim | ||
return envString.replace(commentsRegex, '') | ||
} | ||
|
||
/** | ||
* Strips out newlines from env file string | ||
*/ | ||
export function stripEmptyLines(envString: string): string { | ||
const emptyLinesRegex = /(^\n)/gim | ||
return envString.replace(emptyLinesRegex, '') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { assert } from 'chai' | ||
import { | ||
stripEmptyLines, | ||
stripComments, | ||
parseEnvVars, | ||
parseEnvString, | ||
} from '../../src/loaders/env.js' | ||
|
||
describe('stripEmptyLines', (): void => { | ||
it('should strip out all empty lines', (): void => { | ||
const envString = stripEmptyLines( | ||
'\nBOB=COOL\n\nNODE_ENV=dev\n\nANSWER=42 AND COUNTING\n\n', | ||
) | ||
assert(envString === 'BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n') | ||
}) | ||
}) | ||
|
||
describe('stripComments', (): void => { | ||
it('should strip out all full line comments', (): void => { | ||
const envString = stripComments( | ||
'#BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n#AnotherComment\n', | ||
) | ||
assert(envString === '\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n\n') | ||
}) | ||
|
||
it('should not strip out #s from values', (): void => { | ||
const envString = stripComments( | ||
'#\nBOB=COMMENT#ELL\n#\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n#AnotherComment\n', | ||
) | ||
assert( | ||
envString === | ||
'\nBOB=COMMENT#ELL\n\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n\n', | ||
envString, | ||
) | ||
}) | ||
}) | ||
|
||
describe('parseEnvVars', (): void => { | ||
it("should parse out all env vars in string when not ending with '\\n'", (): void => { | ||
const envVars = parseEnvVars( | ||
'BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\nNUMBER=42\nBOOLEAN=true', | ||
) | ||
assert(envVars.BOB === 'COOL') | ||
assert(envVars.NODE_ENV === 'dev') | ||
assert(envVars.ANSWER === '42 AND COUNTING') | ||
assert(envVars.NUMBER === 42) | ||
assert(envVars.BOOLEAN === true) | ||
}) | ||
|
||
it("should parse out all env vars in string with format 'key=value'", (): void => { | ||
const envVars = parseEnvVars( | ||
'BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n', | ||
) | ||
assert(envVars.BOB === 'COOL') | ||
assert(envVars.NODE_ENV === 'dev') | ||
assert(envVars.ANSWER === '42 AND COUNTING') | ||
}) | ||
|
||
it('should ignore invalid lines', (): void => { | ||
const envVars = parseEnvVars( | ||
'BOB=COOL\nTHISIS$ANDINVALIDLINE\nANSWER=42 AND COUNTING\n', | ||
) | ||
assert(Object.keys(envVars).length === 2) | ||
assert(envVars.BOB === 'COOL') | ||
assert(envVars.ANSWER === '42 AND COUNTING') | ||
}) | ||
|
||
it('should default an empty value to an empty string', (): void => { | ||
const envVars = parseEnvVars('EMPTY=\n') | ||
assert(envVars.EMPTY === '') | ||
}) | ||
|
||
it('should escape double quoted values', (): void => { | ||
const envVars = parseEnvVars('DOUBLE_QUOTES="double_quotes"\n') | ||
assert(envVars.DOUBLE_QUOTES === 'double_quotes') | ||
}) | ||
|
||
it('should escape single quoted values', (): void => { | ||
const envVars = parseEnvVars("SINGLE_QUOTES='single_quotes'\n") | ||
assert(envVars.SINGLE_QUOTES === 'single_quotes') | ||
}) | ||
|
||
it('should preserve embedded double quotes', (): void => { | ||
const envVars = parseEnvVars( | ||
'DOUBLE=""""\nDOUBLE_ONE=\'"double_one"\'\nDOUBLE_TWO=""double_two""\n', | ||
) | ||
assert(envVars.DOUBLE === '""') | ||
assert(envVars.DOUBLE_ONE === '"double_one"') | ||
assert(envVars.DOUBLE_TWO === '"double_two"') | ||
}) | ||
|
||
it('should preserve newlines when surrounded in quotes', (): void => { | ||
const envVars = parseEnvVars( | ||
'ONE_NEWLINE="ONE\\n"\nTWO_NEWLINES="HELLO\\nWORLD\\n"\nTHREE_NEWLINES="HELLO\\n\\nWOR\\nLD"\n', | ||
) | ||
assert(envVars.ONE_NEWLINE === 'ONE\n') | ||
assert(envVars.TWO_NEWLINES === 'HELLO\nWORLD\n') | ||
assert(envVars.THREE_NEWLINES === 'HELLO\n\nWOR\nLD') | ||
}) | ||
|
||
it('should preserve embedded single quotes', (): void => { | ||
const envVars = parseEnvVars( | ||
"SINGLE=''''\nSINGLE_ONE=''single_one''\nSINGLE_TWO=\"'single_two'\"\n", | ||
) | ||
assert(envVars.SINGLE === "''") | ||
assert(envVars.SINGLE_ONE === "'single_one'") | ||
assert(envVars.SINGLE_TWO === "'single_two'") | ||
}) | ||
|
||
it('should parse out all env vars ignoring spaces around = sign', (): void => { | ||
const envVars = parseEnvVars( | ||
'BOB = COOL\nNODE_ENV =dev\nANSWER= 42 AND COUNTING', | ||
) | ||
assert(envVars.BOB === 'COOL') | ||
assert(envVars.NODE_ENV === 'dev') | ||
assert(envVars.ANSWER === '42 AND COUNTING') | ||
}) | ||
|
||
it('should parse out all env vars ignoring spaces around = sign', (): void => { | ||
const envVars = parseEnvVars( | ||
'BOB = "COOL "\nNODE_ENV = dev\nANSWER= \' 42 AND COUNTING\'', | ||
) | ||
assert(envVars.BOB === 'COOL ') | ||
assert(envVars.NODE_ENV === 'dev') | ||
assert(envVars.ANSWER === ' 42 AND COUNTING') | ||
}) | ||
}) | ||
|
||
describe('parseEnvString', (): void => { | ||
it('should parse env vars and merge (overwrite) with process.env vars', (): void => { | ||
const env = parseEnvString('BOB=COOL\nNODE_ENV=dev\nANSWER=42\n') | ||
assert(env.BOB === 'COOL') | ||
assert(env.NODE_ENV === 'dev') | ||
assert(env.ANSWER === 42) | ||
}) | ||
}) |
Oops, something went wrong.