UNPKG

@argdown/node

Version:

Async Argdown application for node.js

157 lines (155 loc) 4.7 kB
let fs = require("fs"); let path = require("path"); let mkdirp = require("mkdirp"); import defaultsDeep from "lodash.defaultsdeep"; import isString from "lodash.isstring"; import isEmpty from "lodash.isempty"; import isFunction from "lodash.isfunction"; import { IArgdownRequest, IArgdownResponse, IArgdownLogger, ArgdownPluginError } from "@argdown/core"; import { IAsyncArgdownPlugin, IAsyncRequestHandler } from "../IAsyncArgdownPlugin"; export interface IFileNameProvider { ( settings: ISaveAsSettings, request: IArgdownRequest, response: IArgdownResponse ): string; } export interface ISaveAsSettings { dataKey?: string; extension?: string; outputDir?: string; isRequestData?: boolean; fileName?: string | IFileNameProvider; } declare module "@argdown/core" { interface IArgdownRequest { saveAs?: ISaveAsSettings; } interface IArgdownResponse { outputPath?: string; } } export class SaveAsFilePlugin implements IAsyncArgdownPlugin { name = "SaveAsFilePlugin"; defaults: ISaveAsSettings; constructor(config: ISaveAsSettings) { this.defaults = defaultsDeep({}, config, { dataKey: "test", extension: ".txt", outputDir: "./output" }); } // there can be several instances of this plugin in the same ArgdownApplication // Because of this, we can not add the instance default settings to the request object as in other plugins // Instead we have to add it each time the getSettings method is called to avoid keeping request specific state getSettings(request: IArgdownRequest): ISaveAsSettings { let settings = request.saveAs || {}; settings = defaultsDeep({}, settings, this.defaults); return settings; } runAsync: IAsyncRequestHandler = async (request, response, logger) => { const settings = this.getSettings(request); if (!settings.dataKey) { throw new ArgdownPluginError( this.name, "missing-dataKey-setting", "No data key." ); } let fileContent: string | Buffer | undefined = !settings.isRequestData ? (<any>response)[settings.dataKey!] : (<any>request)[settings.dataKey!]; if (fileContent === undefined) { throw new ArgdownPluginError( this.name, "missing-content", "No content to save." ); } if (!isString(fileContent) && !Buffer.isBuffer(fileContent)) { throw new ArgdownPluginError( this.name, "invalid-content", "Content is neither a string nor a buffer." ); } if (isEmpty(fileContent)) { throw new ArgdownPluginError( this.name, "missing-content", "Data field is empty string." ); } const dataSettings = !settings.isRequestData && settings.dataKey ? (<any>request)[settings.dataKey] : null; let fileName = "default"; let outputDir = settings.outputDir; if (request.outputPath) { outputDir = path.dirname(request.outputPath); } else if (dataSettings && dataSettings.outputDir) { outputDir = path.dirname(dataSettings.outputDir); } if (request.outputPath) { fileName = this.getFileName(request.outputPath); } else if (isFunction(settings.fileName)) { fileName = settings.fileName.call(this, settings, request, response); } else if (isString(settings.fileName)) { fileName = settings.fileName; } else if (request.inputPath) { fileName = this.getFileName(request.inputPath); } if (request.outputSuffix) { fileName = fileName + request.outputSuffix; } if (dataSettings && dataSettings.outputDir) { outputDir = dataSettings.outputDir; } if (outputDir && settings.extension) { response.outputPath = await this.saveAsFile( fileContent, outputDir, fileName, settings.extension, logger ); } }; getFileName(file: string): string { let extension = path.extname(file); return path.basename(file, extension); } async saveAsFile( data: string | Buffer, outputDir: string, fileName: string, extension: string, logger: IArgdownLogger ) { let absoluteOutputDir = path.resolve(process.cwd(), outputDir); const outputPath = path.resolve(absoluteOutputDir, fileName + extension); await mkdirp(absoluteOutputDir); await new Promise<void>((resolve, reject) => { fs.writeFile(outputPath, data, function(err: Error) { if (err) { reject(err); } logger.log( "verbose", "Saved " + absoluteOutputDir + "/" + fileName + extension ); resolve(); }); }); return outputPath; } }