"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const code_frame_1 = require("@babel/code-frame"); const json5_1 = __importDefault(require("json5")); const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const JsonFileError_1 = __importStar(require("./JsonFileError")); const writeAtomic_1 = require("./writeAtomic"); const DEFAULT_OPTIONS = { badJsonDefault: undefined, jsonParseErrorDefault: undefined, cantReadFileDefault: undefined, ensureDir: false, default: undefined, json5: false, space: 2, addNewLineAtEOF: true, }; /** * The JsonFile class represents the contents of json file. * * It's polymorphic on "JSONObject", which is a simple type representing * and object with string keys and either objects or primitive types as values. * @type {[type]} */ class JsonFile { file; options; static read = read; static readAsync = readAsync; static parseJsonString = parseJsonString; static write = write; static writeAsync = writeAsync; static get = getSync; static getAsync = getAsync; static set = setSync; static setAsync = setAsync; static merge = merge; static mergeAsync = mergeAsync; static deleteKey = deleteKey; static deleteKeyAsync = deleteKeyAsync; static deleteKeys = deleteKeys; static deleteKeysAsync = deleteKeysAsync; static rewrite = rewrite; static rewriteAsync = rewriteAsync; constructor(file, options = {}) { this.file = file; this.options = options; } read(options) { return read(this.file, this._getOptions(options)); } async readAsync(options) { return readAsync(this.file, this._getOptions(options)); } write(object, options) { return write(this.file, object, this._getOptions(options)); } async writeAsync(object, options) { return writeAsync(this.file, object, this._getOptions(options)); } parseJsonString(json, options) { return parseJsonString(json, options); } get(key, defaultValue, options) { return getSync(this.file, key, defaultValue, this._getOptions(options)); } async getAsync(key, defaultValue, options) { return getAsync(this.file, key, defaultValue, this._getOptions(options)); } set(key, value, options) { return setSync(this.file, key, value, this._getOptions(options)); } async setAsync(key, value, options) { return setAsync(this.file, key, value, this._getOptions(options)); } async merge(sources, options) { return merge(this.file, sources, this._getOptions(options)); } async mergeAsync(sources, options) { return mergeAsync(this.file, sources, this._getOptions(options)); } deleteKey(key, options) { return deleteKey(this.file, key, this._getOptions(options)); } async deleteKeyAsync(key, options) { return deleteKeyAsync(this.file, key, this._getOptions(options)); } deleteKeys(keys, options) { return deleteKeys(this.file, keys, this._getOptions(options)); } async deleteKeysAsync(keys, options) { return deleteKeysAsync(this.file, keys, this._getOptions(options)); } rewrite(options) { return rewrite(this.file, this._getOptions(options)); } async rewriteAsync(options) { return rewriteAsync(this.file, this._getOptions(options)); } _getOptions(options) { return { ...this.options, ...options, }; } } exports.default = JsonFile; function read(file, options) { let json; try { json = node_fs_1.default.readFileSync(file, 'utf8'); } catch (error) { assertEmptyJsonString(json, file); const defaultValue = cantReadFileDefault(options); if (defaultValue === undefined) { throw new JsonFileError_1.default(`Can't read JSON file: ${file}`, error, error.code, file); } else { return defaultValue; } } return parseJsonString(json, options, file); } async function readAsync(file, options) { let json; try { json = await node_fs_1.default.promises.readFile(file, 'utf8'); } catch (error) { assertEmptyJsonString(json, file); const defaultValue = cantReadFileDefault(options); if (defaultValue === undefined) { throw new JsonFileError_1.default(`Can't read JSON file: ${file}`, error, error.code); } else { return defaultValue; } } return parseJsonString(json, options); } function parseJsonString(json, options, fileName) { assertEmptyJsonString(json, fileName); try { if (_getOption(options, 'json5')) { return json5_1.default.parse(json); } else { return JSON.parse(json); } } catch (e) { const defaultValue = jsonParseErrorDefault(options); if (defaultValue === undefined) { const location = locationFromSyntaxError(e, json); if (location) { const codeFrame = (0, code_frame_1.codeFrameColumns)(json, { start: location }); e.codeFrame = codeFrame; e.message += `\n${codeFrame}`; } throw new JsonFileError_1.default(`Error parsing JSON: ${json}`, e, 'EJSONPARSE', fileName); } else { return defaultValue; } } } function getSync(file, key, defaultValue, options) { const object = read(file, options); if (key in object) { return object[key]; } if (defaultValue === undefined) { throw new JsonFileError_1.default(`No value at key path "${String(key)}" in JSON object from: ${file}`); } return defaultValue; } async function getAsync(file, key, defaultValue, options) { const object = await readAsync(file, options); if (key in object) { return object[key]; } if (defaultValue === undefined) { throw new JsonFileError_1.default(`No value at key path "${String(key)}" in JSON object from: ${file}`); } return defaultValue; } function write(file, object, options) { if (options?.ensureDir) { node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true }); } const space = _getOption(options, 'space'); const json5 = _getOption(options, 'json5'); const addNewLineAtEOF = _getOption(options, 'addNewLineAtEOF'); let json; try { if (json5) { json = json5_1.default.stringify(object, null, space); } else { json = JSON.stringify(object, null, space); } } catch (e) { throw new JsonFileError_1.default(`Couldn't JSON.stringify object for file: ${file}`, e); } const data = addNewLineAtEOF ? `${json}\n` : json; (0, writeAtomic_1.writeFileAtomicSync)(file, data); return object; } async function writeAsync(file, object, options) { if (options?.ensureDir) { await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(file), { recursive: true }); } const space = _getOption(options, 'space'); const json5 = _getOption(options, 'json5'); const addNewLineAtEOF = _getOption(options, 'addNewLineAtEOF'); let json; try { if (json5) { json = json5_1.default.stringify(object, null, space); } else { json = JSON.stringify(object, null, space); } } catch (e) { throw new JsonFileError_1.default(`Couldn't JSON.stringify object for file: ${file}`, e); } const data = addNewLineAtEOF ? `${json}\n` : json; await (0, writeAtomic_1.writeFileAtomic)(file, data); return object; } function setSync(file, key, value, options) { // TODO: Consider implementing some kind of locking mechanism, but // it's not critical for our use case, so we'll leave it out for now const object = read(file, options); return write(file, { ...object, [key]: value }, options); } async function setAsync(file, key, value, options) { // TODO: Consider implementing some kind of locking mechanism, but // it's not critical for our use case, so we'll leave it out for now const object = await readAsync(file, options); return writeAsync(file, { ...object, [key]: value }, options); } async function mergeAsync(file, sources, options) { const object = await readAsync(file, options); if (Array.isArray(sources)) { Object.assign(object, ...sources); } else { Object.assign(object, sources); } return writeAsync(file, object, options); } function merge(file, sources, options) { const object = read(file, options); if (Array.isArray(sources)) { Object.assign(object, ...sources); } else { Object.assign(object, sources); } return write(file, object, options); } async function deleteKeyAsync(file, key, options) { return deleteKeysAsync(file, [key], options); } function deleteKey(file, key, options) { return deleteKeys(file, [key], options); } async function deleteKeysAsync(file, keys, options) { const object = await readAsync(file, options); let didDelete = false; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (object.hasOwnProperty(key)) { delete object[key]; didDelete = true; } } if (didDelete) { return writeAsync(file, object, options); } return object; } function deleteKeys(file, keys, options) { const object = read(file, options); let didDelete = false; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (object.hasOwnProperty(key)) { delete object[key]; didDelete = true; } } if (didDelete) { return write(file, object, options); } return object; } async function rewriteAsync(file, options) { const object = await readAsync(file, options); return writeAsync(file, object, options); } function rewrite(file, options) { return write(file, read(file, options), options); } function jsonParseErrorDefault(options = {}) { if (options.jsonParseErrorDefault === undefined) { return options.default; } else { return options.jsonParseErrorDefault; } } function cantReadFileDefault(options = {}) { if (options.cantReadFileDefault === undefined) { return options.default; } else { return options.cantReadFileDefault; } } function _getOption(options, field) { if (options) { if (options[field] !== undefined) { return options[field]; } } return DEFAULT_OPTIONS[field]; } function locationFromSyntaxError(error, sourceString) { // JSON5 SyntaxError has lineNumber and columnNumber. if ('lineNumber' in error && 'columnNumber' in error) { return { line: error.lineNumber, column: error.columnNumber }; } // JSON SyntaxError only includes the index in the message. const match = /at position (\d+)/.exec(error.message); if (match) { const index = parseInt(match[1], 10); const lines = sourceString.slice(0, index + 1).split('\n'); return { line: lines.length, column: lines[lines.length - 1].length }; } return null; } function assertEmptyJsonString(json, file) { if (json?.trim() === '') { throw new JsonFileError_1.EmptyJsonFileError(file); } } //# sourceMappingURL=JsonFile.js.map