index.cjs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. 'use strict';
  2. const node_fs = require('node:fs');
  3. const node_path = require('node:path');
  4. const node_os = require('node:os');
  5. const destr = require('destr');
  6. const defu = require('defu');
  7. function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
  8. const destr__default = /*#__PURE__*/_interopDefaultCompat(destr);
  9. function isBuffer (obj) {
  10. return obj &&
  11. obj.constructor &&
  12. (typeof obj.constructor.isBuffer === 'function') &&
  13. obj.constructor.isBuffer(obj)
  14. }
  15. function keyIdentity (key) {
  16. return key
  17. }
  18. function flatten (target, opts) {
  19. opts = opts || {};
  20. const delimiter = opts.delimiter || '.';
  21. const maxDepth = opts.maxDepth;
  22. const transformKey = opts.transformKey || keyIdentity;
  23. const output = {};
  24. function step (object, prev, currentDepth) {
  25. currentDepth = currentDepth || 1;
  26. Object.keys(object).forEach(function (key) {
  27. const value = object[key];
  28. const isarray = opts.safe && Array.isArray(value);
  29. const type = Object.prototype.toString.call(value);
  30. const isbuffer = isBuffer(value);
  31. const isobject = (
  32. type === '[object Object]' ||
  33. type === '[object Array]'
  34. );
  35. const newKey = prev
  36. ? prev + delimiter + transformKey(key)
  37. : transformKey(key);
  38. if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
  39. (!opts.maxDepth || currentDepth < maxDepth)) {
  40. return step(value, newKey, currentDepth + 1)
  41. }
  42. output[newKey] = value;
  43. });
  44. }
  45. step(target);
  46. return output
  47. }
  48. function unflatten (target, opts) {
  49. opts = opts || {};
  50. const delimiter = opts.delimiter || '.';
  51. const overwrite = opts.overwrite || false;
  52. const transformKey = opts.transformKey || keyIdentity;
  53. const result = {};
  54. const isbuffer = isBuffer(target);
  55. if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
  56. return target
  57. }
  58. // safely ensure that the key is
  59. // an integer.
  60. function getkey (key) {
  61. const parsedKey = Number(key);
  62. return (
  63. isNaN(parsedKey) ||
  64. key.indexOf('.') !== -1 ||
  65. opts.object
  66. )
  67. ? key
  68. : parsedKey
  69. }
  70. function addKeys (keyPrefix, recipient, target) {
  71. return Object.keys(target).reduce(function (result, key) {
  72. result[keyPrefix + delimiter + key] = target[key];
  73. return result
  74. }, recipient)
  75. }
  76. function isEmpty (val) {
  77. const type = Object.prototype.toString.call(val);
  78. const isArray = type === '[object Array]';
  79. const isObject = type === '[object Object]';
  80. if (!val) {
  81. return true
  82. } else if (isArray) {
  83. return !val.length
  84. } else if (isObject) {
  85. return !Object.keys(val).length
  86. }
  87. }
  88. target = Object.keys(target).reduce(function (result, key) {
  89. const type = Object.prototype.toString.call(target[key]);
  90. const isObject = (type === '[object Object]' || type === '[object Array]');
  91. if (!isObject || isEmpty(target[key])) {
  92. result[key] = target[key];
  93. return result
  94. } else {
  95. return addKeys(
  96. key,
  97. result,
  98. flatten(target[key], opts)
  99. )
  100. }
  101. }, {});
  102. Object.keys(target).forEach(function (key) {
  103. const split = key.split(delimiter).map(transformKey);
  104. let key1 = getkey(split.shift());
  105. let key2 = getkey(split[0]);
  106. let recipient = result;
  107. while (key2 !== undefined) {
  108. if (key1 === '__proto__') {
  109. return
  110. }
  111. const type = Object.prototype.toString.call(recipient[key1]);
  112. const isobject = (
  113. type === '[object Object]' ||
  114. type === '[object Array]'
  115. );
  116. // do not write over falsey, non-undefined values if overwrite is false
  117. if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
  118. return
  119. }
  120. if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
  121. recipient[key1] = (
  122. typeof key2 === 'number' &&
  123. !opts.object
  124. ? []
  125. : {}
  126. );
  127. }
  128. recipient = recipient[key1];
  129. if (split.length > 0) {
  130. key1 = getkey(split.shift());
  131. key2 = getkey(split[0]);
  132. }
  133. }
  134. // unflatten again for 'messy objects'
  135. recipient[key1] = unflatten(target[key], opts);
  136. });
  137. return result
  138. }
  139. const RE_KEY_VAL = /^\s*([^\s=]+)\s*=\s*(.*)?\s*$/;
  140. const RE_LINES = /\n|\r|\r\n/;
  141. const defaults = {
  142. name: ".conf",
  143. dir: process.cwd(),
  144. flat: false
  145. };
  146. function withDefaults(options) {
  147. if (typeof options === "string") {
  148. options = { name: options };
  149. }
  150. return { ...defaults, ...options };
  151. }
  152. function parse(contents, options = {}) {
  153. const config = {};
  154. const lines = contents.split(RE_LINES);
  155. for (const line of lines) {
  156. const match = line.match(RE_KEY_VAL);
  157. if (!match) {
  158. continue;
  159. }
  160. const key = match[1];
  161. if (!key || key === "__proto__" || key === "constructor") {
  162. continue;
  163. }
  164. const value = destr__default(
  165. (match[2] || "").trim()
  166. /* val */
  167. );
  168. if (key.endsWith("[]")) {
  169. const nkey = key.slice(0, Math.max(0, key.length - 2));
  170. config[nkey] = (config[nkey] || []).concat(value);
  171. continue;
  172. }
  173. config[key] = value;
  174. }
  175. return options.flat ? config : unflatten(config, { overwrite: true });
  176. }
  177. function parseFile(path, options) {
  178. if (!node_fs.existsSync(path)) {
  179. return {};
  180. }
  181. return parse(node_fs.readFileSync(path, "utf8"), options);
  182. }
  183. function read(options) {
  184. options = withDefaults(options);
  185. return parseFile(node_path.resolve(options.dir, options.name), options);
  186. }
  187. function readUser(options) {
  188. options = withDefaults(options);
  189. options.dir = process.env.XDG_CONFIG_HOME || node_os.homedir();
  190. return read(options);
  191. }
  192. function serialize(config) {
  193. return Object.entries(flatten(config)).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
  194. }
  195. function write(config, options) {
  196. options = withDefaults(options);
  197. node_fs.writeFileSync(node_path.resolve(options.dir, options.name), serialize(config), {
  198. encoding: "utf8"
  199. });
  200. }
  201. function writeUser(config, options) {
  202. options = withDefaults(options);
  203. options.dir = process.env.XDG_CONFIG_HOME || node_os.homedir();
  204. write(config, options);
  205. }
  206. function update(config, options) {
  207. options = withDefaults(options);
  208. if (!options.flat) {
  209. config = unflatten(config, { overwrite: true });
  210. }
  211. const newConfig = defu.defu(config, read(options));
  212. write(newConfig, options);
  213. return newConfig;
  214. }
  215. function updateUser(config, options) {
  216. options = withDefaults(options);
  217. options.dir = process.env.XDG_CONFIG_HOME || node_os.homedir();
  218. return update(config, options);
  219. }
  220. exports.defaults = defaults;
  221. exports.parse = parse;
  222. exports.parseFile = parseFile;
  223. exports.read = read;
  224. exports.readUser = readUser;
  225. exports.serialize = serialize;
  226. exports.update = update;
  227. exports.updateUser = updateUser;
  228. exports.write = write;
  229. exports.writeUser = writeUser;