diff options
Diffstat (limited to 'tools/eslint/node_modules/unified/index.js')
-rw-r--r-- | tools/eslint/node_modules/unified/index.js | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/tools/eslint/node_modules/unified/index.js b/tools/eslint/node_modules/unified/index.js new file mode 100644 index 0000000000..051383e98a --- /dev/null +++ b/tools/eslint/node_modules/unified/index.js @@ -0,0 +1,637 @@ +/** + * @author Titus Wormer + * @copyright 2015 Titus Wormer + * @license MIT + * @module unified + * @fileoverview Pluggable text processing interface. + */ + +'use strict'; + +/* Dependencies. */ +var events = require('events'); +var has = require('has'); +var once = require('once'); +var extend = require('extend'); +var bail = require('bail'); +var vfile = require('vfile'); +var trough = require('trough'); + +/* Expose an abstract processor. */ +module.exports = unified().abstract(); + +/* Methods. */ +var slice = [].slice; + +/* Process pipeline. */ +var pipeline = trough() + .use(function (p, ctx) { + ctx.tree = p.parse(ctx.file, ctx.options); + }) + .use(function (p, ctx, next) { + p.run(ctx.tree, ctx.file, function (err, tree, file) { + if (err) { + next(err); + } else { + ctx.tree = tree; + ctx.file = file; + next(); + } + }); + }) + .use(function (p, ctx) { + ctx.file.contents = p.stringify(ctx.tree, ctx.file, ctx.options); + }); + +/** + * Function to create the first processor. + * + * @return {Function} - First processor. + */ +function unified() { + var attachers = []; + var transformers = trough(); + var namespace = {}; + var chunks = []; + var emitter = new events.EventEmitter(); + var ended = false; + var concrete = true; + var settings; + var key; + + /** + * Create a new processor based on the processor + * in the current scope. + * + * @return {Processor} - New concrete processor based + * on the descendant processor. + */ + 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; + } + + /* Mix in methods. */ + for (key in emitter) { + processor[key] = emitter[key]; + } + + /* Helpers. */ + + /** + * Assert a parser is available. + * + * @param {string} name - Name of callee. + */ + function assertParser(name) { + if (!isParser(processor.Parser)) { + throw new Error('Cannot `' + name + '` without `Parser`'); + } + } + + /** + * Assert a compiler is available. + * + * @param {string} name - Name of callee. + */ + function assertCompiler(name) { + if (!isCompiler(processor.Compiler)) { + throw new Error('Cannot `' + name + '` without `Compiler`'); + } + } + + /** + * Assert the processor is concrete. + * + * @param {string} name - Name of callee. + */ + function assertConcrete(name) { + if (!concrete) { + throw new Error( + 'Cannot ' + + (name ? 'invoke `' + name + '` on' : 'pipe into') + + ' abstract processor.\n' + + 'To make the processor concrete, invoke it: ' + + 'use `processor()` instead of `processor`.' + ); + } + } + + /** + * Assert `node` is a Unist node. + * + * @param {*} node - Value to check. + */ + function assertNode(node) { + if (!isNode(node)) { + throw new Error('Expected node, got `' + node + '`'); + } + } + + /** + * Assert, if no `done` is given, that `complete` is + * `true`. + * + * @param {string} name - Name of callee. + * @param {boolean} complete - Whether an async process + * is complete. + * @param {Function?} done - Optional handler of async + * results. + */ + function assertDone(name, complete, done) { + if (!complete && !done) { + throw new Error( + 'Expected `done` to be given to `' + name + '` ' + + 'as async plug-ins are used' + ); + } + } + + /* Throw as early as possible. + * As events are triggered synchroneously, the stack + * is preserved. */ + processor.on('pipe', function () { + assertConcrete(); + }); + + /** + * Abstract: used to signal an abstract processor which + * should made concrete before using. + * + * For example, take unified itself. It’s abstract. + * Plug-ins should not be added to it. Rather, it should + * be made concrete (by invoking it) before modifying it. + * + * In essence, always invoke this when exporting a + * processor. + * + * @return {Processor} - The operated on processor. + */ + function abstract() { + concrete = false; + + return processor; + } + + /** + * Data management. + * + * Getter / setter for processor-specific informtion. + * + * @param {string} key - Key to get or set. + * @param {*} value - Value to set. + * @return {*} - Either the operator on processor in + * setter mode; or the value stored as `key` in + * getter mode. + */ + function data(key, value) { + assertConcrete('data'); + + if (typeof key === 'string') { + /* Set `key`. */ + if (arguments.length === 2) { + namespace[key] = value; + + return processor; + } + + /* Get `key`. */ + return (has(namespace, key) && namespace[key]) || null; + } + + /* Get space. */ + if (!key) { + return namespace; + } + + /* Set space. */ + namespace = key; + + return processor; + } + + /** + * Plug-in management. + * + * Pass it: + * * an attacher and options, + * * a list of attachers and options for all of them; + * * a tuple of one attacher and options. + * * a matrix: list containing any of the above and + * matrices. + * + * @param {...*} value - See description. + * @return {Processor} - The operated on processor. + */ + function use(value) { + var args = slice.call(arguments, 0); + var params = args.slice(1); + var index; + var length; + var transformer; + + assertConcrete('use'); + + /* Multiple attachers. */ + if ('length' in value && !isFunction(value)) { + index = -1; + length = value.length; + + if (!isFunction(value[0])) { + /* Matrix of things. */ + while (++index < length) { + use(value[index]); + } + } else if (isFunction(value[1])) { + /* List of things. */ + while (++index < length) { + use.apply(null, [value[index]].concat(params)); + } + } else { + /* Arguments. */ + use.apply(null, value); + } + + return processor; + } + + /* Store attacher. */ + attachers.push(args); + + /* Single attacher. */ + transformer = value.apply(null, [processor].concat(params)); + + if (isFunction(transformer)) { + transformers.use(transformer); + } + + return processor; + } + + /** + * Parse a file (in string or VFile representation) + * into a Unist node using the `Parser` on the + * processor. + * + * @param {(string|VFile)?} [file] - File to process. + * @param {Object?} [options] - Configuration. + * @return {Node} - Unist node. + */ + function parse(file, options) { + assertConcrete('parse'); + assertParser('parse'); + + return new processor.Parser(vfile(file), options, processor).parse(); + } + + /** + * Run transforms on a Unist node representation of a file + * (in string or VFile representation). + * + * @param {Node} node - Unist node. + * @param {(string|VFile)?} [file] - File representation. + * @param {Function?} [done] - Callback. + * @return {Node} - The given or resulting Unist node. + */ + function run(node, file, done) { + var complete = false; + var result; + + assertConcrete('run'); + assertNode(node); + + result = node; + + if (!done && file && !isFile(file)) { + done = file; + file = null; + } + + transformers.run(node, vfile(file), function (err, tree, file) { + complete = true; + result = tree || node; + + (done || bail)(err, tree, file); + }); + + assertDone('run', complete, done); + + return result; + } + + /** + * Stringify a Unist node representation of a file + * (in string or VFile representation) into a string + * using the `Compiler` on the processor. + * + * @param {Node} node - Unist node. + * @param {(string|VFile)?} [file] - File representation. + * @param {Object?} [options] - Configuration. + * @return {string} - String representation. + */ + function stringify(node, file, options) { + assertConcrete('stringify'); + assertCompiler('stringify'); + assertNode(node); + + if (!options && file && !isFile(file)) { + options = file; + file = null; + } + + return new processor.Compiler(vfile(file), options, processor).compile(node); + } + + /** + * 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. + * + * @param {(string|VFile)?} file - File representation. + * @param {Object?} [options] - Configuration. + * @param {Function?} [done] - Callback. + * @return {VFile} - The given or resulting VFile. + */ + function process(file, options, done) { + var complete = false; + + assertConcrete('process'); + assertParser('process'); + assertCompiler('process'); + + if (!done && isFunction(options)) { + done = options; + options = null; + } + + file = vfile(file); + + pipeline.run(processor, { + file: file, + options: options || {} + }, function (err) { + complete = true; + + if (done) { + done(err, file); + } else { + bail(err); + } + }); + + assertDone('process', complete, done); + + return file; + } + + /* Streams. */ + + /** + * Write a chunk into memory. + * + * @param {(Buffer|string)?} chunk - Value to write. + * @param {string?} [encoding] - Encoding. + * @param {Function?} [callback] - Callback. + * @return {boolean} - Whether the write was succesful. + */ + function write(chunk, encoding, callback) { + assertConcrete('write'); + + if (isFunction(encoding)) { + callback = encoding; + encoding = null; + } + + if (ended) { + throw new Error('Did not expect `write` after `end`'); + } + + chunks.push((chunk || '').toString(encoding || 'utf8')); + + if (callback) { + callback(); + } + + /* Signal succesful write. */ + return true; + } + + /** + * End the writing. Passes all arguments to a final + * `write`. Starts the process, which will trigger + * `error`, with a fatal error, if any; `data`, with + * the generated document in `string` form, if + * succesful. If messages are triggered during the + * process, those are triggerd as `warning`s. + * + * @return {boolean} - Whether the last write was + * succesful. + */ + function end() { + assertConcrete('end'); + assertParser('end'); + assertCompiler('end'); + + write.apply(null, arguments); + + ended = true; + + process(chunks.join(''), settings, function (err, file) { + var messages = file.messages; + var length = messages.length; + var index = -1; + + chunks = settings = null; + + /* Trigger messages as warnings, except for fatal error. */ + while (++index < length) { + if (messages[index] !== err) { + processor.emit('warning', messages[index]); + } + } + + if (err) { + /* Don’t enter an infinite error throwing loop. */ + global.setTimeout(function () { + processor.emit('error', err); + }, 4); + } else { + processor.emit('data', file.contents); + processor.emit('end'); + } + }); + + return true; + } + + /** + * Pipe the processor into a writable stream. + * + * Basically `Stream#pipe`, but inlined and + * simplified to keep the bundled size down. + * + * @see https://github.com/nodejs/node/blob/master/lib/stream.js#L26 + * + * @param {Stream} dest - Writable stream. + * @param {Object?} [options] - Processing + * configuration. + * @return {Stream} - The destination stream. + */ + function pipe(dest, options) { + var onend = once(function () { + if (dest.end) { + dest.end(); + } + }); + + assertConcrete('pipe'); + + settings = options || {}; + + /** + * Handle data. + * + * @param {*} chunk - Data to pass through. + */ + function ondata(chunk) { + if (dest.writable) { + dest.write(chunk); + } + } + + /** + * Clean listeners. + */ + function cleanup() { + processor.removeListener('data', ondata); + processor.removeListener('end', onend); + processor.removeListener('error', onerror); + processor.removeListener('end', cleanup); + processor.removeListener('close', cleanup); + + dest.removeListener('error', onerror); + dest.removeListener('close', cleanup); + } + + /** + * Close dangling pipes and handle unheard errors. + * + * @param {Error} err - Exception. + */ + function onerror(err) { + var handlers = processor._events.error; + + cleanup(); + + /* Cannot use `listenerCount` in node <= 0.12. */ + if (!handlers || !handlers.length || handlers === onerror) { + throw err; /* Unhandled stream error in pipe. */ + } + } + + processor.on('data', ondata); + processor.on('error', onerror); + processor.on('end', cleanup); + processor.on('close', cleanup); + + /* If the 'end' option is not supplied, dest.end() will be + * called when the 'end' or 'close' events are received. + * Only dest.end() once. */ + if (!dest._isStdio && settings.end !== false) { + processor.on('end', onend); + } + + dest.on('error', onerror); + dest.on('close', cleanup); + + dest.emit('pipe', processor); + + return dest; + } + + /* Data management. */ + processor.data = data; + + /* Lock. */ + processor.abstract = abstract; + + /* Plug-ins. */ + processor.use = use; + + /* Streaming. */ + processor.writable = true; + processor.readable = true; + processor.write = write; + processor.end = end; + processor.pipe = pipe; + + /* API. */ + processor.parse = parse; + processor.stringify = stringify; + processor.run = run; + processor.process = process; + + /* Expose. */ + return processor; +} + +/** + * Check if `node` is a Unist node. + * + * @param {*} node - Value. + * @return {boolean} - Whether `node` is a Unist node. + */ +function isNode(node) { + return node && typeof node.type === 'string' && node.type.length !== 0; +} + +/** + * Check if `file` is a VFile. + * + * @param {*} file - Value. + * @return {boolean} - Whether `file` is a VFile. + */ +function isFile(file) { + return file && typeof file.contents === 'string'; +} + +/** + * Check if `fn` is a function. + * + * @param {*} fn - Value. + * @return {boolean} - Whether `fn` is a function. + */ +function isFunction(fn) { + return typeof fn === 'function'; +} + +/** + * Check if `compiler` is a Compiler. + * + * @param {*} compiler - Value. + * @return {boolean} - Whether `compiler` is a Compiler. + */ +function isCompiler(compiler) { + return isFunction(compiler) && compiler.prototype && isFunction(compiler.prototype.compile); +} + +/** + * Check if `parser` is a Parser. + * + * @param {*} parser - Value. + * @return {boolean} - Whether `parser` is a Parser. + */ +function isParser(parser) { + return isFunction(parser) && parser.prototype && isFunction(parser.prototype.parse); +} |