UNPKG

makef

Version:

Utilities to create files inside specified directory or copy files

180 lines (168 loc) 7.57 kB
const path = require('path'); const fse = require('fs-extra'); exports.fse = fse; function noop() {} const loggerStub = { error: noop, log: noop }; function getLogger(options) { const { logger } = options; // eslint-disable-next-line no-nested-ternary return logger === false ? loggerStub : (! logger || logger === true ? console : logger); } /** * Create specified files. * * @param {object | string} fileSet * Name of empty file or set of files that should be created. * If a string is passed it is treated as name of the only empty file that should be created. * When an object is passed its fields are used as file names and field values determine contents * for corresponding files. * When a function is specified as field value it will be called to get content for the file. * The following parameters are passed into a function: * - `fileName: string` - name of file that should be created as specified in a field name of `fileSet`. * - `env: object` - additional contextual data. * - `env.data: any` - value of `settings.data` (see below). * - `env.dir: string` - directory where file should be created; it is value of `settings.dir` (see below) * or empty string. * - `env.dirPath: string` - absolute path to directory where file should be created. * - `env.filePath: string` - absolute path to file that should be created. * - `env.logger: object` - an object having methods `log` and `error` that should be used for logging. * * A value returned from the function will be processed to form file content. * Content values (specified as a field value or returned from a function) are treated in the following way: * - `false`, `null` or `undefined` - means that the corresponding file should not be created. * - `true` - means that the corresponding file should be created with empty content. * - an object or an array - is converted to JSON that is used as file content. * - any other value - is converted to string that is used as file content. * @param {object} [settings] * Operation settings. * @param {object} [settings.context] * Object that should be used as `this` when calling a function to get a file content. * @param {*} [settings.data] * Any data that should be passed to a function that is used to get a file content. * @param {string} [settings.dir] * Directory that will be used to resolve (form) absolute path for relative file names. * I.e. it is base/target directory for created files. * By default the current working directory is used. * @param {boolean | object} [settings.logger=console] * An object having methods `log` (or `info`) and `error` that should be used for logging. * Pass `false` to disable logging. * Skipping this option or passing any other falsy value (i.e. `null`, `undefined`, `0`, etc) * or `true` will cause to use `console` as logger. * @return {object.<string, (string | Error)> | undefined} * Object representing creation result, or `undefined` when `fileSet` is not specified (no file is processed). * Object fields are file names as specified in `fileSet`, * a field value is absolute path of the corresponding file, * or error object when the file is not created by some reason. */ exports.createFile = function createFile(fileSet, settings) { const options = settings || {}; const { context, data } = options; const dir = options.dir || ''; const dirPath = path.resolve(dir); const logger = getLogger(options); const log = (logger.log || logger.info).bind(logger); const fileType = typeof fileSet; let result; if (fileSet && (fileType === 'string' || fileType === 'object')) { if (fileType === 'string') { // eslint-disable-next-line no-param-reassign fileSet = {[fileSet]: ''}; } result = {}; for (const fileName in fileSet) { const filePath = path.resolve(dir, fileName); let content = fileSet[fileName]; if (typeof content === 'function') { log(`get content for file '${fileName}' from function`); content = content.call( context, fileName, { filePath, dir, dirPath, data, logger } ); } // eslint-disable-next-line eqeqeq, no-eq-null if (content === false || content == null) { log(`skip flag '${content}' is set for file '${fileName}' so it is not created`); } else { if (content === true) { content = ''; } else if (typeof content === 'object') { content = JSON.stringify(content, null, 4); } else { content = String(content); } try { fse.outputFileSync(filePath, content); result[fileName] = filePath; log(`file '${filePath}' is created`); } catch (e) { result[fileName] = e; logger.error(`cannot create file '${filePath}'; error details -\n${e}`); } } } } else { log('no files are specified as value of "fileSet" parameter'); } return result; }; /** * Copy specified file. * * @param {string} sourceFile * Name/path of file that should be copied. * @param {string} [destFile] * Name/path of destination file that should be created. * If the file is not set a partially applied function will be returned * that can be used to copy source file several times by passing a destination file and settings as arguments. * @param {object} [settings] * Operation settings. * @param {string} [settings.dir] * Directory where destination file should be created. * By default the current working directory is used. * @param {boolean | object} [settings.logger=console] * An object having methods `log` (or `info`) and `error` that should be used for logging. * See the corresponding setting of {@link createFile} for details. * @param {string} [settings.sourceDir] * Directory where source file is located. */ exports.copyFile = function copyFile(sourceFile, destFile, settings) { // eslint-disable-line consistent-return if (sourceFile) { if (destFile) { const options = settings || {}; const logger = getLogger(options); const sourcePath = path.resolve(options.sourceDir || '', sourceFile); const destPath = path.resolve(options.dir || '', destFile); try { fse.copySync(sourcePath, destPath, {overwrite: true}); logger.log(`file '${sourcePath}' is copied to '${destPath}'`); } catch (e) { logger.error(`cannot copy file '${sourcePath}' to '${destPath}'; error details -\n${e}`); } } else { return function partialCopyFile(fileName, copySettings) { return copyFile(sourceFile, fileName, copySettings); }; } } };