env-file-rw
Version:
Read and write from .env file
173 lines (172 loc) • 6.21 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
var NodeType;
(function (NodeType) {
NodeType[NodeType["VARIABLE"] = 0] = "VARIABLE";
NodeType[NodeType["COMMENT"] = 1] = "COMMENT";
NodeType[NodeType["UNKNOWN"] = 2] = "UNKNOWN";
})(NodeType || (NodeType = {}));
const RE_NEWLINES = /\\n/g;
const NEWLINE = '\n';
const NEWLINES_MATCH = /\r\n|\n|\r/;
class EnvFileWriter {
/**
* if immediateParse, content is parsed synchronously
*/
constructor(fileName, immediateParse = true) {
this.tree = {};
this.treePendingChanges = {};
this.fileName = fileName;
if (immediateParse) {
this.parseSync();
}
}
parseSingleValue(value) {
const end = value.length - 1;
const isDoubleQuoted = value[0] === '"' && value[end] === '"';
const isSingleQuoted = value[0] === "'" && value[end] === "'";
if (isSingleQuoted || isDoubleQuoted) {
value = value.substring(1, end);
if (isDoubleQuoted) {
value = value.replace(RE_NEWLINES, NEWLINE);
}
}
else {
value = value.trim();
}
return value;
}
/**
* Categorize each line and it's type
*/
constructTreeFromString(fileContent) {
const allLines = fileContent.split(NEWLINES_MATCH);
const tree = {};
// use of foreach to have the index
allLines.forEach((line, index) => {
var _a;
// higly inspired from the parse function of dotenv, because reasons
const keyValue = line.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/);
if (keyValue) {
const key = keyValue[1];
const value = ((_a = keyValue[2]) !== null && _a !== void 0 ? _a : '');
tree[key] = {
type: NodeType.VARIABLE,
key,
value: this.parseSingleValue(value),
line: index
};
}
else if (line.trimStart().startsWith("#")) {
tree[index] = {
type: NodeType.COMMENT,
key: index.toString(),
value: line,
line: index
};
}
else {
tree[index] = {
type: NodeType.UNKNOWN,
key: index.toString(),
value: line,
line: index
};
}
});
return tree;
}
applyPendingChangesOnString(fileContent) {
const eachLines = fileContent.split(NEWLINES_MATCH);
for (const key in this.treePendingChanges) {
const element = this.treePendingChanges[key];
if (element.line === undefined) {
eachLines.push(`${element.key}=${element.value}`);
}
else {
eachLines[element.line] = `${element.key}=${element.value}`;
}
}
return eachLines.join("\n");
}
/**
* read the file and parse the content, synchronously
*/
parseSync() {
const fileContent = fs_1.readFileSync(this.fileName, "utf8");
return this.tree = this.constructTreeFromString(fileContent);
}
/**
* read the file and parse the content
*/
parse() {
return __awaiter(this, void 0, void 0, function* () {
const fileContent = yield promises_1.readFile(this.fileName, "utf8");
return this.tree = this.constructTreeFromString(fileContent);
});
}
exists(key) {
return this.tree[key] !== undefined;
}
/**
* Get the value of a key
*/
get(key, defaultValue) {
var _a, _b;
return (_b = (_a = this.tree[key]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : defaultValue;
}
/**
* Set the value of a key
* call .save() to persist changes
*/
set(key, value) {
if (!this.exists(key)) {
this.treePendingChanges[key] = {
key: key,
value: this.parseSingleValue(value),
type: NodeType.VARIABLE,
line: undefined,
};
}
else {
this.treePendingChanges[key] = {
key: key,
value: this.parseSingleValue(value),
type: this.tree[key].type,
line: this.tree[key].line,
};
}
}
/**
* Persists the pending changes, synchronously
*/
saveSync() {
const fileContent = fs_1.readFileSync(this.fileName, "utf8");
fs_1.writeFileSync(this.fileName, this.applyPendingChangesOnString(fileContent));
this.tree = Object.assign(Object.assign({}, this.tree), this.treePendingChanges);
this.treePendingChanges = {};
}
/**
* Persists the pending changes
*/
save() {
return __awaiter(this, void 0, void 0, function* () {
const fileContent = (yield promises_1.readFile(this.fileName, "utf8"));
yield promises_1.writeFile(this.fileName, this.applyPendingChangesOnString(fileContent));
this.tree = Object.assign(Object.assign({}, this.tree), this.treePendingChanges);
this.treePendingChanges = {};
});
}
}
exports.default = EnvFileWriter;