Dotenv File Parser
Parse .env files with comments, export prefixes, quoted values, escapes and multiline strings without mutating process.env.
Code
export function parseDotenv(input: string): Record<string, string> {
if (typeof input !== "string") throw new TypeError("input must be a string");
const escapes: Record<string, string> = { "\\": "\\", n: "\n", r: "\r", t: "\t", '"': '"' };
const result: Record<string, string> = {};
const lines = input.replace(/\r\n?/g, "\n").split("\n");
for (let index = 0; index < lines.length; index += 1) {
let line = lines[index]!.trim();
if (line === "" || line.startsWith("#")) continue;
if (line.startsWith("export ")) line = line.slice("export ".length).trim();
const match = /^([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(line);
if (!match) throw new SyntaxError("invalid dotenv line " + (index + 1));
const key = match[1]!;
let raw = match[2]!;
let value: string;
if (raw.startsWith('"')) {
while (!/(^|[^\\])"\s*(#.*)?$/.test(raw)) {
index += 1;
if (index >= lines.length) throw new SyntaxError("unterminated quoted value for " + key);
raw += "\n" + lines[index]!;
}
const end = raw.lastIndexOf('"');
value = raw.slice(1, end).replace(/\\([\\nrt"])/g, (_, character: string) => {
return escapes[character] ?? character;
});
} else if (raw.startsWith("'")) {
let end = 1;
while (end < raw.length) {
if (raw[end] === "'") {
if (raw[end + 1] === "'") {
end += 2;
continue;
}
break;
}
end += 1;
}
if (end >= raw.length) throw new SyntaxError("unterminated quoted value for " + key);
value = raw.slice(1, end).replace(/''/g, "'");
} else {
value = raw.replace(/\s+#.*$/, "").trim();
}
result[key] = value;
}
return result;
}Report a problem with this exampleOpens a pre-filled issue on GitHub.