UNPKG

@expo/json-file

Version:

A module for reading, writing, and manipulating JSON files

440 lines (438 loc) • 14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, /** * 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]} */ "default", { enumerable: true, get: function() { return JsonFile; } }); function _json5() { const data = /*#__PURE__*/ _interop_require_default(require("json5")); _json5 = function() { return data; }; return data; } function _nodefs() { const data = /*#__PURE__*/ _interop_require_default(require("node:fs")); _nodefs = function() { return data; }; return data; } function _nodepath() { const data = /*#__PURE__*/ _interop_require_default(require("node:path")); _nodepath = function() { return data; }; return data; } const _JsonFileError = /*#__PURE__*/ _interop_require_wildcard(require("./JsonFileError")); const _writeAtomic = require("./writeAtomic"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const DEFAULT_OPTIONS = { badJsonDefault: undefined, jsonParseErrorDefault: undefined, cantReadFileDefault: undefined, ensureDir: false, mode: undefined, default: undefined, json5: false, space: 2, addNewLineAtEOF: true }; 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 }; } } function read(file, options) { let json; try { json = _nodefs().default.readFileSync(file, 'utf8'); } catch (error) { assertEmptyJsonString(json, file); const defaultValue = cantReadFileDefault(options); if (defaultValue === undefined) { throw new _JsonFileError.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 _nodefs().default.promises.readFile(file, 'utf8'); } catch (error) { assertEmptyJsonString(json, file); const defaultValue = cantReadFileDefault(options); if (defaultValue === undefined) { throw new _JsonFileError.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().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 { codeFrameColumns } = require('@babel/code-frame'); const codeFrame = codeFrameColumns(json, { start: location }); e.codeFrame = codeFrame; e.message += `\n${codeFrame}`; } throw new _JsonFileError.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.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.default(`No value at key path "${String(key)}" in JSON object from: ${file}`); } return defaultValue; } function write(file, object, options) { if (options?.ensureDir) { _nodefs().default.mkdirSync(_nodepath().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().default.stringify(object, null, space); } else { json = JSON.stringify(object, null, space); } } catch (e) { throw new _JsonFileError.default(`Couldn't JSON.stringify object for file: ${file}`, e); } const data = addNewLineAtEOF ? `${json}\n` : json; (0, _writeAtomic.writeFileAtomicSync)(file, data, { mode: options?.mode }); return object; } async function writeAsync(file, object, options) { if (options?.ensureDir) { await _nodefs().default.promises.mkdir(_nodepath().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().default.stringify(object, null, space); } else { json = JSON.stringify(object, null, space); } } catch (e) { throw new _JsonFileError.default(`Couldn't JSON.stringify object for file: ${file}`, e); } const data = addNewLineAtEOF ? `${json}\n` : json; await (0, _writeAtomic.writeFileAtomic)(file, data, { mode: options?.mode }); 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 (key != null && 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 (key != null && 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 && match[1] != null) { const index = parseInt(match[1], 10); const lines = sourceString.slice(0, index + 1).split('\n'); const lastLine = lines[lines.length - 1]; if (lastLine != null) { return { line: lines.length, column: lastLine.length }; } } return null; } function assertEmptyJsonString(json, file) { if (json?.trim() === '') { throw new _JsonFileError.EmptyJsonFileError(file); } } //# sourceMappingURL=JsonFile.js.map