@glitchdotcom/dotenv
is a totally tubular ๐๐ผ library to parse and manipulate dotenv
syntax while maintaining comments and whitespace. @glitchdotcom/dotenv
has zero
dependencies, is licensed under the MIT license, and has a simple, functional api.
# npm
npm install @glitchdotcom/dotenv
# yarn
yarn add @glitchdotcom/dotenv
# pnpm
pnpm install @glitchdotcom/dotenv
@glitchdotcom/dotenv
is licensed under the MIT license. a copy of this license can be
found in the LICENSE file.
for this project's code of conduct, see the CODE_OF_CONDUCT.md file.
do you want to contribute to something ~amazing~ (this package)? yes?? well the CONTRIBUTING.md file should tell you everything you need to know! if it doesn't, let us know (which would make a perfect first contribution)!
to see how dotenv syntax looks as a node list, you can play with an interactive parser over at glitch.com!
import * as dotenv from "@glitchdotcom/dotenv";
// or... const dotenv = require("@glitchdotcom/dotenv");
const envText = `SOME_KEY=some value`;
// node list represents the following as an array of objects...
// SOME_KEY=some value
let nodes = dotenv.textToNodes(envText);
// find index of the key value node with SOME_KEY as the key
const index = nodes.find((node) => node.key === "SOME_KEY");
// nodes now represents...
// ANOTHER_KEY=some value
nodes = dotenv.changeKey(nodes, index, "ANOTHER_KEY");
// nodes now represents...
// ANOTHER_KEY=another value
nodes = dotenv.changeValue(nodes, index, "another value");
// nodes now represents...
// ANOTHER_KEY=another value
// NEW_KEY=new value
nodes = dotenv.appendKeyValue(nodes, "NEW_KEY", "new value");
// nodes now represents...
// NEW_KEY=new value
nodes = dotenv.removeKeyValue(nodes, index);
// newText will be "NEW_KEY=new value"
const newText = dotenv.nodesToText(nodes);
// parsedText will be { NEW_KEY: "new value" }
const parsedText = dotenv.parseText(newText);
// parsedNodes will be { NEW_KEY: "new value" }
const parsedNodes = dotenv.parseNodes(nodes);
dotenv exports a handful of consts that represent the different types of nodes. there are
3 node types, KEY_VALUE
, COMMENT
, and INVALID_LINE
.
example:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY=value
# some comment`);
let firstCommentNode = nodes.find((node) => node.type === dotenv.COMMENT);
if (!firstCommentNode) {
// no comment node found!
} else {
assert(firstCommentNode.comment === "some comment");
}
KEY_VALUE
nodes have the following properties: type (will always be "KEY_VALUE"
), key (no
whitespace), value (no whitespace), escapedValue (value including escaped newlines),
fullKey (includes extraneous whitespace), fullValue (includes extraneous whitespace), quote (",
', or an empty string depending on what quotes are around the value), and originalQuote (the
quotes that were around the value when the text was parsed). as an example, a key-value pair
like this...
# extra whitespace intentional
SOME_KEY= some value
...will be parsed into a node like this...
{
type: "KEY_VALUE",
key: "SOME_KEY",
fullKey: " SOME_KEY",
value: "some value",
escapedValue: "some value",
fullValue: " some value",
quote: "",
originalQuote: ""
}
if the value contained escaped newlines and quotes around it like this...
# extra whitespace intentional
SOME_KEY=" some \n value"
...then it will be parsed into something like this...
{
type: "KEY_VALUE",
key: "SOME_KEY",
fullKey: " SOME_KEY",
value: " some \n value", // notice the extra space in front
escapedValue: " some \\n value",
fullValue: "\" some \\n value\"",
quote: "\"", // quote and originalQuote contain the quote that was used
originalQuote: "\""
}
COMMENT
nodes have the following properties: type (will always be "COMMENT"
), prefix (pound
symbol and extraneous whitespace before comment body), and comment (the comment body). as an
example, a comment line like this...
# a comment! leading whitespace intentional
...will be parsed into a node like this...
{
type: "COMMENT",
prefix: " # ", // note the whitespace in the prefix
comment: "a comment! leading whitespace intentional"
}
INVALID_LINE
nodes have the following properties: type (will always be INVALID_LINE
), and
line (the contents of the line). as an example, an invalid line like this...
i am an invalid line because i do not contain an equals sign!
...will be parsed into a node like this...
{
type: "INVALID_LINE",
line: "i am an invalid line because i do not contain an equals sign!"
}
an important thing to note with INVALID_LINE
nodes is that an empty line is also an
INVALID_LINE
since it doesn't contain an equals sign! empty lines aren't ignored since one
of the goals of @glitchdotcom/dotenv
is to preserve as much whitespace as possible. we
have to capture that data somehow, so we do it with an INVALID_LINE
node that has an empty
string as the line
property.
{
type: "INVALID_LINE",
line: ""
}
textToNodes
converts a string to a dotenv node list that can be manipulated. the node list
represents the dotenv data contained within the string as an array of nodes. it maintains comments,
empty/invalid lines, and extraneous whitespace. it does everything it can to return something
even if the string passed to it is not valid dotenv syntax. nodesToText
does the reverse of
textToNodes
. it takes a node list and converts it back into a string. like textToNodes
,
it maintains comments, empty/invalid lines, and extraneous whitespace.
example:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY=value`);
let text = dotenv.nodesToText(nodes);
assert(text === `KEY=value`);
appendKeyValue
adds a new key value pair to the end of the node list. like all dotenv manipulation
functions, appendKeyValue
is immutable and returns a new list instead of modifying the
list in place.
example:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY=value`);
nodes = dotenv.appendKeyValue(nodes, "SOMETHING", "anything");
let text = dotenv.nodesToText(nodes);
assert(
text ===
`KEY=value
SOMETHING=anything`,
);
changeKey
changes the key of a key-value node. using changeKey
does not modify
any whitespace that was present in the original text. like all dotenv manipulation
functions, changeKey
is immutable and returns a new list instead of modifying the
list in place.
examples:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY=value`);
let index = nodes.find((node) => node.key === "KEY");
nodes = dotenv.changeKey(nodes, index, "SOMETHING_ELSE");
let text = dotenv.nodesToText(nodes);
assert(text === `SOMETHING_ELSE=value`);
// now with whitespace!
let nodes = dotenv.textToNodes(` KEY =value`);
let index = nodes.find((node) => node.key === "KEY"); // the `key` property does not include whitespace
nodes = dotenv.changeKey(nodes, index, "SOMETHING_ELSE");
let text = dotenv.nodesToText(nodes);
assert(text === ` SOMETHING_ELSE =value`); // when converting back to text, the original whitespace is preserved!
changeValue
changes the value of a key-value node. it automatically escapes newlines
and adds quotes around the value if necessary. like changeKey
, the original whitespace
is not modified. like all dotenv manipulation functions, changeValue
is immutable and
returns a new list instead of modifying the list in place.
example:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY= value`); // notice whitespace
let index = nodes.find((node) => node.key === "KEY");
nodes = dotenv.changeValue(nodes, index, "another\nvalue"); // new value contains a newline
let text = dotenv.nodesToText(nodes);
// the value is now quoted and the newline was escaped! also, the preceding whitespace
// was kept intact!
assert(text === `KEY= "another\\nvalue"`);
removeKeyValue
removes a key-value node from the node list. like all dotenv manipulation
functions, removeKeyValue
is immutable and returns a new list instead of modifying the
list in place.
example:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY=value
SOMETHING=anything`);
let index = nodes.find((node) => node.key === "SOMETHING");
nodes = dotenv.removeKeyValue(nodes, index); // removing the `SOMETHING` property
let text = dotenv.nodesToText(nodes);
assert(text === `KEY=value`);
parseText
takes dotenv text and extracts the key-value pairs into an object (similar
to the original dotenv
package).
example:
import * as dotenv from "@glitchdotcom/dotenv";
let env = dotenv.parseText(`KEY=value`);
assert(env.KEY === `value`);
parseNodes
takes a node list and extracts the key-value pairs into an object. generally
you'd want to use parseText
, however if you've already converted the text into a node
list, parseNodes
would skip the text parsing step.
example:
import * as dotenv from "@glitchdotcom/dotenv";
let nodes = dotenv.textToNodes(`KEY=value`);
let env = dotenv.parseNodes(nodes);
assert(env.KEY === `value`);
Made by Glitch
\ ใoใ)ใ