UNPKG

mdast

Version:

Markdown processor powered by plugins

274 lines (235 loc) 5.23 kB
/** * @author Titus Wormer * @copyright 2015 Titus Wormer * @license MIT * @module mdast:cli:file-set * @version 2.2.2 * @fileoverview Collection of virtual files. */ 'use strict'; /* eslint-env node */ /* * Dependencies. */ var ware = require('ware'); var toVFile = require('to-vfile'); var filePipeline = require('./file-pipeline'); /** * Locked `VFile#move`. * * @this {VFile} * @memberof {VFile} * @return {VFile} - Context object. */ function locked() { return this; } /** * Utility invoked when a single file has completed it's * pipeline, invoking `fileSet.done` when all files are * done. * * @example * var fileSet = new FileSet(cli); * fileSet.done = function () {console.log('done!');} * * fileSet.add(new File()) * fileSet.add(new File()) * * one(fileSet); * one(fileSet); * // 'done!' * * @param {FileSet} fileSet - Set in which a file * completed. */ function one(fileSet) { fileSet.count++; if (fileSet.count >= fileSet.length && fileSet.done) { fileSet.done(); fileSet.done = null; } } /** * Construct a new file-set. * * @example * var fileSet = new FileSet(cli); * * @constructor * @class {FileSet} * @param {CLI|Object} cli - As returned by `lib/cli/cli`. */ function FileSet(cli) { var self = this; /** * Files in the set. * * @member {Array.<VFile>} contents */ self.contents = []; /** * Number of files in the set. * * @member {number} length */ self.length = 0; /** * Number of processed files. * * @member {number} count */ self.count = 0; /** * File-paths to the original location of files in * the set. * * @member {Array.<string>} soucePaths */ self.sourcePaths = []; /** * CLI executing the set. * * @member {CLI} cli */ self.cli = cli; /** * Pipeline to run when all files in the file-set * are processed. * * @member {Ware} pipeline */ self.pipeline = ware(); } /** * Create an array representation of `fileSet`. * * @example * var fileSet = new FileSet(cli); * fileSet.valueOf() // [] * fileSet.toJSON() // [] * * @this {FileSet} * @return {Array.<File>} - Value at the `contents` property * in context. */ function valueOf() { return this.contents; } /** * Attach middleware to the pipeline on `fileSet`. * * A plug-in (function) can have an `pluginId` property, * which is used to ignore duplicate attachment. * * This pipeline will later be run when when all attached * files are after the transforming stage. * * @example * var fileSet = new FileSet(cli); * fileSet.use(console.log); * * @this {FileSet} * @param {Function} plugin - Middleware. * @return {FileSet} - `this`; context object. */ function use(plugin) { var self = this; var pipeline = self.pipeline; var duplicate = false; if (plugin && plugin.pluginId) { duplicate = pipeline.fns.some(function (fn) { return fn.pluginId === plugin.pluginId; }); } if (!duplicate && pipeline.fns.indexOf(plugin) !== -1) { duplicate = true; } if (!duplicate) { pipeline.use(plugin); } return this; } /** * Add a file to be processed. * * Ignores duplicate files (based on the `filePath` at time * of addition). * * Only runs `file-pipeline` on files which have not * `failed` before addition. * * @example * var fileSet = new FileSet(cli); * var fileA = new File({ * 'directory': '~', * 'filename': 'example', * 'extension': 'md' * }); * var fileB = new File({ * 'directory': '~', * 'filename': 'example', * 'extension': 'md' * }); * * fileSet.add(fileA); * fileSet.length; // 1 * * fileSet.add(fileB); * fileSet.length; // 1 * * @this {FileSet} * @param {File|string} file - Virtual file, or path. * @return {FileSet} - `this`; context object. */ function add(file) { var self = this; var paths = self.sourcePaths; var sourcePath; var context; if (typeof file === 'string') { file = toVFile(file); } sourcePath = file.filePath(); if (paths.indexOf(sourcePath) !== -1) { return self; } paths.push(sourcePath); file.sourcePath = sourcePath; if (!file.namespace('mdast:cli').given) { file.move = locked; } self.length++; self.valueOf().push(file); context = { 'file': file, 'fileSet': self }; /* * Force an asynchronous operation. * This ensures that files which fall through * the file pipeline quicker that expected (e.g., * when already fatally failed) still queue up * correctly. */ setImmediate(function () { filePipeline.run(context, function (err) { if (err) { file.fail(err); } one(self); }); }); return self; } /* * Expose methods. */ FileSet.prototype.valueOf = valueOf; FileSet.prototype.toJSON = valueOf; FileSet.prototype.use = use; FileSet.prototype.add = add; /* * Expose. */ module.exports = FileSet;