UNPKG

pandemics

Version:

Simplified academic writing.

219 lines (200 loc) 7.67 kB
const fs = require('fs'); const path = require('path'); const fsTools = require('./fs-tools.js'); function splitFormat (format = []) { const prefix = format.split('.').slice(0, -1).join('.'); const ext = format.split('.').pop(); return { prefix, ext }; } module.exports = class Instructions { /* **************************************************************************** * ***************************************************************************** */ constructor (recipe, format) { this.recipe = recipe; /* ====================================================================== */ // Default recipe /* ====================================================================== */ if (this.recipe.isDefault) { this.format = format; this._record = this._loadRecord(); /* ====================================================================== */ // Provided recipe /* ====================================================================== */ } else { // see if we can find an unambiguous record const uniqueRecordFormat = this._uniqueRecordFormat(format); if (uniqueRecordFormat) { this.format = uniqueRecordFormat; this._record = this._loadRecord(this.recipe.fullPath); // otherwise, see if we can find an unambiguous template } else { const uniqueTemplate = this._uniqueTemplate(format); console.log(uniqueTemplate) if (uniqueTemplate) { this.format = uniqueTemplate.format; this._record = this._loadRecord(); this._record.template = uniqueTemplate.template; } // if still nothing, we failed if (!this._record) { throw new Error(`Could not find recipe.${format}.json and could not guess template.`); } } } } /* **************************************************************************** * return content of the json instruction file for the requested format ***************************************************************************** */ _loadRecord (recipePath) { if (!recipePath) { recipePath = path.join( __dirname, '..', '_defaults' ); } // path to the record file const recordPath = path.join( recipePath, `recipe.${this.format}.json` ); // exit if file does not exist (eg for default) if (!fs.existsSync(recordPath)) { return {}; } // parse and return json try { return JSON.parse(fs.readFileSync(recordPath, 'utf8')); } catch (err) { throw new Error(`Invalid recipe. Check that ${recordPath} contains correctly defined JSON.`); } } /* **************************************************************************** * list all recipe.*.json file in the recipe folder ***************************************************************************** */ _listRecordFormats () { const recipeRgxp = /^recipe\.(.*)\.json$/; return fsTools.listFiles( this.recipe.fullPath, { type: 'file', filter: fileName => recipeRgxp.test(fileName) } ).map((fileName) => fileName.match(recipeRgxp)[1]); // keep only format } /* **************************************************************************** * Return the format for which there is a unique record, using the format * selector if needed. If there is no record for the requested format, or not * exactly one for undefined format, this will return undefined ***************************************************************************** */ _uniqueRecordFormat (format) { // list availble records let recordFormats = this._listRecordFormats(); // filter records ending with requested format if (format) { recordFormats = recordFormats.filter(fmt => fmt.endsWith(format)); } // if nested formats, keep only shortest let formatLengths = recordFormats.map(fmt => fmt.split('.').length); recordFormats = recordFormats.filter(fmt => fmt.split('.').length == Math.min(...formatLengths)) // if unique record left, return it if (recordFormats.length === 1) { return recordFormats[0]; } } /* ************************************************************************** * list all potential templates in the recipe folder *************************************************************************** */ _listTemplates(format) { // map target formats to their possible templates. const formatMap = [ { ext: ['pdf','tex','latex'], templateExt: ['.tex', '.latex', '.xelatex'] }, { ext: ['docx'], templateExt: ['.doc', '.docx'] }, { ext: ['html'], templateExt: ['.html', '.html5'] } ]; /* If a format is provided, list only templates for it * ---------------------------------------------------------------------- */ if (format) { // get requested format and potential prefix const {prefix, ext} = splitFormat(format); // list potential template extension for the requested format and // recombine with the prefix const templateFormats = formatMap .find(fmtMap => fmtMap.ext.includes(ext)) .templateExt .map(e => [prefix, e].join('')); // list all files matching the prefixed template format const templates = fsTools.listFiles( this.recipe.fullPath, { type: 'file', filter: fileName => templateFormats.some(fmt => fileName.endsWith(fmt)) } ); // return both format and found templates return [{format, templates}]; /* If no specific format requested, return all potential templates and their * associate formats * ---------------------------------------------------------------------- */ } else { return formatMap.map((fmtMap) => this._listTemplates(fmtMap.ext[0])[0]); } } /* *************************************************************************** * Return a pair of format and template, if this pair can be identified * unambigusouly in the recipe folder, eventually restricting the search to the * requested format. If suche pair does not exist (no or too many templaetes) * this function will return undefined *************************************************************************** */ _uniqueTemplate (format) { let candidates = this._listTemplates(format) .filter(c => c.templates.length === 1) if (candidates.length === 1) { return { format: candidates[0].format, template: candidates[0].templates[0] } } } /* *************************************************************************** * Getters, nothing to see here *************************************************************************** */ get extension () { return splitFormat(this.format).ext; } get prefix () { return splitFormat(this.format).prefix; } get template () { if (this._record.template) { return path.join( this.recipe.fullPath, this._record.template ); }; } get options () { if (typeof this._record.options === 'string') { return [this._record.options]; } else if (Array.isArray(this._record.options)){ return this._record.options; } else if (this._record.options === undefined){ return; } else { throw new Error(`Invalid recipe instructions options.`); } } get filters () { return (this._record || {}).filters; } get preprocessing () { return (this._record || {}).preprocessing; } toString () { return `format: ${this.prefix} + ${this.extension}`; } };