UNPKG

mdast

Version:

Markdown processor powered by plugins

217 lines (181 loc) 4.93 kB
/** * @author Titus Wormer * @copyright 2015 Titus Wormer * @license MIT * @module mdast:cli:file-pipeline:configure * @version 2.2.2 * @fileoverview Configure a file. */ 'use strict'; /* eslint-env node */ /* * Dependencies. */ var fs = require('fs'); var path = require('path'); var debug = require('debug')('mdast:cli:file-pipeline:configure'); var npmPrefix = require('npm-prefix')(); var mdast = require('../../..'); /* * Methods. */ var exists = fs.existsSync; var join = path.join; var resolve = path.resolve; /* * Constants. */ var SEPERATOR = path.sep; var MODULES = 'node_modules'; var isWindows = process.platform === 'win32'; var isGlobal = process.argv[1].indexOf(npmPrefix) === 0; var globals = resolve(npmPrefix, isWindows ? '' : 'lib', MODULES); /* * Utilities. */ /** * Find root of a node module: parent directory of * `package.json`, or, the given directory if no * ancestral `package.json` is found. * * @example * findRoot('mdast/test'); // 'mdast' * * @todo Externalise. * @param {string} base - Path to directory. * @return {string} - Path to an ancestral project * directory. */ function findRoot(base) { var location = base; var parts = base.split(SEPERATOR); while (!exists(join(location, 'package.json')) && parts.length > 1) { parts.pop(); location = parts.join(SEPERATOR); } return parts.length ? location : base; } /** * Require a plugin. Checks, in this order: * * - `$package/$pathlike`; * - `$package/$pathlike.js`; * - `$package/node_modules/$pathlike`; * - `$package/node_modules/mdast-$pathlike`; * - `$cwd/node_modules/$pathlike`; * - `$cwd/node_modules/mdast-$pathlike`; * - `$pathlike`. * * Where `$package` is an ancestral package directory. * * When using a globally installed executable, the * following are also included: * * - `$modules/$pathlike`; * - `$modules/mdast-$pathlike`. * * Where `$modules` is the directory of globally installed * npm packages. * * @see https://docs.npmjs.com/files/folders#node-modules * * @example * var plugin = findPlugin('toc'); * * @todo Externalise. * @throws {Error} - Fails when `pathlike` cannot be * resolved. * @param {string} pathlike - Reference to plugin. * @param {string?} [cwd] - Relative working directory, * defaults to the current working directory. * @return {Object} - Result of `require`ing `plugin`. */ function findPlugin(pathlike, cwd) { var root = findRoot(cwd); var pluginlike = 'mdast-' + pathlike; var index = -1; var plugin = pathlike; var length; var paths = [ resolve(root, pathlike), resolve(root, pathlike + '.js'), resolve(root, MODULES, pathlike), resolve(root, MODULES, pluginlike), resolve(cwd, MODULES, pathlike), resolve(cwd, MODULES, pluginlike) ]; if (isGlobal) { paths.push( resolve(globals, pathlike), resolve(globals, pluginlike) ); } length = paths.length; while (++index < length) { if (exists(paths[index])) { plugin = paths[index]; break; } } debug('Using plug-in `%s` at `%s`', pathlike, plugin); return require(plugin); } /** * Collect configuration for a file based on the context. * * @example * var fileSet = new FileSet(cli); * var file = new File({ * 'directory': '~', * 'filename': 'example', * 'extension': 'md' * }); * * configure({'file': file, 'fileSet': fileSet}); * * @param {Object} context */ function configure(context, next) { var file = context.file; var cli = context.fileSet.cli; var config = cli.configuration; var processor = mdast(); var plugins; if (file.hasFailed()) { next(); return; } config.getConfiguration(file.filePath(), function (err, options) { debug('Setting output `%s`', options.output); debug('Using settings `%j`', options.settings); plugins = Object.keys(options.plugins); debug('Using plug-ins `%j`', plugins); /* * Use, with options. */ plugins.forEach(function (name) { var option = options.plugins[name]; var plugin; if (option === false) { debug('Ignoring plug-in `%s`', name); return; } try { plugin = findPlugin(name, cli.cwd); debug('Applying options `%j` to `%s`', option, name); processor.use(plugin, option, context.fileSet); } catch (err) { next(err) return false; } }); context.output = options.output; context.settings = options.settings; context.processor = processor; next(); }); } /* * Expose. */ module.exports = configure;