index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. 'use strict';
  2. const dirname = require('path').dirname;
  3. const fs = require('graceful-fs');
  4. const inspect = require('util').inspect;
  5. const isPlainObj = require('is-plain-obj');
  6. const mkdirpSync = require('mkdirp').sync;
  7. const PATH_ERROR = 'Expected a file path to write a file';
  8. const ENCODING_ERROR = 'Expected a string to be a valid encoding, for exmaple \'utf8\' and \'ascii\'';
  9. const ENCODING_OPTION_ERROR = ENCODING_ERROR.replace('a string', '`encoding` option');
  10. const COMMON_MESSAGE = 'option to be a positive integer or a string of octal code to specify';
  11. const nameMessageMap = new Map([
  12. ['mode', `Expected \`mode\` ${COMMON_MESSAGE} file and directory mode`],
  13. ['fileMode', `Expected \`fileMode\` ${COMMON_MESSAGE} file mode`],
  14. ['dirMode', `Expected \`dirMode\` ${COMMON_MESSAGE} directory mode`]
  15. ]);
  16. module.exports = function outputFileSync(filePath, data, options) {
  17. if (typeof filePath !== 'string') {
  18. throw new TypeError(`${PATH_ERROR}, but got a non-string value ${inspect(filePath)}.`);
  19. }
  20. if (filePath.length === 0) {
  21. throw new Error(`${PATH_ERROR}, but got '' (empty string).`);
  22. }
  23. if (typeof data !== 'string' && !Buffer.isBuffer(data) && !(data instanceof Uint8Array)) {
  24. throw new TypeError(`Expected file content to be a string, Buffer or Uint8Array, but got ${
  25. inspect(data)
  26. } instead.`);
  27. }
  28. if (options !== null && options !== undefined) {
  29. if (typeof options === 'string') {
  30. if (options.length === 0) {
  31. throw new Error(`${ENCODING_ERROR}, but got '' (empty string).`);
  32. }
  33. if (!Buffer.isEncoding(options)) {
  34. throw new Error(`${ENCODING_ERROR}, but got ${inspect(options)} instead.`);
  35. }
  36. options = {encoding: options};
  37. } else if (!isPlainObj(options)) {
  38. throw new TypeError('Expected a string to specify file encoding or ' +
  39. `an object to specify output-file-sync options, but got ${inspect(options)}.`);
  40. }
  41. if (options.encoding !== null && options.encoding !== undefined) {
  42. if (typeof options.encoding !== 'string') {
  43. throw new TypeError(`${ENCODING_OPTION_ERROR}, but got ${inspect(options.encoding)} instead.`);
  44. }
  45. if (options.encoding.length === 0) {
  46. throw new Error(`${ENCODING_OPTION_ERROR}, but got '' (empty string).`);
  47. }
  48. if (!Buffer.isEncoding(options.encoding)) {
  49. throw new Error(`${ENCODING_OPTION_ERROR}, but got ${inspect(options.encoding)} instead.`);
  50. }
  51. }
  52. for (const pair of nameMessageMap) {
  53. const optionName = pair[0];
  54. const val = options[optionName];
  55. const message = pair[1];
  56. if (val !== undefined) {
  57. if (typeof val === 'number') {
  58. if (!isFinite(val)) {
  59. throw new RangeError(`${message}, but got ${val}.`);
  60. }
  61. if (val > Number.MAX_SAFE_INTEGER) {
  62. throw new RangeError(`${message}, but got a too large number.`);
  63. }
  64. if (val < 0) {
  65. throw new RangeError(`${message}, but got a negative number ${val}.`);
  66. }
  67. if (!Number.isInteger(val)) {
  68. throw new Error(`${message}, but got a non-integer number ${val}.`);
  69. }
  70. } else if (typeof val === 'string') {
  71. if (val.length === 0) {
  72. throw new Error(`${message}, but got '' (empty string).`);
  73. }
  74. const parsed = parseInt(val, 8);
  75. if (isNaN(parsed)) {
  76. throw new RangeError(`${message}, but got an invalid octal ${inspect(val)}.`);
  77. }
  78. if (parsed < 0) {
  79. throw new RangeError(`${message}, but got a negative octal ${inspect(val)}.`);
  80. }
  81. } else {
  82. throw new TypeError(`${message}, but got ${inspect(val)} instead.`);
  83. }
  84. }
  85. }
  86. } else {
  87. options = {};
  88. }
  89. const createdDirPath = mkdirpSync(dirname(filePath), options.dirMode !== undefined ? Object.assign({fs}, options, {
  90. mode: options.dirMode
  91. }) : options);
  92. fs.writeFileSync(filePath, data, options.fileMode !== undefined ? Object.assign({}, options, {
  93. mode: options.fileMode
  94. }) : options);
  95. return createdDirPath;
  96. };