diff --git a/src/parse-env-file.ts b/src/parse-env-file.ts index 3c2cb46..c57ba0a 100644 --- a/src/parse-env-file.ts +++ b/src/parse-env-file.ts @@ -66,12 +66,19 @@ export function parseEnvVars(envString: string): Environment { 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() + let value = match[3].trim() - // remove any surrounding quotes - value = value - .replace(/(^['"]|['"]$)/g, '') - .replace(/\\n/g, '\n') + // if the string is quoted, remove everything after the final + // quote. This implicitly removes inline comments. + if (value.startsWith("'") || value.startsWith('"')) { + value = value.slice(1, value.lastIndexOf(value[0])); + } else { + // if the string is not quoted, we need to explicitly remove + // inline comments. + value = value.split('#')[0].trim(); + } + + value = value.replace(/\\n/g, '\n'); // Convert string to JS type if appropriate if (value !== '' && !isNaN(+value)) { diff --git a/test/parse-env-file.spec.ts b/test/parse-env-file.spec.ts index 6ca7c87..cb01827 100644 --- a/test/parse-env-file.spec.ts +++ b/test/parse-env-file.spec.ts @@ -96,6 +96,36 @@ describe('parseEnvVars', (): void => { assert(envVars.NODE_ENV === 'dev') assert(envVars.ANSWER === ' 42 AND COUNTING') }) + + describe('value', () => { + const testCases = [ + [`a`, `a`], + [`'a'`, `a`], + [`"a"`, `a`], + [`"a"`, `a`], + [`"single 'quotes' inside double"`, `single 'quotes' inside double`], + [`'double "quotes" inside single'`, `double "quotes" inside single`], + [`a # comment without quotes`, `a`], + [`'a' # comment with single quotes`, `a`], + [`"a" # comment with double quotes`, `a`], + [`"a"garbage after final quote`, `a`], + [ + `"double quotes " within double quotes"`, + `double quotes " within double quotes`, + ], + [ + `'single quotes ' within single quotes'`, + `single quotes ' within single quotes`, + ], + [`line\\nbreaks`, `line\nbreaks`], + [`"line\\n\\nbreaks in quotes"`, `line\n\nbreaks in quotes`], + ]; + for (const [input, output] of testCases) { + it(`should parse ${input}`, () => { + assert.equal(parseEnvVars(`KEY=${input}`).KEY, output) + }) + } + }) }) describe('parseEnvString', (): void => { @@ -172,6 +202,14 @@ describe('getEnvFileVars', (): void => { ONLY: 'IN=PRODUCTION', GALAXY: 'hitch\nhiking', BRINGATOWEL: true, + a: 1, + b: 2, + c: 3, + d: "=", + e: "equals symbol = = ", + json_no_quotes: "{\"foo\": \"bar\"}", + json_with_quotes: "{\"foo\": \"bar\"}", + quotes: "hello \n\n \" 'escaped \\tsingle quote' \" #cool #fun ", }) }) diff --git a/test/test-files/test b/test/test-files/test index bd9a1d2..da68452 100644 --- a/test/test-files/test +++ b/test/test-files/test @@ -3,3 +3,14 @@ ANSWER=42 ONLY= "IN=PRODUCTION" GALAXY="hitch\nhiking" BRINGATOWEL=true +# full-line comment +# +quotes = 'hello \n\n " 'escaped \tsingle quote' " #cool #fun ' # inline comment +a=1#inline comment +b='2'#inline comment +c="3"#inline comment +d== +e='equals symbol = = ' +json_no_quotes={"foo": "bar"} +json_with_quotes='{"foo": "bar"}' +json_with_quotes='{"foo": "bar"}'