123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- 'use strict'
-
- var bail = require('bail')
- var buffer = require('is-buffer')
- var extend = require('extend')
- var plain = require('is-plain-obj')
- var trough = require('trough')
- var vfile = require('vfile')
-
- // 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(error, tree, file) {
- if (error) {
- next(error)
- } else {
- ctx.tree = tree
- ctx.file = file
- next()
- }
- }
- }
-
- function pipelineStringify(p, ctx) {
- var result = p.stringify(ctx.tree, ctx.file)
-
- if (result === undefined || result === null) {
- // Empty.
- } else if (typeof result === 'string' || buffer(result)) {
- ctx.file.contents = result
- } else {
- ctx.file.result = result
- }
- }
-
- // Function to create the first processor.
- function unified() {
- var attachers = []
- var transformers = trough()
- var namespace = {}
- var freezeIndex = -1
- var frozen
-
- // Data management.
- processor.data = data
-
- // Lock.
- processor.freeze = freeze
-
- // Plugins.
- 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 index = -1
-
- while (++index < attachers.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.
- // Plugins 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 transformer
-
- if (frozen) {
- return processor
- }
-
- while (++freezeIndex < attachers.length) {
- values = attachers[freezeIndex]
-
- if (values[1] === false) {
- continue
- }
-
- if (values[1] === true) {
- values[1] = undefined
- }
-
- transformer = values[0].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 (typeof key === 'string') {
- // 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
- }
-
- // Plugin 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 index = -1
-
- if (plugins === null || plugins === undefined) {
- // Empty.
- } else if (typeof plugins === 'object' && 'length' in plugins) {
- while (++index < plugins.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(true, entry[1], value)
- }
-
- entry[1] = value
- } else {
- attachers.push(slice.call(arguments))
- }
- }
- }
-
- function find(plugin) {
- var index = -1
-
- while (++index < attachers.length) {
- if (attachers[index][0] === plugin) {
- return attachers[index]
- }
- }
- }
-
- // 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, 'parse')) {
- 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(error, tree, file) {
- tree = tree || node
- if (error) {
- reject(error)
- } 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 result
- var complete
-
- run(node, file, done)
-
- assertDone('runSync', 'run', complete)
-
- return result
-
- function done(error, tree) {
- complete = true
- result = tree
- bail(error)
- }
- }
-
- // 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, 'compile')) {
- 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(error) {
- if (error) {
- reject(error)
- } else if (resolve) {
- resolve(file)
- } else {
- cb(null, file)
- }
- }
- }
- }
-
- // Process the given document (in string or vfile representation), sync.
- function processSync(doc) {
- var file
- var complete
-
- freeze()
- assertParser('processSync', processor.Parser)
- assertCompiler('processSync', processor.Compiler)
- file = vfile(doc)
-
- process(file, done)
-
- assertDone('processSync', 'process', complete)
-
- return file
-
- function done(error) {
- complete = true
- bail(error)
- }
- }
- }
-
- // Check if `value` is a constructor.
- function newable(value, name) {
- return (
- typeof value === 'function' &&
- value.prototype &&
- // A function with keys in its prototype is probably a constructor.
- // Classes’ prototype methods are not enumerable, so we check if some value
- // exists in the prototype.
- (keys(value.prototype) || name in 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`.'
- )
- }
- }
-
- // Assert `node` is a unist node.
- function assertNode(node) {
- if (!node || typeof node.type !== 'string') {
- 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'
- )
- }
- }
|