4 var extend = require('extend')
5 var bail = require('bail')
6 var vfile = require('vfile')
7 var trough = require('trough')
8 var string = require('x-is-string')
9 var plain = require('is-plain-obj')
11 /* Expose a frozen processor. */
12 module.exports = unified().freeze()
15 var own = {}.hasOwnProperty
17 /* Process pipeline. */
18 var pipeline = trough()
21 .use(pipelineStringify)
23 function pipelineParse(p, ctx) {
24 ctx.tree = p.parse(ctx.file)
27 function pipelineRun(p, ctx, next) {
28 p.run(ctx.tree, ctx.file, done)
30 function done(err, tree, file) {
41 function pipelineStringify(p, ctx) {
42 ctx.file.contents = p.stringify(ctx.tree, ctx.file)
45 /* Function to create the first processor. */
48 var transformers = trough()
53 /* Data management. */
57 processor.freeze = freeze
60 processor.attachers = attachers
64 processor.parse = parse
65 processor.stringify = stringify
67 processor.runSync = runSync
68 processor.process = process
69 processor.processSync = processSync
74 /* Create a new processor based on the processor
75 * in the current scope. */
76 function processor() {
77 var destination = unified()
78 var length = attachers.length
81 while (++index < length) {
82 destination.use.apply(null, attachers[index])
85 destination.data(extend(true, {}, namespace))
90 /* Freeze: used to signal a processor that has finished
93 * For example, take unified itself. It’s frozen.
94 * Plug-ins should not be added to it. Rather, it should
95 * be extended, by invoking it, before modifying it.
97 * In essence, always invoke this when exporting a
109 while (++freezeIndex < attachers.length) {
110 values = attachers[freezeIndex]
115 if (options === false) {
119 if (options === true) {
120 values[1] = undefined
123 transformer = plugin.apply(processor, values.slice(1))
125 if (typeof transformer === 'function') {
126 transformers.use(transformer)
131 freezeIndex = Infinity
137 * Getter / setter for processor-specific informtion. */
138 function data(key, value) {
141 if (arguments.length === 2) {
142 assertUnfrozen('data', frozen)
144 namespace[key] = value
150 return (own.call(namespace, key) && namespace[key]) || null
155 assertUnfrozen('data', frozen)
164 /* Plug-in management.
167 * * an attacher and options,
169 * * a list of presets, attachers, and arguments (list
170 * of attachers and options). */
171 function use(value) {
174 assertUnfrozen('use', frozen)
176 if (value === null || value === undefined) {
178 } else if (typeof value === 'function') {
179 addPlugin.apply(null, arguments)
180 } else if (typeof value === 'object') {
181 if ('length' in value) {
187 throw new Error('Expected usable value, not `' + value + '`')
191 namespace.settings = extend(namespace.settings || {}, settings)
196 function addPreset(result) {
197 addList(result.plugins)
199 if (result.settings) {
200 settings = extend(settings || {}, result.settings)
204 function add(value) {
205 if (typeof value === 'function') {
207 } else if (typeof value === 'object') {
208 if ('length' in value) {
209 addPlugin.apply(null, value)
214 throw new Error('Expected usable value, not `' + value + '`')
218 function addList(plugins) {
222 if (plugins === null || plugins === undefined) {
224 } else if (typeof plugins === 'object' && 'length' in plugins) {
225 length = plugins.length
228 while (++index < length) {
232 throw new Error('Expected a list of plugins, not `' + plugins + '`')
236 function addPlugin(plugin, value) {
237 var entry = find(plugin)
240 if (plain(entry[1]) && plain(value)) {
241 value = extend(entry[1], value)
246 attachers.push(slice.call(arguments))
251 function find(plugin) {
252 var length = attachers.length
256 while (++index < length) {
257 entry = attachers[index]
259 if (entry[0] === plugin) {
265 /* Parse a file (in string or VFile representation)
266 * into a Unist node using the `Parser` on the
268 function parse(doc) {
269 var file = vfile(doc)
273 Parser = processor.Parser
274 assertParser('parse', Parser)
276 if (newable(Parser)) {
277 return new Parser(String(file), file).parse()
280 return Parser(String(file), file) // eslint-disable-line new-cap
283 /* Run transforms on a Unist node representation of a file
284 * (in string or VFile representation), async. */
285 function run(node, file, cb) {
289 if (!cb && typeof file === 'function') {
295 return new Promise(executor)
300 function executor(resolve, reject) {
301 transformers.run(node, vfile(file), done)
303 function done(err, tree, file) {
307 } else if (resolve) {
316 /* Run transforms on a Unist node representation of a file
317 * (in string or VFile representation), sync. */
318 function runSync(node, file) {
322 run(node, file, done)
324 assertDone('runSync', 'run', complete)
328 function done(err, tree) {
335 /* Stringify a Unist node representation of a file
336 * (in string or VFile representation) into a string
337 * using the `Compiler` on the processor. */
338 function stringify(node, doc) {
339 var file = vfile(doc)
343 Compiler = processor.Compiler
344 assertCompiler('stringify', Compiler)
347 if (newable(Compiler)) {
348 return new Compiler(node, file).compile()
351 return Compiler(node, file) // eslint-disable-line new-cap
354 /* Parse a file (in string or VFile representation)
355 * into a Unist node using the `Parser` on the processor,
356 * then run transforms on that node, and compile the
357 * resulting node using the `Compiler` on the processor,
358 * and store that result on the VFile. */
359 function process(doc, cb) {
361 assertParser('process', processor.Parser)
362 assertCompiler('process', processor.Compiler)
365 return new Promise(executor)
370 function executor(resolve, reject) {
371 var file = vfile(doc)
373 pipeline.run(processor, {file: file}, done)
378 } else if (resolve) {
387 /* Process the given document (in string or VFile
388 * representation), sync. */
389 function processSync(doc) {
394 assertParser('processSync', processor.Parser)
395 assertCompiler('processSync', processor.Compiler)
400 assertDone('processSync', 'process', complete)
411 /* Check if `func` is a constructor. */
412 function newable(value) {
413 return typeof value === 'function' && keys(value.prototype)
416 /* Check if `value` is an object with keys. */
417 function keys(value) {
425 /* Assert a parser is available. */
426 function assertParser(name, Parser) {
427 if (typeof Parser !== 'function') {
428 throw new Error('Cannot `' + name + '` without `Parser`')
432 /* Assert a compiler is available. */
433 function assertCompiler(name, Compiler) {
434 if (typeof Compiler !== 'function') {
435 throw new Error('Cannot `' + name + '` without `Compiler`')
439 /* Assert the processor is not frozen. */
440 function assertUnfrozen(name, frozen) {
444 'Cannot invoke `' + name + '` on a frozen processor.\nCreate a new ',
445 'processor first, by invoking it: use `processor()` instead of ',
452 /* Assert `node` is a Unist node. */
453 function assertNode(node) {
454 if (!node || !string(node.type)) {
455 throw new Error('Expected node, got `' + node + '`')
459 /* Assert that `complete` is `true`. */
460 function assertDone(name, asyncName, complete) {
463 '`' + name + '` finished async. Use `' + asyncName + '` instead'