index.js 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. 'use strict';
  2. const path = require('path');
  3. const fs = require('graceful-fs');
  4. const writeFileAtomic = require('write-file-atomic');
  5. const sortKeys = require('sort-keys');
  6. const makeDir = require('make-dir');
  7. const pify = require('pify');
  8. const detectIndent = require('detect-indent');
  9. const init = (fn, fp, data, opts) => {
  10. if (!fp) {
  11. throw new TypeError('Expected a filepath');
  12. }
  13. if (data === undefined) {
  14. throw new TypeError('Expected data to stringify');
  15. }
  16. opts = Object.assign({
  17. indent: '\t',
  18. sortKeys: false
  19. }, opts);
  20. if (opts.sortKeys) {
  21. data = sortKeys(data, {
  22. deep: true,
  23. compare: typeof opts.sortKeys === 'function' && opts.sortKeys
  24. });
  25. }
  26. return fn(fp, data, opts);
  27. };
  28. const readFile = fp => pify(fs.readFile)(fp, 'utf8').catch(() => {});
  29. const main = (fp, data, opts) => {
  30. return (opts.detectIndent ? readFile(fp) : Promise.resolve())
  31. .then(str => {
  32. const indent = str ? detectIndent(str).indent : opts.indent;
  33. const json = JSON.stringify(data, opts.replacer, indent);
  34. return pify(writeFileAtomic)(fp, `${json}\n`, {mode: opts.mode});
  35. });
  36. };
  37. const mainSync = (fp, data, opts) => {
  38. let indent = opts.indent;
  39. if (opts.detectIndent) {
  40. try {
  41. const file = fs.readFileSync(fp, 'utf8');
  42. indent = detectIndent(file).indent;
  43. } catch (err) {
  44. if (err.code !== 'ENOENT') {
  45. throw err;
  46. }
  47. }
  48. }
  49. const json = JSON.stringify(data, opts.replacer, indent);
  50. return writeFileAtomic.sync(fp, `${json}\n`, {mode: opts.mode});
  51. };
  52. module.exports = (fp, data, opts) => {
  53. return makeDir(path.dirname(fp), {fs})
  54. .then(() => init(main, fp, data, opts));
  55. };
  56. module.exports.sync = (fp, data, opts) => {
  57. makeDir.sync(path.dirname(fp), {fs});
  58. init(mainSync, fp, data, opts);
  59. };