| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- 'use strict'
- /* Dependencies. */
- var extend = require('extend')
- var bail = require('bail')
- var vfile = require('vfile')
- var trough = require('trough')
- var string = require('x-is-string')
- var plain = require('is-plain-obj')
- /* Expose a frozen processor. */
- module.exports = unified().freeze()
- var slice = [].slice
- var own = {}.hasOwnProperty
- /* Process pipeline. */
- var pipeline = trough()
- .use(pipelineParse)
- .use(pipelineRun)
- .use(pipelineStringify)
- function pipelineParse(p, ctx) {
- ctx.tree = p.parse(ctx.file)
- }
- function pipelineRun(p, ctx, next) {
- p.run(ctx.tree, ctx.file, done)
- function done(err, tree, file) {
- if (err) {
- next(err)
- } else {
- ctx.tree = tree
- ctx.file = file
- next()
- }
- }
- }
- function pipelineStringify(p, ctx) {
- ctx.file.contents = p.stringify(ctx.tree, ctx.file)
- }
- /* Function to create the first processor. */
- function unified() {
- var attachers = []
- var transformers = trough()
- var namespace = {}
- var frozen = false
- var freezeIndex = -1
- /* Data management. */
- processor.data = data
- /* Lock. */
- processor.freeze = freeze
- /* Plug-ins. */
- processor.attachers = attachers
- processor.use = use
- /* API. */
- processor.parse = parse
- processor.stringify = stringify
- processor.run = run
- processor.runSync = runSync
- processor.process = process
- processor.processSync = processSync
- /* Expose. */
- return processor
- /* Create a new processor based on the processor
- * in the current scope. */
- function processor() {
- var destination = unified()
- var length = attachers.length
- var index = -1
- while (++index < length) {
- destination.use.apply(null, attachers[index])
- }
- destination.data(extend(true, {}, namespace))
- return destination
- }
- /* Freeze: used to signal a processor that has finished
- * configuration.
- *
- * For example, take unified itself. It’s frozen.
- * Plug-ins should not be added to it. Rather, it should
- * be extended, by invoking it, before modifying it.
- *
- * In essence, always invoke this when exporting a
- * processor. */
- function freeze() {
- var values
- var plugin
- var options
- var transformer
- if (frozen) {
- return processor
- }
- while (++freezeIndex < attachers.length) {
- values = attachers[freezeIndex]
- plugin = values[0]
- options = values[1]
- transformer = null
- if (options === false) {
- continue
- }
- if (options === true) {
- values[1] = undefined
- }
- transformer = plugin.apply(processor, values.slice(1))
- if (typeof transformer === 'function') {
- transformers.use(transformer)
- }
- }
- frozen = true
- freezeIndex = Infinity
- return processor
- }
- /* Data management.
- * Getter / setter for processor-specific informtion. */
- function data(key, value) {
- if (string(key)) {
- /* Set `key`. */
- if (arguments.length === 2) {
- assertUnfrozen('data', frozen)
- namespace[key] = value
- return processor
- }
- /* Get `key`. */
- return (own.call(namespace, key) && namespace[key]) || null
- }
- /* Set space. */
- if (key) {
- assertUnfrozen('data', frozen)
- namespace = key
- return processor
- }
- /* Get space. */
- return namespace
- }
- /* Plug-in management.
- *
- * Pass it:
- * * an attacher and options,
- * * a preset,
- * * a list of presets, attachers, and arguments (list
- * of attachers and options). */
- function use(value) {
- var settings
- assertUnfrozen('use', frozen)
- if (value === null || value === undefined) {
- /* Empty */
- } else if (typeof value === 'function') {
- addPlugin.apply(null, arguments)
- } else if (typeof value === 'object') {
- if ('length' in value) {
- addList(value)
- } else {
- addPreset(value)
- }
- } else {
- throw new Error('Expected usable value, not `' + value + '`')
- }
- if (settings) {
- namespace.settings = extend(namespace.settings || {}, settings)
- }
- return processor
- function addPreset(result) {
- addList(result.plugins)
- if (result.settings) {
- settings = extend(settings || {}, result.settings)
- }
- }
- function add(value) {
- if (typeof value === 'function') {
- addPlugin(value)
- } else if (typeof value === 'object') {
- if ('length' in value) {
- addPlugin.apply(null, value)
- } else {
- addPreset(value)
- }
- } else {
- throw new Error('Expected usable value, not `' + value + '`')
- }
- }
- function addList(plugins) {
- var length
- var index
- if (plugins === null || plugins === undefined) {
- /* Empty */
- } else if (typeof plugins === 'object' && 'length' in plugins) {
- length = plugins.length
- index = -1
- while (++index < length) {
- add(plugins[index])
- }
- } else {
- throw new Error('Expected a list of plugins, not `' + plugins + '`')
- }
- }
- function addPlugin(plugin, value) {
- var entry = find(plugin)
- if (entry) {
- if (plain(entry[1]) && plain(value)) {
- value = extend(entry[1], value)
- }
- entry[1] = value
- } else {
- attachers.push(slice.call(arguments))
- }
- }
- }
- function find(plugin) {
- var length = attachers.length
- var index = -1
- var entry
- while (++index < length) {
- entry = attachers[index]
- if (entry[0] === plugin) {
- return entry
- }
- }
- }
- /* Parse a file (in string or VFile representation)
- * into a Unist node using the `Parser` on the
- * processor. */
- function parse(doc) {
- var file = vfile(doc)
- var Parser
- freeze()
- Parser = processor.Parser
- assertParser('parse', Parser)
- if (newable(Parser)) {
- return new Parser(String(file), file).parse()
- }
- return Parser(String(file), file) // eslint-disable-line new-cap
- }
- /* Run transforms on a Unist node representation of a file
- * (in string or VFile representation), async. */
- function run(node, file, cb) {
- assertNode(node)
- freeze()
- if (!cb && typeof file === 'function') {
- cb = file
- file = null
- }
- if (!cb) {
- return new Promise(executor)
- }
- executor(null, cb)
- function executor(resolve, reject) {
- transformers.run(node, vfile(file), done)
- function done(err, tree, file) {
- tree = tree || node
- if (err) {
- reject(err)
- } else if (resolve) {
- resolve(tree)
- } else {
- cb(null, tree, file)
- }
- }
- }
- }
- /* Run transforms on a Unist node representation of a file
- * (in string or VFile representation), sync. */
- function runSync(node, file) {
- var complete = false
- var result
- run(node, file, done)
- assertDone('runSync', 'run', complete)
- return result
- function done(err, tree) {
- complete = true
- bail(err)
- result = tree
- }
- }
- /* Stringify a Unist node representation of a file
- * (in string or VFile representation) into a string
- * using the `Compiler` on the processor. */
- function stringify(node, doc) {
- var file = vfile(doc)
- var Compiler
- freeze()
- Compiler = processor.Compiler
- assertCompiler('stringify', Compiler)
- assertNode(node)
- if (newable(Compiler)) {
- return new Compiler(node, file).compile()
- }
- return Compiler(node, file) // eslint-disable-line new-cap
- }
- /* Parse a file (in string or VFile representation)
- * into a Unist node using the `Parser` on the processor,
- * then run transforms on that node, and compile the
- * resulting node using the `Compiler` on the processor,
- * and store that result on the VFile. */
- function process(doc, cb) {
- freeze()
- assertParser('process', processor.Parser)
- assertCompiler('process', processor.Compiler)
- if (!cb) {
- return new Promise(executor)
- }
- executor(null, cb)
- function executor(resolve, reject) {
- var file = vfile(doc)
- pipeline.run(processor, {file: file}, done)
- function done(err) {
- if (err) {
- reject(err)
- } else if (resolve) {
- resolve(file)
- } else {
- cb(null, file)
- }
- }
- }
- }
- /* Process the given document (in string or VFile
- * representation), sync. */
- function processSync(doc) {
- var complete = false
- var file
- freeze()
- assertParser('processSync', processor.Parser)
- assertCompiler('processSync', processor.Compiler)
- file = vfile(doc)
- process(file, done)
- assertDone('processSync', 'process', complete)
- return file
- function done(err) {
- complete = true
- bail(err)
- }
- }
- }
- /* Check if `func` is a constructor. */
- function newable(value) {
- return typeof value === 'function' && keys(value.prototype)
- }
- /* Check if `value` is an object with keys. */
- function keys(value) {
- var key
- for (key in value) {
- return true
- }
- return false
- }
- /* Assert a parser is available. */
- function assertParser(name, Parser) {
- if (typeof Parser !== 'function') {
- throw new Error('Cannot `' + name + '` without `Parser`')
- }
- }
- /* Assert a compiler is available. */
- function assertCompiler(name, Compiler) {
- if (typeof Compiler !== 'function') {
- throw new Error('Cannot `' + name + '` without `Compiler`')
- }
- }
- /* Assert the processor is not frozen. */
- function assertUnfrozen(name, frozen) {
- if (frozen) {
- throw new Error(
- [
- 'Cannot invoke `' + name + '` on a frozen processor.\nCreate a new ',
- 'processor first, by invoking it: use `processor()` instead of ',
- '`processor`.'
- ].join('')
- )
- }
- }
- /* Assert `node` is a Unist node. */
- function assertNode(node) {
- if (!node || !string(node.type)) {
- throw new Error('Expected node, got `' + node + '`')
- }
- }
- /* Assert that `complete` is `true`. */
- function assertDone(name, asyncName, complete) {
- if (!complete) {
- throw new Error(
- '`' + name + '` finished async. Use `' + asyncName + '` instead'
- )
- }
- }
|