index.mjs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { existsSync, readFileSync, writeFileSync } from 'fs';
  2. import { resolve } from 'path';
  3. import { homedir } from 'os';
  4. import destr from 'destr';
  5. import flat from 'flat';
  6. import defu from 'defu';
  7. const RE_KEY_VAL = /^\s*([^=\s]+)\s*=\s*(.*)?\s*$/;
  8. const RE_LINES = /\n|\r|\r\n/;
  9. const defaults = {
  10. name: ".conf",
  11. dir: process.cwd(),
  12. flat: false
  13. };
  14. function withDefaults(options) {
  15. if (typeof options === "string") {
  16. options = { name: options };
  17. }
  18. return { ...defaults, ...options };
  19. }
  20. function parse(contents, options = {}) {
  21. const config = {};
  22. const lines = contents.split(RE_LINES);
  23. for (const line of lines) {
  24. const match = line.match(RE_KEY_VAL);
  25. if (!match) {
  26. continue;
  27. }
  28. const key = match[1];
  29. if (!key || key === "__proto__" || key === "constructor") {
  30. continue;
  31. }
  32. const val = destr(match[2].trim());
  33. if (key.endsWith("[]")) {
  34. const nkey = key.substr(0, key.length - 2);
  35. config[nkey] = (config[nkey] || []).concat(val);
  36. continue;
  37. }
  38. config[key] = val;
  39. }
  40. return options.flat ? config : flat.unflatten(config, { overwrite: true });
  41. }
  42. function parseFile(path, options) {
  43. if (!existsSync(path)) {
  44. return {};
  45. }
  46. return parse(readFileSync(path, "utf-8"), options);
  47. }
  48. function read(options) {
  49. options = withDefaults(options);
  50. return parseFile(resolve(options.dir, options.name), options);
  51. }
  52. function readUser(options) {
  53. options = withDefaults(options);
  54. options.dir = process.env.XDG_CONFIG_HOME || homedir();
  55. return read(options);
  56. }
  57. function serialize(config) {
  58. return Object.entries(flat.flatten(config)).map(([key, val]) => `${key}=${typeof val === "string" ? val : JSON.stringify(val)}`).join("\n");
  59. }
  60. function write(config, options) {
  61. options = withDefaults(options);
  62. writeFileSync(resolve(options.dir, options.name), serialize(config), {
  63. encoding: "utf-8"
  64. });
  65. }
  66. function writeUser(config, options) {
  67. options = withDefaults(options);
  68. options.dir = process.env.XDG_CONFIG_HOME || homedir();
  69. write(config, options);
  70. }
  71. function update(config, options) {
  72. options = withDefaults(options);
  73. if (!options.flat) {
  74. config = flat.unflatten(config, { overwrite: true });
  75. }
  76. const newConfig = defu(config, read(options));
  77. write(newConfig, options);
  78. return newConfig;
  79. }
  80. function updateUser(config, options) {
  81. options = withDefaults(options);
  82. options.dir = process.env.XDG_CONFIG_HOME || homedir();
  83. return update(config, options);
  84. }
  85. export { defaults, parse, parseFile, read, readUser, serialize, update, updateUser, write, writeUser };