Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

index.js 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. 'use strict';
  2. const fs = require('fs');
  3. const path = require('path');
  4. const crypto = require('crypto');
  5. const assert = require('assert');
  6. const EventEmitter = require('events');
  7. const dotProp = require('dot-prop');
  8. const makeDir = require('make-dir');
  9. const pkgUp = require('pkg-up');
  10. const envPaths = require('env-paths');
  11. const writeFileAtomic = require('write-file-atomic');
  12. const obj = () => Object.create(null);
  13. // Prevent caching of this module so module.parent is always accurate
  14. delete require.cache[__filename];
  15. const parentDir = path.dirname((module.parent && module.parent.filename) || '.');
  16. class Conf {
  17. constructor(opts) {
  18. const pkgPath = pkgUp.sync(parentDir);
  19. opts = Object.assign({
  20. // Can't use `require` because of Webpack being annoying:
  21. // https://github.com/webpack/webpack/issues/196
  22. projectName: pkgPath && JSON.parse(fs.readFileSync(pkgPath, 'utf8')).name
  23. }, opts);
  24. if (!opts.projectName && !opts.cwd) {
  25. throw new Error('Project name could not be inferred. Please specify the `projectName` option.');
  26. }
  27. opts = Object.assign({
  28. configName: 'config'
  29. }, opts);
  30. if (!opts.cwd) {
  31. opts.cwd = envPaths(opts.projectName).config;
  32. }
  33. this.events = new EventEmitter();
  34. this.encryptionKey = opts.encryptionKey;
  35. this.path = path.resolve(opts.cwd, `${opts.configName}.json`);
  36. this.store = Object.assign(obj(), opts.defaults, this.store);
  37. }
  38. get(key, defaultValue) {
  39. return dotProp.get(this.store, key, defaultValue);
  40. }
  41. set(key, val) {
  42. if (typeof key !== 'string' && typeof key !== 'object') {
  43. throw new TypeError(`Expected \`key\` to be of type \`string\` or \`object\`, got ${typeof key}`);
  44. }
  45. const store = this.store;
  46. if (typeof key === 'object') {
  47. for (const k of Object.keys(key)) {
  48. dotProp.set(store, k, key[k]);
  49. }
  50. } else {
  51. dotProp.set(store, key, val);
  52. }
  53. this.store = store;
  54. }
  55. has(key) {
  56. return dotProp.has(this.store, key);
  57. }
  58. delete(key) {
  59. const store = this.store;
  60. dotProp.delete(store, key);
  61. this.store = store;
  62. }
  63. clear() {
  64. this.store = obj();
  65. }
  66. onDidChange(key, callback) {
  67. if (typeof key !== 'string') {
  68. throw new TypeError(`Expected \`key\` to be of type \`string\`, got ${typeof key}`);
  69. }
  70. if (typeof callback !== 'function') {
  71. throw new TypeError(`Expected \`callback\` to be of type \`function\`, got ${typeof callback}`);
  72. }
  73. let currentValue = this.get(key);
  74. const onChange = () => {
  75. const oldValue = currentValue;
  76. const newValue = this.get(key);
  77. try {
  78. assert.deepEqual(newValue, oldValue);
  79. } catch (err) {
  80. currentValue = newValue;
  81. callback.call(this, newValue, oldValue);
  82. }
  83. };
  84. this.events.on('change', onChange);
  85. return () => this.events.removeListener('change', onChange);
  86. }
  87. get size() {
  88. return Object.keys(this.store).length;
  89. }
  90. get store() {
  91. try {
  92. let data = fs.readFileSync(this.path, this.encryptionKey ? null : 'utf8');
  93. if (this.encryptionKey) {
  94. try {
  95. const decipher = crypto.createDecipher('aes-256-cbc', this.encryptionKey);
  96. data = Buffer.concat([decipher.update(data), decipher.final()]);
  97. } catch (err) {/* ignore */}
  98. }
  99. return Object.assign(obj(), JSON.parse(data));
  100. } catch (err) {
  101. if (err.code === 'ENOENT') {
  102. makeDir.sync(path.dirname(this.path));
  103. return obj();
  104. }
  105. if (err.name === 'SyntaxError') {
  106. return obj();
  107. }
  108. throw err;
  109. }
  110. }
  111. set store(val) {
  112. // Ensure the directory exists as it could have been deleted in the meantime
  113. makeDir.sync(path.dirname(this.path));
  114. let data = JSON.stringify(val, null, '\t');
  115. if (this.encryptionKey) {
  116. const cipher = crypto.createCipher('aes-256-cbc', this.encryptionKey);
  117. data = Buffer.concat([cipher.update(Buffer.from(data)), cipher.final()]);
  118. }
  119. writeFileAtomic.sync(this.path, data);
  120. this.events.emit('change');
  121. }
  122. // TODO: Use `Object.entries()` here at some point
  123. * [Symbol.iterator]() {
  124. const store = this.store;
  125. for (const key of Object.keys(store)) {
  126. yield [key, store[key]];
  127. }
  128. }
  129. }
  130. module.exports = Conf;