All files / src/plugins SaveAsFilePlugin.ts

15.79% Statements 9/57
0% Branches 0/40
11.11% Functions 1/9
15.79% Lines 9/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 1071x 1x 1x 1x 1x                                 1x 5x     5x                             5x                                                                                                                                    
let fs = require("fs");
let path = require("path");
let mkdirp = require("mkdirp");
import * as _ from "lodash";
import { IArgdownRequest, IArgdownResponse, IHtmlRequest, IArgdownLogger, ArgdownPluginError } from "@argdown/core";
import { IAsyncArgdownPlugin, IAsyncRequestHandler } from "../IAsyncArgdownPlugin";
import { IFileRequest } from "../IFileRequest";
 
export interface IFileNameProvider {
  (settings: ISaveAsSettings, request: IArgdownRequest, response: IArgdownResponse): string;
}
export interface ISaveAsSettings {
  dataKey?: string;
  extension?: string;
  outputDir?: string;
  isRequestData?: boolean;
  fileName?: string | IFileNameProvider;
}
export interface ISaveAsRequest extends IFileRequest, IHtmlRequest {
  saveAs?: ISaveAsSettings;
}
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 {
    const r = <ISaveAsRequest>request;
    let settings = r.saveAs || {};
    settings = _.defaultsDeep({}, settings, this.defaults);
    return settings;
  }
  runAsync: IAsyncRequestHandler = async (request, response, logger) => {
    const saveAsRequest = <ISaveAsRequest>request;
    const settings = this.getSettings(request);
    if (!settings.dataKey) {
      throw new ArgdownPluginError(this.name, "No data key.");
    }
    let fileContent: string | undefined = !settings.isRequestData
      ? (<any>response)[settings.dataKey!]
      : (<any>request)[settings.dataKey!];
    if (fileContent === undefined) {
      throw new ArgdownPluginError(this.name, "No content to save.");
    }
    if (!_.isString(fileContent)) {
      throw new ArgdownPluginError(this.name, "Content is not a string.");
    }
    if (_.isEmpty(fileContent)) {
      throw new ArgdownPluginError(this.name, "Data field is empty string.");
    }
    const dataSettings = !settings.isRequestData && settings.dataKey ? (<any>request)[settings.dataKey] : null;
    let fileName = "default";
    let outputDir = settings.outputDir;
    if (saveAsRequest.outputDir) {
      fileName = this.getFileName(saveAsRequest.outputDir);
      outputDir = path.dirname(saveAsRequest.outputDir);
    } else if (dataSettings && dataSettings.outputDir) {
      fileName = this.getFileName(dataSettings.outputDir);
      outputDir = path.dirname(dataSettings.outputDir);
    } else if (_.isFunction(settings.fileName)) {
      fileName = settings.fileName.call(settings, request, response);
    } else if (_.isString(settings.fileName)) {
      fileName = settings.fileName;
    } else if (saveAsRequest.inputPath) {
      fileName = this.getFileName(saveAsRequest.inputPath);
    }
    if (dataSettings && dataSettings.outputDir) {
      outputDir = dataSettings.outputDir;
    }
    if (outputDir && settings.extension) {
      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, outputDir: string, fileName: string, extension: string, logger: IArgdownLogger) {
    let absoluteOutputDir = path.resolve(process.cwd(), outputDir);
    await new Promise((resolve, reject) => {
      mkdirp(absoluteOutputDir, function(err: Error) {
        if (err) {
          reject(err);
        }
        resolve();
      });
    });
    await new Promise((resolve, reject) => {
      fs.writeFile(absoluteOutputDir + "/" + fileName + extension, data, function(err: Error) {
        if (err) {
          reject(err);
        }
        logger.log("verbose", "Saved " + absoluteOutputDir + "/" + fileName + extension);
        resolve();
      });
    });
  }
}