The dotenv editor lets you update .env* files without destroying formatting, comments, ordering, blank lines, or unknown lines, and it can deterministically select a target file across a get-dotenv paths cascade (with optional template bootstrap).
Use this when you want to:
.env.local or .env.<env>.local in sync with a secrets source without rewriting the whole file..env.local.template → .env.local).The editor is format-preserving by design: it parses into a document model, applies edits to the model, and renders back to text.
All APIs below are exported from the package root (@karmaniverous/get-dotenv).
High-level entrypoints:
editDotenvText(text, updates, options?) → string (pure text; no filesystem)editDotenvFile(updates, options) → { path, createdFromTemplate, changed } (FS adapter; target selection + template bootstrap + in-place edit)Lower-level building blocks (when you need more control):
parseDotenvDocument(text) → DotenvDocumentapplyDotenvEdits(doc, updates, opts?) → DotenvDocumentrenderDotenvDocument(doc, eolMode?) → stringTypes of interest:
DotenvUpdateMap, DotenvUpdateValueDotenvEditOptions (mode, duplicateKeys, eol, etc.)EditDotenvFileOptions, EditDotenvFileResultDotenvTargetScope (global | env), DotenvTargetPrivacy (public | private)Use this when you already have the file contents in memory (or when you’re writing a tool that manages IO separately).
import { editDotenvText } from '@karmaniverous/get-dotenv';
const before = [
'# header',
'',
'APP_SETTING=one',
'raw line stays',
'ENV_SETTING=two',
'',
].join('\n');
const after = editDotenvText(before, { APP_SETTING: 'updated', UNUSED: null });
console.log(after);
Key behaviors:
Updates are expressed as a Record<string, DotenvUpdateValue>.
Supported value types:
string | number | boolean → written as string tokens (number/boolean are stringified)Record<string, unknown> | unknown[] → written as JSON.stringify(value)null → delete matching key lines (default nullBehavior: 'delete')undefined → skip (default undefinedBehavior: 'skip')Important nuance (sync mode):
mode: 'sync', keys that are not present in the update map are deleted, but a key that is present with undefined counts as “present” and is not deleted (while also not being updated).mode: 'merge' (default): update existing keys and append missing keys; do not delete unrelated keys.mode: 'sync': delete assignment/bare-key lines for keys not present in the update map, while preserving comments/unknown lines.When the same key appears multiple times, you can choose how to update/delete occurrences:
duplicateKeys: 'all' (default)duplicateKeys: 'first'duplicateKeys: 'last'The editor prefers correctness over minimal diff, but it preserves quote style where it is safe to do so.
Rules of thumb:
#, the value is quoted to avoid comment truncation.FOO or BAR # comment are converted into assignments when updated (default separator =), preserving the inline comment.Rendering can preserve EOLs or normalize them:
eol: 'preserve' (default): detect file EOL and keep it; inserted line breaks use the detected EOL.eol: 'lf' or eol: 'crlf': normalize the entire output.Trailing newline presence/absence is preserved.
Use this when you want the library to select and update a specific dotenv target file across paths deterministically.
The target is determined by:
paths: string[] (directories only; this is the only search surface)scope: 'global' | 'env'privacy: 'public' | 'private'dotenvToken (default: .env)privateToken (default: local)env (and optional defaultEnv) when scope: 'env'Filename construction:
<dotenvToken><dotenvToken>.<env><dotenvToken>.<privateToken><dotenvToken>.<env>.<privateToken>By default, selection uses reverse path order (highest precedence wins), matching the get-dotenv overlay model:
searchOrder: 'reverse' (default): checks paths from last → firstsearchOrder: 'forward': checks paths from first → lastIf the target file does not exist anywhere under paths, but a template exists at <target>.<templateExtension>, it will be copied to <target> before editing.
templateExtension: 'template' by default, producing patterns like .env.local.template.If neither the target nor the template exists under any provided path, editDotenvFile throws.
.env.local from template and set keysimport { editDotenvFile } from '@karmaniverous/get-dotenv';
await editDotenvFile(
{ API_URL: 'https://example.com', UNUSED: null },
{
paths: ['./'],
scope: 'global',
privacy: 'private',
dotenvToken: '.env',
privateToken: 'local',
},
);
import { editDotenvFile } from '@karmaniverous/get-dotenv';
await editDotenvFile(
{ FEATURE_FLAG: '1' },
{
paths: ['./'],
scope: 'env',
privacy: 'private',
defaultEnv: 'dev',
},
);