index.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. 'use strict';
  2. const path = require('path');
  3. const os = require('os');
  4. const fs = require('graceful-fs');
  5. const makeDir = require('make-dir');
  6. const xdgBasedir = require('xdg-basedir');
  7. const writeFileAtomic = require('write-file-atomic');
  8. const dotProp = require('dot-prop');
  9. const uniqueString = require('unique-string');
  10. const configDir = xdgBasedir.config || path.join(os.tmpdir(), uniqueString());
  11. const permissionError = 'You don\'t have access to this file.';
  12. const makeDirOptions = {mode: 0o0700};
  13. const writeFileOptions = {mode: 0o0600};
  14. class Configstore {
  15. constructor(id, defaults, opts) {
  16. opts = opts || {};
  17. const pathPrefix = opts.globalConfigPath ?
  18. path.join(id, 'config.json') :
  19. path.join('configstore', `${id}.json`);
  20. this.path = path.join(configDir, pathPrefix);
  21. this.all = Object.assign({}, defaults, this.all);
  22. }
  23. get all() {
  24. try {
  25. return JSON.parse(fs.readFileSync(this.path, 'utf8'));
  26. } catch (err) {
  27. // Create dir if it doesn't exist
  28. if (err.code === 'ENOENT') {
  29. makeDir.sync(path.dirname(this.path), makeDirOptions);
  30. return {};
  31. }
  32. // Improve the message of permission errors
  33. if (err.code === 'EACCES') {
  34. err.message = `${err.message}\n${permissionError}\n`;
  35. }
  36. // Empty the file if it encounters invalid JSON
  37. if (err.name === 'SyntaxError') {
  38. writeFileAtomic.sync(this.path, '', writeFileOptions);
  39. return {};
  40. }
  41. throw err;
  42. }
  43. }
  44. set all(val) {
  45. try {
  46. // Make sure the folder exists as it could have been deleted in the meantime
  47. makeDir.sync(path.dirname(this.path), makeDirOptions);
  48. writeFileAtomic.sync(this.path, JSON.stringify(val, null, '\t'), writeFileOptions);
  49. } catch (err) {
  50. // Improve the message of permission errors
  51. if (err.code === 'EACCES') {
  52. err.message = `${err.message}\n${permissionError}\n`;
  53. }
  54. throw err;
  55. }
  56. }
  57. get size() {
  58. return Object.keys(this.all || {}).length;
  59. }
  60. get(key) {
  61. return dotProp.get(this.all, key);
  62. }
  63. set(key, val) {
  64. const config = this.all;
  65. if (arguments.length === 1) {
  66. for (const k of Object.keys(key)) {
  67. dotProp.set(config, k, key[k]);
  68. }
  69. } else {
  70. dotProp.set(config, key, val);
  71. }
  72. this.all = config;
  73. }
  74. has(key) {
  75. return dotProp.has(this.all, key);
  76. }
  77. delete(key) {
  78. const config = this.all;
  79. dotProp.delete(config, key);
  80. this.all = config;
  81. }
  82. clear() {
  83. this.all = {};
  84. }
  85. }
  86. module.exports = Configstore;