UNPKG

key-value-file

Version:

A simple key/value file parser/writer

240 lines 7.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KeyValue = void 0; const token_1 = require("./token"); /** * Class for manipulating key/value data (`.env` files f.ex) */ class KeyValue { /** * Constructor * @param tokens {@see tokenize:tokenize|tokenize()} */ constructor(tokens) { this._tokens = []; if (tokens) { this._tokens = tokens; } } /** * Returns the value of key `key` * @param key The key to get the value for * @returns `undefined` if the `key` is not found */ get(key) { const t = this.getValueForKey(key); return t && t.value; } /** * Set the value of key `key`. If the `key` doesn't exist it's created * @param key * @param value * @returns The instance being called */ set(key, value) { const t = this.getValueForKey(key); if (t) { t.value = `${value}`; } else { this.addNewlineBeforeNewValue(); this._tokens.push({ type: token_1.TokenType.Key, value: key }); this._tokens.push({ type: token_1.TokenType.Delimiter, value: '=' }); this._tokens.push({ type: token_1.TokenType.Value, value: `${value}` }); } return this; } /** * Rename the key `key` to `newName` * @param key The name of the key to rename * @param newName The new name of the key * @returns The instance being called */ rename(key, newName) { const t = this.getKey(key); if (t) { t.value = newName; } return this; } /** * Delete the key `key` * @param key The name of the key to delete * @returns The instance being called */ delete(key) { const k = this.getKeyIndex(key); if (k > -1) { const tokens = this._tokens; let i = k + 1; for (; i < tokens.length; i++) { const t = tokens[i]; if (t.type === token_1.TokenType.Value) { break; } } this._tokens.splice(k, i - k); } return this; } /** * Add new newline token */ addNewline() { return this.pushNewlineToken(); } /** * Add a comment node. * * Note! The comment should not have a leading `#`. * The comment can be multi-line * @param comment */ addComment(comment) { if (this._tokens.length) { this.pushNewlineToken(); } comment = comment .split('\n') .map((s) => `# ${s}`) .join('\n'); this._tokens.push({ type: token_1.TokenType.Comment, value: comment }); return this; } /** * Remove all comment tokens */ removeComments() { const tokens = this._tokens; const newTokens = []; const rmLeadingWs = () => { const i = newTokens.length - 2; if (newTokens[i] && newTokens[i].type === token_1.TokenType.Whitespace) { while (newTokens[i]) { if (newTokens[i].type === token_1.TokenType.Whitespace) { newTokens.splice(i, 1); } } } }; // tslint:disable-next-line: prefer-for-of for (let i = 0; i < tokens.length; i++) { if (tokens[i].type === token_1.TokenType.Comment) { if (!this.isTrailingComment(i)) { if (tokens[i - 1] && tokens[i - 1].type === token_1.TokenType.Whitespace) { rmLeadingWs(); } i += 1; } continue; } newTokens.push(tokens[i]); } this._tokens = newTokens; return this; } /** * Stringify the tokens * @param collapseWhitespace If `true` all whitespaces, except newlines, * will be removed */ toString(collapseWhitespace = false) { const tokens = collapseWhitespace ? this.collapseWhitespace() : this._tokens; return tokens.map((t) => t.value).join(''); } /** * Check if the comment at `pos` is a trailing comment or not * @param pos */ isTrailingComment(pos) { const tokens = this._tokens; const c = tokens[pos]; if (c.type !== token_1.TokenType.Comment) { throw new Error(`Expected a comment node, got ${c.type}`); } let i = pos - 1; while (tokens[i]) { const t = tokens[i]; i -= 1; if (t.type === token_1.TokenType.Whitespace) { continue; } else if (t.type === token_1.TokenType.Value) { return true; } else if (t.type === token_1.TokenType.Newline) { return false; } } return false; } /** * Check if we need no add a newline before adding a new key/value pair */ addNewlineBeforeNewValue() { if (!this._tokens.length) { return; } const last = this._tokens[this._tokens.length - 1]; if (last.type !== token_1.TokenType.Newline) { this.pushNewlineToken(); } } /** * Push a newline token */ pushNewlineToken() { this._tokens.push({ type: token_1.TokenType.Newline, value: '\n' }); return this; } /** * Get the key token with name `key` * @param key */ getKey(key) { return this._tokens.find((t) => t.type === token_1.TokenType.Key && t.value === key); } /** * Returns the index of the key token with name `key` * @param key */ getKeyIndex(key) { return this._tokens.findIndex((val) => { return val.type === token_1.TokenType.Key && val.value === key; }); } /** * Remove all unnecessary whitespace tokens */ collapseWhitespace() { const len = this._tokens.length - 1; return this._tokens.filter((t, i) => { if ((this._tokens[i - 1] && this._tokens[i - 1].type === token_1.TokenType.Newline && t.type === token_1.TokenType.Newline) || (i === len && t.type === token_1.TokenType.Newline)) { return false; } return t.type === token_1.TokenType.Newline || t.type !== token_1.TokenType.Whitespace; }); } /** * Returns the value token for key `key` * @param key */ getValueForKey(key) { const keyPos = this.getKeyIndex(key); const tokens = this._tokens; if (keyPos > -1) { for (let i = keyPos + 1; i < tokens.length; i++) { const t = tokens[i]; if (t.type === token_1.TokenType.Value) { return t; } } } return undefined; } } exports.KeyValue = KeyValue; //# sourceMappingURL=keyvalue.js.map