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