From dc6083d00c810cc6c64bba3a3e9ba728796b6976 Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Wed, 28 Feb 2024 11:41:51 +0100 Subject: [PATCH] Support multiline values (#233) --- source/index.ts | 25 ++++++++++++++++++++++--- source/test.ts | 26 +++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/source/index.ts b/source/index.ts index 6be3928..40203a7 100644 --- a/source/index.ts +++ b/source/index.ts @@ -9,9 +9,9 @@ export type Input = Record /** Parse an envfile string. */ export function parse(src: string): Data { const result: Data = {} - const lines = src.toString().split('\n') + const lines = splitInLines(src) for (const line of lines) { - const match = line.match(/^([^=:#]+?)[=:](.*)/) + const match = line.match(/^([^=:#]+?)[=:]((.|\n)*)/) if (match) { const key = match[1].trim() const value = match[2].trim().replace(/['"]+/g, '') @@ -26,9 +26,28 @@ export function stringify(obj: Input): string { let result = '' for (const [key, value] of Object.entries(obj)) { if (key) { - const line = `${key}=${String(value)}` + const line = `${key}=${jsonValueToEnv(value)}` result += line + '\n' } } return result } + +function splitInLines(src: string): string[] { + return src + .replace(/("[\s\S]*?")/g, (_m, cg) => { + return cg.replace(/\n/g, '%%LINE-BREAK%%') + }) + .split('\n') + .filter((i) => Boolean(i.trim())) + .map((i) => i.replace(/%%LINE-BREAK%%/g, '\n')) +} + +function jsonValueToEnv(value: any): string { + let processedValue = String(value) + processedValue = processedValue.replace(/\n/g, '\\n') + processedValue = processedValue.includes('\\n') + ? `"${processedValue}"` + : processedValue + return processedValue +} diff --git a/source/test.ts b/source/test.ts index 0b9a596..da81384 100644 --- a/source/test.ts +++ b/source/test.ts @@ -8,7 +8,7 @@ import filedirname from 'filedirname' import { resolve } from 'path' // local -import { parse } from './index.js' +import { parse, stringify } from './index.js' // prepare const [file, dir] = filedirname() @@ -49,4 +49,28 @@ kava.suite('envfile', function (suite, test) { deepEqual(result, expected) done() }) + + test('line breaks inside quotes should be preserved on parse', function (done) { + const str = `name="bob\nmarley"\nplanet=earth` + const expected = { + name: 'bob\nmarley', + planet: 'earth', + } + const result = parse(str) + + deepEqual(result, expected) + done() + }) + + test('line breaks inside quotes should be preserved on stringify', function (done) { + const input = { + name: 'bob\nmarley', + planet: 'earth', + } + const expected = `name="bob\\nmarley"\nplanet=earth\n` + const result = stringify(input) + + deepEqual(result, expected) + done() + }) })