output-file-sync
Version:
Synchronously write a file and create its ancestor directories if needed
121 lines (96 loc) • 3.9 kB
JavaScript
const dirname = require('path').dirname;
const fs = require('graceful-fs');
const inspect = require('util').inspect;
const isPlainObj = require('is-plain-obj');
const mkdirpSync = require('mkdirp').sync;
const PATH_ERROR = 'Expected a file path to write a file';
const ENCODING_ERROR = 'Expected a string to be a valid encoding, for exmaple \'utf8\' and \'ascii\'';
const ENCODING_OPTION_ERROR = ENCODING_ERROR.replace('a string', '`encoding` option');
const COMMON_MESSAGE = 'option to be a positive integer or a string of octal code to specify';
const nameMessageMap = new Map([
['mode', `Expected \`mode\` ${COMMON_MESSAGE} file and directory mode`],
['fileMode', `Expected \`fileMode\` ${COMMON_MESSAGE} file mode`],
['dirMode', `Expected \`dirMode\` ${COMMON_MESSAGE} directory mode`]
]);
module.exports = function outputFileSync(filePath, data, options) {
if (typeof filePath !== 'string') {
throw new TypeError(`${PATH_ERROR}, but got a non-string value ${inspect(filePath)}.`);
}
if (filePath.length === 0) {
throw new Error(`${PATH_ERROR}, but got '' (empty string).`);
}
if (typeof data !== 'string' && !Buffer.isBuffer(data) && !(data instanceof Uint8Array)) {
throw new TypeError(`Expected file content to be a string, Buffer or Uint8Array, but got ${
inspect(data)
} instead.`);
}
if (options !== null && options !== undefined) {
if (typeof options === 'string') {
if (options.length === 0) {
throw new Error(`${ENCODING_ERROR}, but got '' (empty string).`);
}
if (!Buffer.isEncoding(options)) {
throw new Error(`${ENCODING_ERROR}, but got ${inspect(options)} instead.`);
}
options = {encoding: options};
} else if (!isPlainObj(options)) {
throw new TypeError('Expected a string to specify file encoding or ' +
`an object to specify output-file-sync options, but got ${inspect(options)}.`);
}
if (options.encoding !== null && options.encoding !== undefined) {
if (typeof options.encoding !== 'string') {
throw new TypeError(`${ENCODING_OPTION_ERROR}, but got ${inspect(options.encoding)} instead.`);
}
if (options.encoding.length === 0) {
throw new Error(`${ENCODING_OPTION_ERROR}, but got '' (empty string).`);
}
if (!Buffer.isEncoding(options.encoding)) {
throw new Error(`${ENCODING_OPTION_ERROR}, but got ${inspect(options.encoding)} instead.`);
}
}
for (const pair of nameMessageMap) {
const optionName = pair[0];
const val = options[optionName];
const message = pair[1];
if (val !== undefined) {
if (typeof val === 'number') {
if (!isFinite(val)) {
throw new RangeError(`${message}, but got ${val}.`);
}
if (val > Number.MAX_SAFE_INTEGER) {
throw new RangeError(`${message}, but got a too large number.`);
}
if (val < 0) {
throw new RangeError(`${message}, but got a negative number ${val}.`);
}
if (!Number.isInteger(val)) {
throw new Error(`${message}, but got a non-integer number ${val}.`);
}
} else if (typeof val === 'string') {
if (val.length === 0) {
throw new Error(`${message}, but got '' (empty string).`);
}
const parsed = parseInt(val, 8);
if (isNaN(parsed)) {
throw new RangeError(`${message}, but got an invalid octal ${inspect(val)}.`);
}
if (parsed < 0) {
throw new RangeError(`${message}, but got a negative octal ${inspect(val)}.`);
}
} else {
throw new TypeError(`${message}, but got ${inspect(val)} instead.`);
}
}
}
} else {
options = {};
}
const createdDirPath = mkdirpSync(dirname(filePath), options.dirMode !== undefined ? Object.assign({fs}, options, {
mode: options.dirMode
}) : options);
fs.writeFileSync(filePath, data, options.fileMode !== undefined ? Object.assign({}, options, {
mode: options.fileMode
}) : options);
return createdDirPath;
};
;