Socket
Socket
Sign inDemoInstall

unified

Package Overview
Dependencies
Maintainers
1
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

unified - npm Package Compare versions

Comparing version 5.1.0 to 6.0.0

794

index.js

@@ -1,15 +0,5 @@

/**
* @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');

@@ -19,36 +9,37 @@ var bail = require('bail');

var trough = require('trough');
var buffer = require('is-buffer');
var string = require('x-is-string');
var func = require('x-is-function');
var array = require('isarray');
/* Expose an abstract processor. */
module.exports = unified().abstract();
/* Expose a frozen processor. */
module.exports = unified().freeze();
/* 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);
});
var pipeline = trough().use(pipelineParse).use(pipelineRun).use(pipelineStringify);
/**
* Function to create the first processor.
*
* @return {Function} - First processor.
*/
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() {

@@ -58,21 +49,4 @@ var attachers = [];

var namespace = {};
var chunks = [];
var emitter = new events.EventEmitter();
var ended = false;
var concrete = true;
var settings;
var key;
var frozen = false;
/* Mix in methods. */
for (key in emitter) {
processor[key] = emitter[key];
}
/* Throw as early as possible.
* As events are triggered synchroneously, the stack
* is preserved. */
processor.on('pipe', function () {
assertConcrete();
});
/* Data management. */

@@ -82,3 +56,3 @@ processor.data = data;

/* Lock. */
processor.abstract = abstract;
processor.freeze = freeze;

@@ -89,9 +63,2 @@ /* Plug-ins. */

/* Streaming. */
processor.writable = true;
processor.readable = true;
processor.write = write;
processor.end = end;
processor.pipe = pipe;
/* API. */

@@ -101,3 +68,5 @@ processor.parse = parse;

processor.run = run;
processor.runSync = runSync;
processor.process = process;
processor.processSync = processSync;

@@ -107,9 +76,4 @@ /* Expose. */

/**
* Create a new processor based on the processor
* in the current scope.
*
* @return {Processor} - New concrete processor based
* on the descendant processor.
*/
/* Create a new processor based on the processor
* in the current scope. */
function processor() {

@@ -129,88 +93,44 @@ var destination = unified();

/* Helpers. */
/**
* Assert a parser is available.
/* Freeze: used to signal a processor that has finished
* configuration.
*
* @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.
* 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.
*
* @param {string} name - Name of callee.
*/
function assertCompiler(name) {
if (!isCompiler(processor.Compiler)) {
throw new Error('Cannot `' + name + '` without `Compiler`');
}
}
* In essence, always invoke this when exporting a
* processor. */
function freeze() {
var index = -1;
var values;
var plugin;
var options;
var transformer;
/**
* 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`.'
);
if (frozen) {
return 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 + '`');
}
}
while (++index < attachers.length) {
values = attachers[index];
plugin = values[0];
options = values[1];
transformer = null;
/**
* 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'
);
if (options === false) {
return;
}
if (options === true) {
values[1] = undefined;
}
transformer = plugin.apply(processor, values.slice(1));
if (func(transformer)) {
transformers.use(transformer);
}
}
}
/**
* 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;
frozen = true;

@@ -220,19 +140,10 @@ 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.
*/
/* Data management.
* Getter / setter for processor-specific informtion. */
function data(key, value) {
assertConcrete('data');
if (string(key)) {
/* Set `key`. */
if (arguments.length === 2) {
assertUnfrozen('data', frozen);
namespace[key] = value;

@@ -247,430 +158,311 @@

/* Get space. */
if (!key) {
return namespace;
/* Set space. */
if (key) {
assertUnfrozen('data', frozen);
namespace = key;
return processor;
}
/* Set space. */
namespace = key;
return processor;
/* Get space. */
return namespace;
}
/**
* Plug-in management.
/* 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.
* * a processor: another processor to use all its
* plugins (except parser if there’s already one).
*
* @param {...*} value - See description.
* @return {Processor} - The operated on processor.
*/
* * a preset,
* * a list of presets, attachers, and arguments (list
* of attachers and options). */
function use(value) {
var args = slice.call(arguments, 0);
var params = args.slice(1);
var parser;
var index;
var length;
var transformer;
var result;
var settings;
assertConcrete('use');
assertUnfrozen('use', frozen);
/* 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));
}
if (value === null || value === undefined) {
/* empty */
} else if (func(value)) {
addPlugin.apply(null, arguments);
} else if (typeof value === 'object') {
if ('length' in value) {
addList(value);
} else {
/* Arguments. */
use.apply(null, value);
addPreset(value);
}
} else {
throw new Error('Expected usable value, not `' + value + '`');
}
return processor;
if (settings) {
namespace.settings = extend(namespace.settings || {}, settings);
}
/* Store attacher. */
attachers.push(args);
return processor;
/* Use a processor (except its parser if there’s already one.
* Note that the processor is stored on `attachers`, making
* it possibly mutating in the future, but also ensuring
* the parser isn’t overwritten in the future either. */
if (isProcessor(value)) {
parser = processor.Parser;
result = use(value.attachers);
function addPreset(result) {
addList(result.plugins);
if (parser) {
processor.Parser = parser;
if (result.settings) {
settings = extend(settings || {}, result.settings);
}
}
return result;
function add(value) {
if (func(value)) {
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 + '`');
}
}
/* Single attacher. */
transformer = value.apply(null, [processor].concat(params));
function addList(plugins) {
var length;
var index;
if (isFunction(transformer)) {
transformers.use(transformer);
if (plugins === null || plugins === undefined) {
/* empty */
} else if (array(plugins)) {
length = plugins.length;
index = -1;
while (++index < length) {
add(plugins[index]);
}
} else {
throw new Error('Expected a list of plugins, not `' + plugins + '`');
}
}
return processor;
}
function addPlugin(plugin, value) {
var entry = find(plugin);
/**
* Parse a file (in string or VFile representation)
* into a Unist node using the `Parser` on the
* processor.
*
* @param {VFile?} [file] - File to process.
* @param {Object?} [options] - Configuration.
* @return {Node} - Unist node.
*/
function parse(file, options) {
assertConcrete('parse');
assertParser('parse');
if (entry) {
if (value !== false && entry[1] !== false && !array(value)) {
value = extend(entry[1], value);
}
return new processor.Parser(vfile(file), options, processor).parse();
entry[1] = value;
} else {
attachers.push(slice.call(arguments));
}
}
}
/**
* 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;
function find(plugin) {
var length = attachers.length;
var index = -1;
var entry;
assertConcrete('run');
assertNode(node);
while (++index < length) {
entry = attachers[index];
result = node;
if (!done && isFunction(file)) {
done = file;
file = null;
if (entry[0] === plugin) {
return entry;
}
}
}
transformers.run(node, vfile(file), function (err, tree, file) {
complete = true;
result = tree || node;
/* 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;
(done || bail)(err, tree, file);
});
freeze();
Parser = processor.Parser;
assertParser('parse', Parser);
assertDone('run', complete, done);
if (newable(Parser)) {
return new Parser(String(file), file).parse();
}
return result;
return Parser(String(file), file); // eslint-disable-line new-cap
}
/**
* 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');
/* 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 (
!options &&
!string(file) &&
!buffer(file) &&
!(typeof file === 'object' && 'messages' in file)
) {
options = file;
if (!cb && func(file)) {
cb = 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;
if (!cb) {
return new Promise(executor);
}
file = vfile(file);
executor(null, cb);
pipeline.run(processor, {
file: file,
options: options || {}
}, function (err) {
complete = true;
function executor(resolve, reject) {
transformers.run(node, vfile(file), done);
if (done) {
done(err, file);
} else {
bail(err);
function done(err, tree, file) {
tree = tree || node;
if (err) {
reject(err);
} else if (resolve) {
resolve(tree);
} else {
cb(null, tree, file);
}
}
});
}
}
assertDone('process', complete, done);
/* 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;
return file;
}
run(node, file, done);
/* Streams. */
assertDone('runSync', 'run', complete);
/**
* 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');
return result;
if (isFunction(encoding)) {
callback = encoding;
encoding = null;
function done(err, tree) {
complete = true;
bail(err);
result = tree;
}
}
if (ended) {
throw new Error('Did not expect `write` after `end`');
}
/* 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;
chunks.push((chunk || '').toString(encoding || 'utf8'));
freeze();
Compiler = processor.Compiler;
assertCompiler('stringify', Compiler);
assertNode(node);
if (callback) {
callback();
if (newable(Compiler)) {
return new Compiler(node, file).compile();
}
/* Signal succesful write. */
return true;
return Compiler(node, file); // eslint-disable-line new-cap
}
/**
* 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');
/* 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);
write.apply(null, arguments);
if (!cb) {
return new Promise(executor);
}
ended = true;
executor(null, cb);
process(chunks.join(''), settings, function (err, file) {
var messages = file.messages;
var length = messages.length;
var index = -1;
function executor(resolve, reject) {
var file = vfile(doc);
chunks = settings = null;
pipeline.run(processor, {file: file}, done);
/* Trigger messages as warnings, except for fatal error. */
while (++index < length) {
if (messages[index] !== err) {
processor.emit('warning', messages[index]);
function done(err) {
if (err) {
reject(err);
} else if (resolve) {
resolve(file);
} else {
cb(null, file);
}
}
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(onended);
/* Process the given document (in string or VFile
* representation), sync. */
function processSync(doc) {
var complete = false;
var file;
assertConcrete('pipe');
freeze();
assertParser('processSync', processor.Parser);
assertCompiler('processSync', processor.Compiler);
file = vfile(doc);
settings = options || {};
process(file, done);
processor.on('data', ondata);
processor.on('error', onerror);
processor.on('end', cleanup);
processor.on('close', cleanup);
assertDone('processSync', 'process', complete);
/* 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);
}
return file;
dest.on('error', onerror);
dest.on('close', cleanup);
dest.emit('pipe', processor);
return dest;
/** End destination. */
function onended() {
if (dest.end) {
dest.end();
}
function done(err) {
complete = true;
bail(err);
}
}
}
/**
* Handle data.
*
* @param {*} chunk - Data to pass through.
*/
function ondata(chunk) {
if (dest.writable) {
dest.write(chunk);
}
}
/* Check if `func` is a constructor. */
function newable(value) {
return func(value) && keys(value.prototype);
}
/**
* 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. */
}
}
/* Check if `value` is an object with keys. */
function keys(value) {
var key;
for (key in value) {
return true;
}
return false;
}
/**
* Check if `node` is a Unist node.
*
* @param {*} node - Value.
* @return {boolean} - Whether `node` is a Unist node.
*/
function isNode(node) {
return node && string(node.type) && node.type.length !== 0;
/* Assert a parser is available. */
function assertParser(name, Parser) {
if (!func(Parser)) {
throw new Error('Cannot `' + name + '` without `Parser`');
}
}
/**
* Check if `fn` is a function.
*
* @param {*} fn - Value.
* @return {boolean} - Whether `fn` is a function.
*/
function isFunction(fn) {
return typeof fn === 'function';
/* Assert a compiler is available. */
function assertCompiler(name, Compiler) {
if (!func(Compiler)) {
throw new Error('Cannot `' + name + '` without `Compiler`');
}
}
/**
* 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);
/* Assert the processor is not frozen. */
function assertUnfrozen(name, frozen) {
if (frozen) {
throw new Error(
'Cannot invoke `' + name + '` on a frozen processor.\n' +
'Create a new processor first, by invoking it: ' +
'use `processor()` instead of `processor`.'
);
}
}
/**
* 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);
/* Assert `node` is a Unist node. */
function assertNode(node) {
if (!node || !string(node.type)) {
throw new Error('Expected node, got `' + node + '`');
}
}
/**
* Check if `processor` is a unified processor.
*
* @param {*} processor - Value.
* @return {boolean} - Whether `processor` is a processor.
*/
function isProcessor(processor) {
return isFunction(processor) && isFunction(processor.use) && isFunction(processor.process);
/* Assert that `complete` is `true`. */
function assertDone(name, asyncName, complete) {
if (!complete) {
throw new Error('`' + name + '` finished async. Use `' + asyncName + '` instead');
}
}
{
"name": "unified",
"version": "5.1.0",
"version": "6.0.0",
"description": "Pluggable text processing interface",

@@ -12,6 +12,16 @@ "license": "MIT",

"stringify",
"hast",
"rehype",
"retext",
"remark"
],
"repository": "https://github.com/unifiedjs/unified",
"bugs": "https://github.com/unifiedjs/unified/issues",
"author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)",
"contributors": [
"Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)"
],
"files": [
"index.js",
"lib"
],
"dependencies": {

@@ -21,32 +31,16 @@ "bail": "^1.0.0",

"has": "^1.0.1",
"is-buffer": "^1.1.4",
"once": "^1.3.3",
"isarray": "^2.0.1",
"trough": "^1.0.0",
"vfile": "^2.0.0",
"x-is-function": "^1.0.4",
"x-is-string": "^0.1.0"
},
"browser": {
"node-extend": "extend"
},
"repository": "https://github.com/wooorm/unified",
"bugs": "https://github.com/wooorm/unified/issues",
"author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)",
"contributors": [
"Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)"
],
"engines": {
"node": ">=0.11.0"
},
"files": [
"index.js",
"lib"
],
"devDependencies": {
"browserify": "^13.0.0",
"browserify": "^14.0.0",
"esmangle": "^1.0.0",
"nyc": "^8.1.0",
"nyc": "^10.0.0",
"remark-cli": "^2.0.0",
"remark-preset-wooorm": "^1.0.0",
"tape": "^4.4.0",
"xo": "^0.16.0"
"xo": "^0.17.0"
},

@@ -72,2 +66,3 @@ "scripts": {

"rules": {
"import/no-unassigned-import": "off",
"guard-for-in": "off",

@@ -74,0 +69,0 @@ "max-lines": "off"

@@ -10,3 +10,3 @@ # unified [![Build Status][travis-badge]][travis] [![Coverage Status][codecov-badge]][codecov]

[npm][npm-install]:
[npm][]:

@@ -17,5 +17,2 @@ ```bash

**unified** is also available as an AMD, CommonJS, and globals module,
[uncompressed and compressed][releases].
## Usage

@@ -26,19 +23,36 @@

var markdown = require('remark-parse');
var toc = require('remark-toc');
var remark2rehype = require('remark-rehype');
var document = require('rehype-document');
var minify = require('rehype-preset-minify');
var format = require('rehype-format');
var html = require('rehype-stringify');
var reporter = require('vfile-reporter');
process.stdin
.pipe(unified())
unified()
.use(markdown)
.use(toc)
.use(remark2rehype)
.use(document)
.use(minify)
.use(format)
.use(html)
.pipe(process.stdout);
.process('# Hello world!', function (err, file) {
console.error(reporter(err || file));
console.log(String(file));
});
```
Yields:
```html
no issues found
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
```
## Table of Contents

@@ -50,11 +64,14 @@

* [processor.use(plugin\[, options\])](#processoruseplugin-options)
* [processor.parse(file|value\[, options\])](#processorparsefilevalue-options)
* [processor.stringify(node\[, file\]\[, options\])](#processorstringifynode-file-options)
* [processor.parse(file|value)](#processorparsefilevalue)
* [processor.stringify(node\[, file\])](#processorstringifynode-file)
* [processor.run(node\[, file\]\[, done\])](#processorrunnode-file-done)
* [processor.process(file|value\[, options\]\[, done\])](#processorprocessfilevalue-options-done)
* [processor.write(chunk\[, encoding\]\[, callback\])](#processorwritechunk-encoding-callback)
* [processor.end()](#processorend)
* [processor.pipe(stream\[, options\])](#processorpipestream-options)
* [processor.runSync(node\[, file\])](#processorrunsyncnode-file)
* [processor.process(file|value\[, done\])](#processorprocessfilevalue-done)
* [processor.processSync(file|value)](#processorprocesssyncfilevalue)
* [processor.data(key\[, value\])](#processordatakey-value)
* [processor.abstract()](#processorabstract)
* [processor.freeze()](#processorfreeze)
* [Plugin](#plugin)
* [function attacher(\[options\])](#function-attacheroptions)
* [function transformer(node, file\[, next\])](#function-transformernode-file-next)
* [Preset](#preset)
* [License](#license)

@@ -92,5 +109,5 @@

Often, when processors are exposed from a library (for example,
unified itself), they should not be modified directly, as that
unified itself), they should not be configured directly, as that
would change their behaviour for all users. Those processors are
[**abstract**][abstract], and they should be made concrete before
[**frozen**][freeze], and new processors should be made from them before
they are used, by invoking them.

@@ -142,50 +159,6 @@

###### Streaming
**unified** provides a streaming interface which allows it to plug
into transformations outside of itself. An example, which reads
markdown as input, adds a table of content, and writes it out, would
be as follows:
```js
var unified = require('unified');
var markdown = require('remark-parse');
var stringify = require('remark-stringify');
var toc = require('remark-toc');
process.stdin
.pipe(unified())
.use(parse)
.use(toc)
.use(stringify)
.pipe(process.stdout);
```
Which when given on **stdin**(4):
```md
# Alpha
## Table of Content
## Bravo
```
Yields on **stdout**(4):
```md
# Alpha
## Table of Content
* [Bravo](#bravo)
## Bravo
```
###### Programming interface
Next to streaming, there’s also a programming interface, which gives
access to processing metadata (such as lint messages), and supports
multiple passed through files:
The API gives access to processing metadata (such as lint messages), and
supports multiple passed through files:

@@ -201,3 +174,3 @@ ```js

var html = require('rehype-stringify');
var report = require('vfile-reporter');
var reporter = require('vfile-reporter');

@@ -211,3 +184,3 @@ unified()

.process('## Hey guys', function (err, file) {
console.err(report(err || file));
console.err(reporter(err || file));
console.log(file.toString());

@@ -245,5 +218,6 @@ });

* [**remark-retext**][remark-retext].
* [**remark-rehype**][remark-rehype].
* [**rehype-retext**][rehype-retext].
* [**remark-retext**][remark-retext]
* [**remark-rehype**][remark-rehype]
* [**rehype-retext**][rehype-retext]
* [**rehype-remark**][rehype-remark]

@@ -258,3 +232,3 @@ ## API

`Function` — A new [**concrete**][abstract] processor which is
`Function` — A new [**unfrozen**][freeze] processor which is
configured to function the same as its ancestor. But, when the

@@ -271,4 +245,7 @@ descendant processor is configured in the future, that configuration

var remark = require('remark');
var concat = require('concat-stream');
process.stdin.pipe(remark()).pipe(process.stdout);
process.stdin.pipe(concat(function (buf) {
process.stdout.write(remark().processSync(buf))
}));
```

@@ -284,6 +261,4 @@

* `processor.use(plugin[, options])`;
* `processor.use(plugins[, options])`;
* `processor.use(preset)`;
* `processor.use(list)`;
* `processor.use(matrix)`;
* `processor.use(processor)`.

@@ -294,7 +269,6 @@ ###### Parameters

* `options` (`*`, optional) — Configuration for `plugin`.
* `plugins` (`Array.<Function>`) — List of plugins;
* `list` (`Array`) — `plugin` and `options` in an array;
* `matrix` (`Array`) — Array where each entry is a `list`;
* `processor` ([`Processor`][processor]) — Other processor whose
plugins to use (except for a parser).
* `preset` (`Object`) — Object with an optional `plugins` (set to `list`),
and/or an optional `settings` object;
* `list` (`Array`) — plugins, presets, and arguments (a plugin and options
in an array), in an array.

@@ -305,72 +279,33 @@ ###### Returns

#### `Plugin`
###### Note
A **unified** plugin changes the way the applied-on processor works,
in the following ways:
`use` cannot be called on [frozen][freeze] processors. Invoke the processor
first to create a new unfrozen processor.
* It modifies the [**processor**][processor]: such as changing the
parser, the compiler, or linking the processor to other processors;
* It transforms the [**syntax tree**][node] representation of a file;
* It modifies metadata of a file.
###### Example
Plug-in’s are a concept which materialise as [**attacher**][attacher]s.
There are many ways to pass plugins to `.use()`. The below example
gives an overview.
#### `function attacher(processor[, options])`
```js
var unified = require('unified');
An attacher is the thing passed to [`use`][use]. It configures the
processor and in turn can receive options.
unified()
// Plugin with options:
.use(plugin, {})
// Plugins:
.use([plugin, pluginB])
// Two plugins, the second with options:
.use([plugin, [pluginB, {}]])
// Preset with plugins and settings:
.use({plugins: [plugin, [pluginB, {}]], settings: {position: false}})
// Settings only:
.use({settings: {position: false}});
Attachers can configure processors, such as by interacting with parsers
and compilers, linking them to other processors, or by specifying how
the syntax tree is handled.
function plugin() {}
function pluginB() {}
```
###### Parameters
### `processor.parse(file|value)`
* `processor` ([`processor`][processor]) — Context on which it’s used;
* `options` (`*`, optional) — Configuration.
###### Returns
[`transformer`][transformer] — Optional.
#### `function transformer(node, file[, next])`
Transformers modify the syntax tree or metadata of a file.
A transformer is a function which is invoked each time a file is
passed through the transform phase. If an error occurs (either
because it’s thrown, returned, rejected, or passed to [`next`][next]),
the process stops.
The transformation process in **unified** is handled by [`trough`][trough],
see it’s documentation for the exact semantics of transformers.
###### Parameters
* `node` ([**Node**][node]);
* `file` ([**VFile**][file]);
* `next` ([`Function`][next], optional).
###### Returns
* `Error` — Can be returned to stop the process;
* [**Node**][node] — Can be returned and results in further
transformations and `stringify`s to be performed on the new
tree;
* `Promise` — If a promise is returned, the function is asynchronous,
and **must** be resolved (optionally with a [**Node**][node]) or
rejected (optionally with an `Error`).
##### `function next(err[, tree[, file]])`
If the signature of a transformer includes `next` (third argument),
the function **may** finish asynchronous, and **must** invoke `next()`.
###### Parameters
* `err` (`Error`, optional) — Stop the process;
* `node` ([**Node**][node], optional) — New syntax tree;
* `file` ([**VFile**][file], optional) — New virtual file.
### `processor.parse(file|value[, options])`
Parse text to a syntax tree.

@@ -380,5 +315,4 @@

* `file` ([**VFile**][file]);
* `file` ([**VFile**][file])
— Or anything which can be given to `vfile()`.
* `options` (`Object`, optional) — Configuration given to the parser.

@@ -389,14 +323,21 @@ ###### Returns

###### Note
`parse` [freezes][freeze] the processor, if not already frozen.
#### `processor.Parser`
Constructor handling the parsing of text to a syntax tree.
It’s instantiated by the [**parse**][parse] phase in the process
with a [**VFile**][file], `settings`, and the processor.
Function handling the parsing of text to a syntax tree. Used in the
[**parse**][parse] phase in the process and invoked with a `string`
and [**VFile**][file] representation of the document to parse.
The instance must expose a `parse` method which is invoked without
arguments, and must return a syntax tree representation of the
[**VFile**][file].
If `Parser` is a normal parser, it should return a [`Node`][node]: the syntax
tree representation of the given file.
### `processor.stringify(node[, file][, options])`
`Parser` can also be a constructor function, in which case it’s invoked with
`new`. In that case, instances should have a `parse` method, which is invoked
(without arguments), and should return a [`Node`][node].
### `processor.stringify(node[, file])`
Compile a syntax tree to text.

@@ -409,3 +350,2 @@

— Or anything which can be given to `vfile()`.
* `options` (`Object`, optional) — Configuration given to the parser.

@@ -416,12 +356,20 @@ ###### Returns

###### Note
`stringify` [freezes][freeze] the processor, if not already frozen.
#### `processor.Compiler`
Constructor handling the compilation of a syntax tree to text.
It’s instantiated by the [**stringify**][stringify] phase in the
process with a [**VFile**][file], `settings`, and the processor.
Function handling the compilation of syntax tree to a text. Used in the
[**stringify**][stringify] phase in the process and invoked with a
[`Node`][node] and [**VFile**][file] representation of the document to
stringify.
The instance must expose a `compile` method which is invoked with
the syntax tree, and must return a string representation of that
syntax tree.
If `Compiler` is a normal stringifier, it should return a `string`: the text
representation of the given syntax tree.
`Compiler` can also be a constructor function, in which case it’s invoked with
`new`. In that case, instances should have a `compile` method, which is invoked
(without arguments), and should return a `string`.
### `processor.run(node[, file][, done])`

@@ -431,5 +379,2 @@

If asynchronous [**plug-in**][plugin]s are configured, an error
is thrown if [`done`][run-done] is not supplied.
###### Parameters

@@ -444,4 +389,9 @@

[**Node**][node] — The given syntax tree.
[**Promise**][promise], if `done` is not given. Rejected with an error,
or resolved with the resulting syntax tree.
###### Note
`run` [freezes][freeze] the processor, if not already frozen.
##### `function done(err[, node, file])`

@@ -458,4 +408,24 @@

### `processor.process(file|value[, options][, done])`
### `processor.runSync(node[, file])`
Transform a syntax tree by applying [**plug-in**][plugin]s to it.
If asynchronous [**plug-in**][plugin]s are configured, an error is thrown.
###### Parameters
* `node` ([**Node**][node]);
* `file` ([**VFile**][file], optional);
— Or anything which can be given to `vfile()`.
###### Returns
[**Node**][node] — The given syntax tree.
###### Note
`runSync` [freezes][freeze] the processor, if not already frozen.
### `processor.process(file|value[, done])`
Process the given representation of a file as configured on the

@@ -465,5 +435,2 @@ processor. The process invokes `parse`, `run`, and `stringify`

If asynchronous [**plug-in**][plugin]s are configured, an error
is thrown if [`done`][process-done] is not supplied.
###### Parameters

@@ -473,4 +440,2 @@

* `value` (`string`) — String representation of a file;
* `options` (`Object`, optional) — Configuration for both the parser
and compiler;
* `done` ([`Function`][process-done], optional).

@@ -480,4 +445,9 @@

[**VFile**][file] — Virtual file with modified [`contents`][vfile-contents].
[**Promise**][promise], if `done` is not given. Rejected with an error,
or resolved with the resulting file.
###### Note
`process` [freezes][freeze] the processor, if not already frozen.
#### `function done(err, file)`

@@ -493,64 +463,99 @@

### `processor.write(chunk[, encoding][, callback])`
###### Example
> **Note**: Although the interface is compatible with streams,
> all data is currently buffered and passed through in one go.
> This might be changed later.
```js
var unified = require('unified');
var markdown = require('remark-parse');
var remark2rehype = require('remark-rehype');
var document = require('rehype-document');
var format = require('rehype-format');
var html = require('rehype-stringify');
var reporter = require('vfile-reporter');
Write data to the in-memory buffer.
unified()
.use(markdown)
.use(remark2rehype)
.use(document)
.use(format)
.use(html)
.process('# Hello world!')
.then(function (file) {
console.log(String(file));
}, function (err) {
console.error(String(err));
})
```
###### Parameters
Yields:
* `chunk` ([`Buffer`][buffer] or `string`);
* `encoding` (`string`, defaults to `utf8`);
* `callback` (`Function`) — Invoked on successful write.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
```
###### Returns
### `processor.processSync(file|value)`
`boolean` — Whether the write was successful (currently, always true).
Process the given representation of a file as configured on the
processor. The process invokes `parse`, `run`, and `stringify`
internally.
### `processor.end()`
If asynchronous [**plug-in**][plugin]s are configured, an error is thrown.
Signal the writing is complete. Passes all arguments to a final
[`write`][write], and starts the process (using, when available,
options given to [`pipe`][pipe]).
###### Parameters
###### Events
* `file` ([**VFile**][file]);
* `value` (`string`) — String representation of a file;
* `data` (`string`)
— When the process was successful, triggered with the compiled
file;
* `error` (`Error`)
— When the process was unsuccessful, triggered with the fatal
error;
* `warning` ([`VFileMessage`][vfilemessage])
— Each message created by the plug-ins in the process is triggered
and separately passed.
###### Returns
`boolean` — Whether the write was successful (currently, always true).
[**VFile**][file] — Virtual file with modified [`contents`][vfile-contents].
### `processor.pipe(stream[, options])`
###### Note
> **Note**: This does not pass all processed data (e.g., from loose
> `process()` calls) to the destination stream. There’s one process
> created internally especially for streams. Only data piped into
> the processor is piped out.
`processSync` [freezes][freeze] the processor, if not already frozen.
Pipe data streamed into the processor, processed, to the destination
stream. Optionally also set the configuration for how the data
is processed. Calls [`Stream#pipe`][stream-pipe] with the given
arguments under the hood.
###### Example
###### Parameters
```js
var unified = require('unified');
var markdown = require('remark-parse');
var remark2rehype = require('remark-rehype');
var document = require('rehype-document');
var format = require('rehype-format');
var html = require('rehype-stringify');
var reporter = require('vfile-reporter');
* `stream` ([`WritableStream`][writable-stream]);
* `options` (`Object`, optional) — Configuration for process and
`stream.pipe`.
var processor = unified()
.use(markdown)
.use(remark2rehype)
.use(document)
.use(format)
.use(html);
###### Returns
console.log(processor.processSync('# Hello world!').toString());
```
[`WritableStream`][writable-stream] — The given stream.
Yields:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
```
### `processor.data(key[, value])`

@@ -573,2 +578,7 @@

###### Note
Setting information with `data` cannot occur on [frozen][freeze] processors.
Invoke the processor first to create a new unfrozen processor.
###### Example

@@ -590,15 +600,17 @@

### `processor.abstract()`
### `processor.freeze()`
Turn a processor into an abstract processor. Abstract processors
are meant to be extended, and not to be configured or processed
directly (as concrete processors are).
Freeze a processor. Frozen processors are meant to be extended, and not to
be configured or processed directly.
Once a processor is abstract, it cannot be made concrete again.
But, a new concrete processor functioning just like it can be
created by invoking the processor.
Once a processor is frozen, it cannot be unfrozen. But, a new processor
functioning just like it can be created by invoking the processor.
It’s possible to freeze processors explicitly, by calling `.freeze()`, but
[`.parse()`][parse], [`.run()`][run], [`.stringify()`][stringify], and
[`.process()`][process] call `.freeze()` to freeze a processor too.
###### Returns
`Processor` — The processor on which `abstract` is invoked.
`Processor` — The processor on which `freeze` is invoked.

@@ -615,17 +627,20 @@ ###### Example

module.exports = unified().use(parse).use(stringify).abstract();
module.exports = unified().use(parse).use(stringify).freeze();
```
The below example, `a.js`, shows how that processor can be used to
create a command line interface which reformats markdown passed on
**stdin**(4) and outputs it on **stdout**(4).
The below example, `a.js`, shows how that processor can be used and
configured.
```js
var rehype = require('rehype');
var format = require('rehype-format');
// ...
process.stdin.pipe(rehype()).pipe(process.stdout);
rehype()
.use(format)
// ...
```
The below example, `b.js`, shows a similar looking example which
operates on the abstract [**rehype**][rehype] interface. If this
operates on the frozen [**rehype**][rehype] interface. If this
behaviour was allowed it would result in unexpected behaviour, so

@@ -636,4 +651,8 @@ an error is thrown. **This is invalid**:

var rehype = require('rehype');
var format = require('rehype-format');
// ...
process.stdin.pipe(rehype).pipe(process.stdout);
rehype
.use(format)
// ...
```

@@ -644,15 +663,183 @@

```txt
~/index.js:118
throw new Error(
^
~/node_modules/unified/index.js:436
throw new Error(
^
Error: Cannot pipe into abstract processor.
To make the processor concrete, invoke it: use `processor()` instead of `processor`.
at assertConcrete (~/index.js:118:13)
at Function.<anonymous> (~/index.js:135:7)
...
at Object.<anonymous> (~/b.js:76:15)
...
Error: Cannot invoke `use` on a frozen processor.
Create a new processor first, by invoking it: use `processor()` instead of `processor`.
at assertUnfrozen (~/node_modules/unified/index.js:436:11)
at Function.use (~/node_modules/unified/index.js:170:5)
at Object.<anonymous> (~/b.js:6:4)
```
## `Plugin`
A **unified** plugin changes the way the applied-on processor works,
in the following ways:
* It modifies the [**processor**][processor]: such as changing the
parser, the compiler, or linking the processor to other processors;
* It transforms the [**syntax tree**][node] representation of a file;
* It modifies metadata of a file.
Plug-in’s are a concept which materialise as [**attacher**][attacher]s.
###### Example
`move.js`:
```js
module.exports = move;
function move(options) {
var expected = (options || {}).extname;
if (!expected) {
throw new Error('Missing `extname` in options');
}
return transformer;
function transformer(tree, file) {
if (file.extname && file.extname !== expected) {
file.extname = expected;
}
}
}
```
`index.js`:
```js
var unified = require('unified');
var parse = require('remark-parse');
var remark2rehype = require('remark-rehype');
var stringify = require('rehype-stringify');
var vfile = require('to-vfile');
var reporter = require('vfile-reporter');
var move = require('./move');
rehype()
.use(parse)
.use(remark2rehype)
.use(move, {extname: '.html'})
.use(stringify)
.process(vfile.readSync('index.md'), function (err, file) {
console.error(reporter(err || file));
if (file) {
vfile.writeSync(file); // written to `index.html`
}
})
```
### `function attacher([options])`
An attacher is the thing passed to [`use`][use]. It configures the
processor and in turn can receive options.
Attachers can configure processors, such as by interacting with parsers
and compilers, linking them to other processors, or by specifying how
the syntax tree is handled.
###### Context
The context object is set to the invoked on [`processor`][processor].
###### Parameters
* `options` (`*`, optional) — Configuration.
###### Returns
[`transformer`][transformer] — Optional.
###### Note
Attachers are invoked when the processor is [frozen][freeze]: either when
`.freeze()` is called explicitly, or when [`.parse()`][parse], [`.run()`][run],
[`.stringify()`][stringify], or [`.process()`][process] is called for the first
time.
### `function transformer(node, file[, next])`
Transformers modify the syntax tree or metadata of a file.
A transformer is a function which is invoked each time a file is
passed through the transform phase. If an error occurs (either
because it’s thrown, returned, rejected, or passed to [`next`][next]),
the process stops.
The transformation process in **unified** is handled by [`trough`][trough],
see it’s documentation for the exact semantics of transformers.
###### Parameters
* `node` ([**Node**][node]);
* `file` ([**VFile**][file]);
* `next` ([`Function`][next], optional).
###### Returns
* `Error` — Can be returned to stop the process;
* [**Node**][node] — Can be returned and results in further
transformations and `stringify`s to be performed on the new
tree;
* `Promise` — If a promise is returned, the function is asynchronous,
and **must** be resolved (optionally with a [**Node**][node]) or
rejected (optionally with an `Error`).
#### `function next(err[, tree[, file]])`
If the signature of a transformer includes `next` (third argument),
the function **may** finish asynchronous, and **must** invoke `next()`.
###### Parameters
* `err` (`Error`, optional) — Stop the process;
* `node` ([**Node**][node], optional) — New syntax tree;
* `file` ([**VFile**][file], optional) — New virtual file.
## `Preset`
A **unified** preset provides a potentially sharable way to configure
processors. They can contain multiple plugins and optionally settings as
well.
###### Example
`preset.js`:
```js
exports.settings = {bullet: '*', fences: true}
exports.plugins = [
[require('remark-preset-lint-recommended')]
[require('remark-comment-config')],
[require('remark-validate-links')],
[require('remark-lint'), {
blockquoteIndentation: 2,
checkboxCharacterStyle: {checked: 'x', unchecked: ' '},
// ...
}],
[require('remark-toc'), {maxDepth: 3, tight: true}]
[require('remark-github')]
]
```
`index.js`:
```js
var remark = require('remark');
var vfile = require('to-vfile');
var reporter = require('vfile-reporter');
var preset = require('./preset');
remark().use(preset).process(vfile.readSync('index.md'), function (err, file) {
console.error(reporter(err || file));
if (file) {
vfile.writeSync(file);
}
})
```
## License

@@ -664,14 +851,12 @@

[travis-badge]: https://img.shields.io/travis/wooorm/unified.svg
[travis-badge]: https://img.shields.io/travis/unifiedjs/unified.svg
[travis]: https://travis-ci.org/wooorm/unified
[travis]: https://travis-ci.org/unifiedjs/unified
[codecov-badge]: https://img.shields.io/codecov/c/github/wooorm/unified.svg
[codecov-badge]: https://img.shields.io/codecov/c/github/unifiedjs/unified.svg
[codecov]: https://codecov.io/github/wooorm/unified
[codecov]: https://codecov.io/github/unifiedjs/unified
[npm-install]: https://docs.npmjs.com/cli/install
[npm]: https://docs.npmjs.com/cli/install
[releases]: https://github.com/wooorm/unified/releases
[license]: LICENSE

@@ -687,17 +872,17 @@

[hast]: https://github.com/wooorm/hast
[hast]: https://github.com/syntax-tree/hast
[mdast]: https://github.com/wooorm/mdast
[mdast]: https://github.com/syntax-tree/mdast
[nlcst]: https://github.com/wooorm/nlcst
[nlcst]: https://github.com/syntax-tree/nlcst
[unist]: https://github.com/wooorm/unist
[unist]: https://github.com/syntax-tree/unist
[engine]: https://github.com/wooorm/unified-engine
[engine]: https://github.com/unifiedjs/unified-engine
[args]: https://github.com/wooorm/unified-args
[args]: https://github.com/unifiedjs/unified-args
[gulp]: https://github.com/wooorm/unified-engine-gulp
[gulp]: https://github.com/unifiedjs/unified-engine-gulp
[atom]: https://github.com/wooorm/unified-engine-atom
[atom]: https://github.com/unifiedjs/unified-engine-atom

@@ -710,18 +895,12 @@ [remark-rehype]: https://github.com/wooorm/remark-rehype

[unist-utilities]: https://github.com/wooorm/unist#list-of-utilities
[rehype-remark]: https://github.com/wooorm/rehype-remark
[vfile]: https://github.com/wooorm/vfile
[unist-utilities]: https://github.com/syntax-tree/unist#list-of-utilities
[vfile-contents]: https://github.com/wooorm/vfile#vfilecontents
[vfile]: https://github.com/vfile/vfile
[vfile-utilities]: https://github.com/wooorm/vfile#related-tools
[vfile-contents]: https://github.com/vfile/vfile#vfilecontents
[vfilemessage]: https://github.com/wooorm/vfile#vfilemessage
[vfile-utilities]: https://github.com/vfile/vfile#related-tools
[writable-stream]: https://nodejs.org/api/stream.html#stream_class_stream_writable_1
[stream-pipe]: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
[buffer]: https://nodejs.org/api/buffer.html#buffer_buffer
[file]: #file

@@ -733,10 +912,12 @@

[process]: #processorprocessfilevalue-options-done
[process]: #processorprocessfilevalue-done
[parse]: #processorparsefilevalue-options
[parse]: #processorparsefilevalue
[parser]: #processorparser
[stringify]: #processorstringifynode-file-options
[stringify]: #processorstringifynode-file
[run]: #processorrunnode-file-done
[compiler]: #processorcompiler

@@ -746,3 +927,3 @@

[attacher]: #function-attacherprocessor-options
[attacher]: #function-attacheroptions

@@ -753,3 +934,3 @@ [transformer]: #function-transformernode-file-next

[abstract]: #processorabstract
[freeze]: #processorfreeze

@@ -762,6 +943,4 @@ [plugin]: #plugin

[write]: #processorwritechunk-encoding-callback
[trough]: https://github.com/wooorm/trough#function-fninput-next
[pipe]: #processorpipestream-options
[trough]: https://github.com/wooorm/trough#function-fninput-next
[promise]: https://developer.mozilla.org/Web/JavaScript/Reference/Global_Objects/Promise
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc