Comparing version 0.2.0-rc.1 to 0.2.0-rc.2
'use strict'; | ||
var Retext, sentence, paragraph, section, article; | ||
var Retext, | ||
sentence, | ||
paragraph, | ||
section, | ||
article; | ||
/** | ||
* Module dependencies (retext). | ||
*/ | ||
Retext = require('..'); | ||
/* Test data */ | ||
/** | ||
* Test data. | ||
* | ||
* This includes: | ||
* | ||
* - An average sentence (w/ 20 words); | ||
* - An average paragraph (w/ 5 sentences); | ||
* - A (big?) section (w/ 10 paragraphs); | ||
* - A (big?) article (w/ 10 sections); | ||
* | ||
* Source: | ||
* http://www.gutenberg.org/files/10745/10745-h/10745-h.htm | ||
*/ | ||
/* Source: http://www.gutenberg.org/files/10745/10745-h/10745-h.htm */ | ||
/* A sentence, 20 words. */ | ||
sentence = 'Where she had stood was clear, and she was gone since Sir ' + | ||
'Kay does not choose to assume my quarrel.'; | ||
/* A paragraph, 5 sentences, 100 words. */ | ||
paragraph = 'Thou art a churlish knight to so affront a lady ' + | ||
@@ -26,14 +42,21 @@ 'he could not sit upon his horse any longer. ' + | ||
/* A section, 10 paragraphs, 50 sentences, 1,000 words. */ | ||
section = paragraph + Array(10).join('\n\n' + paragraph); | ||
/* An article, 100 paragraphs, 500 sentences, 10,000 words. */ | ||
article = section + Array(10).join('\n\n' + section); | ||
/* Benchmarks */ | ||
/** | ||
* Benchmark suite. | ||
*/ | ||
suite('retext.parse(source);', function () { | ||
var retext = new Retext(); | ||
var retext; | ||
retext = new Retext(); | ||
set('mintime', 100); | ||
/** | ||
* Benchmark a paragraph. | ||
*/ | ||
bench('A paragraph (5 sentences, 100 words)', function () { | ||
@@ -43,2 +66,6 @@ retext.parse(paragraph); | ||
/** | ||
* Benchmark a section. | ||
*/ | ||
bench('A section (10 paragraphs, 50 sentences, 1,000 words)', | ||
@@ -50,2 +77,6 @@ function () { | ||
/** | ||
* Benchmark an article. | ||
*/ | ||
bench('An article (100 paragraphs, 500 sentences, 10,000 words)', | ||
@@ -52,0 +83,0 @@ function () { |
@@ -5,3 +5,3 @@ { | ||
"description": "Extensible system for analysing and manipulating natural language", | ||
"version": "0.2.0-rc.1", | ||
"version": "0.2.0-rc.2", | ||
"keywords": [ | ||
@@ -8,0 +8,0 @@ "natural", |
0.2.0-rc.2 / 2014-09-21 | ||
================== | ||
* Merge branch 'feature/rename-apply-plugins-to-run' | ||
* Rename `applyPlugins` > `run` | ||
* Merge branch 'refactor/api' | ||
* Refactor API | ||
* Merge branch 'feature/fail-without-callback' | ||
* Add interface for error on omitted `done` for `parse` | ||
* Add spec for error on omitted `done` for `parse` | ||
* Merge branch 'feature/chainable-parse' | ||
* Add chainable `parse` and `applyPlugins` | ||
* Add spec for chainable `parse` and `applyPlugins` | ||
* Fix throwing interdependent plugins | ||
* Add spec for throwing interdependent plugins | ||
* Refactor spec | ||
* Refactor benchmark | ||
* Update benchmark results in docs | ||
* Update docs for async changes in 27dbfd7 | ||
* Remove flashy demos from docs | ||
* Update and fix Installation in docs | ||
* Remove sauce labs from travis | ||
* Merge branch 'feature/async' | ||
0.2.0-rc.1 / 2014-09-19 | ||
@@ -3,0 +27,0 @@ ================== |
231
index.js
'use strict'; | ||
var TextOMConstructor = require('textom'), | ||
ParseLatin = require('parse-latin'), | ||
Ware = require('ware'); | ||
var TextOMConstructor, | ||
ParseLatin, | ||
Ware, | ||
has; | ||
function fromAST(TextOM, ast) { | ||
var iterator = -1, | ||
children, node, data, attribute; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
node = new TextOM[ast.type](); | ||
TextOMConstructor = require('textom'); | ||
ParseLatin = require('parse-latin'); | ||
Ware = require('ware'); | ||
if ('children' in ast) { | ||
iterator = -1; | ||
children = ast.children; | ||
/** | ||
* Cached, fast, secure existence test. | ||
*/ | ||
while (children[++iterator]) { | ||
node.append(fromAST(TextOM, children[iterator])); | ||
has = Object.prototype.hasOwnProperty; | ||
/** | ||
* Transform a concrete syntax tree into a tree constructed | ||
* from a given object model. | ||
* | ||
* @param {Object} TextOM - the object model. | ||
* @param {Object} cst - the concrete syntax tree to | ||
* transform. | ||
* @return {Node} the node constructed from the | ||
* CST and the object model. | ||
*/ | ||
function fromCST(TextOM, cst) { | ||
var index, | ||
node, | ||
children, | ||
data, | ||
attribute; | ||
node = new TextOM[cst.type](); | ||
if ('children' in cst) { | ||
index = -1; | ||
children = cst.children; | ||
while (children[++index]) { | ||
node.append(fromCST(TextOM, children[index])); | ||
} | ||
} else { | ||
node.fromString(ast.value); | ||
node.fromString(cst.value); | ||
} | ||
/** | ||
* Currently, `data` properties are not really | ||
* specified or documented. Therefore, the following | ||
* branch is ignored by Istanbul. | ||
* | ||
* The idea is that plugins and parsers can each | ||
* attach data to nodes, in a similar fashion to the | ||
* DOMs dataset, which can be stringified and parsed | ||
* back and forth between the concrete syntax tree | ||
* and the node. | ||
*/ | ||
/* istanbul ignore if: TODO, Untestable, will change soon. */ | ||
if ('data' in ast) { | ||
data = ast.data; | ||
if ('data' in cst) { | ||
data = cst.data; | ||
for (attribute in data) { | ||
if (data.hasOwnProperty(attribute)) { | ||
if (has.call(data, attribute)) { | ||
node.data[attribute] = data[attribute]; | ||
@@ -39,11 +80,12 @@ } | ||
/** | ||
* Define `Retext`. Exported above, and used to instantiate a new | ||
* `Retext`. | ||
* Construct an instance of `Retext`. | ||
* | ||
* @param {Function?} parser - the parser to use. Defaults to parse-latin. | ||
* @public | ||
* @param {Function?} parser - the parser to use. Defaults | ||
* to a new instance of `parse-latin`. | ||
* @constructor | ||
*/ | ||
function Retext(parser) { | ||
var self = this; | ||
var self, | ||
TextOM; | ||
@@ -54,40 +96,62 @@ if (!parser) { | ||
self = this; | ||
TextOM = new TextOMConstructor(); | ||
self.ware = new Ware(); | ||
self.parser = parser; | ||
self.TextOM = parser.TextOM = new TextOMConstructor(); | ||
self.TextOM.parser = parser; | ||
self.TextOM = TextOM; | ||
/** | ||
* Expose `TextOM` on `parser`, and vice versa. | ||
*/ | ||
parser.TextOM = TextOM; | ||
TextOM.parser = parser; | ||
} | ||
/** | ||
* `Retext#use` takes a plugin-a humble function-and when the parse | ||
* method of the Retext instance is called, the plugin will be called | ||
* with the parsed tree, and the retext instance as arguments. | ||
* Attaches `plugin`: a humble function. | ||
* | ||
* Note that, during the parsing stage, when the `use` method is called | ||
* by a plugin, the nested plugin is immediately called, before continuing | ||
* on with its parent plugin. | ||
* When `parse` or `run` is invoked, `plugin` is | ||
* invoked with `node` and a `retext` instance. | ||
* | ||
* @param {Function} plugin - the plugin to call when parsing. | ||
* @param {Function?} plugin.attach - called only once with a Retext | ||
* instance. If you're planning on | ||
* modifying TextOM or a parser, do it | ||
* in this method. | ||
* If `plugin` contains asynchronous functionality, it | ||
* should accept a third argument (`next`) and invoke | ||
* it on completion. | ||
* | ||
* `plugin.attach` is invoked with a `retext` instance | ||
* when attached, enabling `plugin` to depend on other | ||
* plugins. | ||
* | ||
* Code to initialize `plugin` should go into its `attach` | ||
* method, such as functionality to modify the object model | ||
* (TextOM), the parser (e.g., `parse-latin`), or the | ||
* `retext` instance. `plugin.attach` is invoked when | ||
* `plugin` is attached to a `retext` instance. | ||
* | ||
* @param {function(Node, Retext, Function?)} plugin - | ||
* functionality to analyze and manipulate a node. | ||
* @param {function(Retext)} plugin.attach - functionality | ||
* to initialize `plugin`. | ||
* @return this | ||
* @public | ||
*/ | ||
Retext.prototype.use = function (plugin) { | ||
var self; | ||
if (typeof plugin !== 'function') { | ||
throw new TypeError('Illegal invocation: \'' + plugin + | ||
'\' is not a valid argument for \'Retext.prototype.use\''); | ||
throw new TypeError( | ||
'Illegal invocation: `' + plugin + '` ' + | ||
'is not a valid argument for `Retext#use(plugin)`' | ||
); | ||
} | ||
var self = this, | ||
ware = self.ware; | ||
self = this; | ||
if (ware.fns.indexOf(plugin) === -1) { | ||
if (self.ware.fns.indexOf(plugin) === -1) { | ||
self.ware.use(plugin); | ||
if (plugin.attach) { | ||
plugin.attach(self); | ||
} | ||
ware.use(plugin); | ||
} | ||
@@ -99,40 +163,71 @@ | ||
/** | ||
* `Retext#parse` takes a source to be given (and parsed) by the parser. | ||
* Then, `parse` iterates over all plugins, and allows them to modify the | ||
* TextOM tree created by the parser. | ||
* Transform a given value into a node, applies attached | ||
* plugins to the node, and invokes `done` with either an | ||
* error (first argument) or the transformed node (second | ||
* argument). | ||
* | ||
* @param {String?} source - The source to convert. | ||
* @param {Function<Error, Node>} done - Callback with a RootNode containing | ||
* the tokenized source. | ||
* @public | ||
* @param {string?} value - The value to transform. | ||
* @param {function(Error, Node)} done - Callback to | ||
* invoke when the transformations have completed. | ||
* @return this | ||
*/ | ||
Retext.prototype.parse = function (source, done) { | ||
var self = this, | ||
rootNode = fromAST(self.TextOM, self.parser.tokenizeRoot(source)); | ||
self.applyPlugins(rootNode, done); | ||
Retext.prototype.parse = function (value, done) { | ||
var self, | ||
cst; | ||
if (typeof done !== 'function') { | ||
throw new TypeError( | ||
'Illegal invocation: `' + done + '` ' + | ||
'is not a valid argument for `Retext#parse(value, done)`.\n' + | ||
'This breaking change occurred in 0.2.0-rc.1, see GitHub for ' + | ||
'more information.' | ||
); | ||
} | ||
self = this; | ||
cst = self.parser.parse(value); | ||
self.run(fromCST(self.TextOM, cst), done); | ||
return self; | ||
}; | ||
/** | ||
* `Retext#applyPlugins` applies the plugins bound to the retext instance to a | ||
* given tree. | ||
* Applies attached plugins to `node` and invokes `done` | ||
* with either an error (first argument) or the transformed | ||
* `node` (second argument). | ||
* | ||
* Note that, during the parsing stage, when the `use` plugin is called | ||
* by a plugin, the nested plugin is immediately called, before continuing | ||
* on with its parent plugin. | ||
* | ||
* @param {Node} tree - The tree to apply plugins to. | ||
* @param {Function<Error, Node>} done - Callback with the result of | ||
* parsing the tree. | ||
* @public | ||
* @param {Node} node - The node to apply attached | ||
* plugins to. | ||
* @param {function(Error, Node)} done - Callback to | ||
* invoke when the transformations have completed. | ||
* @return this | ||
*/ | ||
Retext.prototype.applyPlugins = function (tree, done) { | ||
var self = this; | ||
self.ware.run(tree, self, done); | ||
Retext.prototype.run = function (node, done) { | ||
var self; | ||
if (typeof done !== 'function') { | ||
throw new TypeError( | ||
'Illegal invocation: `' + done + '` ' + | ||
'is not a valid argument for ' + | ||
'`Retext#run(node, done)`.\n' + | ||
'This breaking change occurred in 0.2.0-rc.1, see GitHub for ' + | ||
'more information.' | ||
); | ||
} | ||
self = this; | ||
self.ware.run(node, self, done); | ||
return self; | ||
}; | ||
/** | ||
* Expose `Retext`. Used to instantiate a new Retext object. | ||
* Expose `Retext`. | ||
*/ | ||
exports = module.exports = Retext; | ||
module.exports = Retext; |
{ | ||
"name": "retext", | ||
"version": "0.2.0-rc.1", | ||
"version": "0.2.0-rc.2", | ||
"description": "Extensible system for analysing and manipulating natural language", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
139
Readme.md
@@ -23,3 +23,3 @@ # ![Retext logo](http://i58.tinypic.com/5xpx5z.png) | ||
NPM: | ||
npm: | ||
```sh | ||
@@ -29,3 +29,3 @@ $ npm install retext | ||
Component.js: | ||
Component: | ||
```sh | ||
@@ -35,45 +35,77 @@ $ component install wooorm/retext | ||
## Flashy demos | ||
Bower: | ||
```sh | ||
$ bower install retext | ||
``` | ||
Most [plugins](https://github.com/wooorm/retext#plugins) have working examples. All available demos are listed under the plugins section, but some of the coolest include: | ||
- [retext-smartypants](http://wooorm.github.io/retext-smartypants/); | ||
- [retext-emoji](http://wooorm.github.io/retext-emoji/); | ||
- [retext-double-metaphone](http://wooorm.github.io/retext-double-metaphone/); | ||
- [retext-search](http://wooorm.github.io/retext-search/); | ||
- [retext-porter-stemmer](http://wooorm.github.io/retext-porter-stemmer/); | ||
## Usage | ||
```js | ||
var Retext = require('retext'), | ||
emoji = require('retext-emoji'), | ||
smartypants = require('retext-smartypants'), | ||
var Retext, | ||
retext, | ||
emoji, | ||
smartypants, | ||
input; | ||
// Modified first paragraph from: | ||
// http://en.wikipedia.org/wiki/Three_wise_monkeys | ||
/** | ||
* Dependencies. | ||
* | ||
* - retext-emoji transforms short-codes into emoji. | ||
* - retext-smartypants transforms dumb quotes and such | ||
* into smart punctuation. | ||
*/ | ||
Retext = require('retext'); | ||
emoji = require('retext-emoji'); | ||
smartypants = require('retext-smartypants'); | ||
/** | ||
* The source to analyse and manipulate, a (modified) | ||
* first paragraph from WikiPedia: | ||
* http://en.wikipedia.org/wiki/Three_wise_monkeys | ||
*/ | ||
input = 'The three wise monkeys [. . .] sometimes called the ' + | ||
'three mystic apes--are a pictorial maxim. Together ' + | ||
'they embody the proverbial principle to ("see no evil, ' + | ||
'hear no evil, speak no evil"). The three monkeys are ' + | ||
'Mizaru (:see_no_evil:), covering his eyes, who sees no ' + | ||
'evil; Kikazaru (:hear_no_evil:), covering his ears, ' + | ||
'who hears no evil; and Iwazaru (:speak_no_evil:), ' + | ||
'covering his mouth, who speaks no evil.'; | ||
'three mystic apes--are a pictorial maxim. Together ' + | ||
'they embody the proverbial principle to ("see no evil, ' + | ||
'hear no evil, speak no evil"). The three monkeys are ' + | ||
'Mizaru (:see_no_evil:), covering his eyes, who sees no ' + | ||
'evil; Kikazaru (:hear_no_evil:), covering his ears, ' + | ||
'who hears no evil; and Iwazaru (:speak_no_evil:), ' + | ||
'covering his mouth, who speaks no evil.'; | ||
var text = new Retext() | ||
/** | ||
* Create a retext instance which uses retext-emoji and -smartypants. | ||
*/ | ||
retext = new Retext() | ||
.use(emoji({ | ||
'convert' : 'encode' | ||
})) | ||
.use(smartypants()) | ||
.parse(input) | ||
.toString(); | ||
// The three wise monkeys […] sometimes called the three | ||
// mystic apes—are a pictorial maxim. Together they | ||
// embody the proverbial principle to (“see no evil, | ||
// hear no evil, speak no evil”). The three monkeys are | ||
// Mizaru (🙈), covering his eyes, who sees no evil; | ||
// Kikazaru (🙉), covering his ears, who hears no evil; | ||
// and Iwazaru (🙊), covering his mouth, who speaks no evil. | ||
.use(smartypants()); | ||
retext.parse(input, function (err, tree) { | ||
/** | ||
* Handle errors. | ||
*/ | ||
if (err) { | ||
throw err; | ||
} | ||
/** | ||
* Log the text content of the tree (the transformed input). | ||
* | ||
* This logs the following: | ||
* The three wise monkeys […] sometimes called the three | ||
* mystic apes—are a pictorial maxim. Together they | ||
* embody the proverbial principle to (“see no evil, | ||
* hear no evil, speak no evil”). The three monkeys are | ||
* Mizaru (🙈), covering his eyes, who sees no evil; | ||
* Kikazaru (🙉), covering his ears, who hears no evil; | ||
* and Iwazaru (🙊), covering his mouth, who speaks no evil. | ||
*/ | ||
console.log(tree.toString()); | ||
}); | ||
``` | ||
@@ -87,9 +119,28 @@ | ||
```js | ||
var Retext = require('retext'), | ||
ParseEnglish = require('parse-english'); | ||
var Retext, | ||
ParseEnglish, | ||
retext; | ||
var retext = new Retext(new ParseEnglish()).parse(/* ...some english... */); | ||
/** | ||
* parse-english works better on English input. | ||
*/ | ||
Retext = require('./'); | ||
ParseEnglish = require('parse-english'); | ||
/** | ||
* Create a retext instance which uses an instance | ||
* of parse-english. | ||
*/ | ||
retext = new Retext(new ParseEnglish()); | ||
/** | ||
* There, ol' chap. | ||
*/ | ||
retext.parse(/* ...some English... */, function (err, tree) {/* ... */}); | ||
``` | ||
Return a new `Retext` instance with the given parser (defaults to parse-latin). | ||
Return a new `Retext` instance with the given [parser](#parsers) (defaults to an instance of parse-latin). | ||
@@ -100,5 +151,5 @@ ### Retext.prototype.use(plugin) | ||
### Retext.prototype.parse(source) | ||
### Retext.prototype.parse(source, callback) | ||
Parses the given source and returns the (by `use`d plugins, modified) tree. | ||
Parses the given source and when done passes either an error (the first argument), or the (by `use`d plugins, modified) tree (the second argument) to the callback. | ||
@@ -157,9 +208,9 @@ ## Plugins | ||
On a MacBook Air, it parses about 2 big articles, 24 sections, or 218 paragraphs per second. | ||
On a MacBook Air, it parses about 2 big articles, 22 sections, or 206 paragraphs per second. | ||
``` | ||
retext.parse(source); | ||
218 op/s » A paragraph (5 sentences, 100 words) | ||
24 op/s » A section (10 paragraphs, 50 sentences, 1,000 words) | ||
2 op/s » An article (100 paragraphs, 500 sentences, 10,000 words) | ||
206 op/s » A paragraph (5 sentences, 100 words) | ||
22 op/s » A section (10 paragraphs, 50 sentences, 1,000 words) | ||
2 op/s » An article (100 paragraphs, 500 sentences, 10,000 words) | ||
``` | ||
@@ -166,0 +217,0 @@ |
'use strict'; | ||
var Retext = require('..'), | ||
assert = require('assert'); | ||
var Retext, | ||
assert; | ||
/* istanbul ignore next: noop */ | ||
/** | ||
* Module dependencies (retext, assert). | ||
*/ | ||
Retext = require('..'); | ||
assert = require('assert'); | ||
/** | ||
* Cache a (by istanbul ignored) no-operation function. | ||
*/ | ||
/* istanbul ignore next */ | ||
function noop() {} | ||
describe('Retext()', function () { | ||
it('should be of type `function`', function () { | ||
/** | ||
* Test `Retext`. | ||
*/ | ||
describe('new Retext()', function () { | ||
it('should be a `function`', function () { | ||
assert(typeof Retext === 'function'); | ||
}); | ||
it('should return a newly initialized `Retext` object, when invoked ' + | ||
'without arguments', function () { | ||
assert(new Retext() instanceof Retext); | ||
} | ||
); | ||
it('should return a newly initialized `Retext` object', function () { | ||
assert(new Retext(noop) instanceof Retext); | ||
assert(new Retext() instanceof Retext); | ||
}); | ||
it('should set the `parser` attribute to `parse-latin`, when invoked ' + | ||
'without arguments', function () { | ||
var retext = new Retext(); | ||
assert('parser' in retext); | ||
assert('TextOM' in retext.parser); | ||
} | ||
); | ||
it('should set `parser` to an instance of parse-latin', function () { | ||
var retext; | ||
it('should set the `parser` attribute to the passed in parser, ' + | ||
'when given', function () { | ||
var retext = new Retext(noop); | ||
assert('parser' in retext); | ||
assert(retext.parser === noop); | ||
} | ||
); | ||
retext = new Retext(); | ||
it('should create a new context/parser/textom when required, thus ' + | ||
'not requiring from memory', function (done) { | ||
new Retext().parse(null, function (err, rootNode1) { | ||
/* istanbul ignore if: won't error. */ | ||
if (err) { | ||
done(err); | ||
} | ||
assert('parser' in retext); | ||
assert('TextOM' in retext.parser); | ||
}); | ||
new Retext().parse(null, function (err, rootNode2) { | ||
assert(rootNode1 instanceof rootNode1.constructor); | ||
assert(!(rootNode1 instanceof rootNode2.constructor)); | ||
assert(rootNode2 instanceof rootNode2.constructor); | ||
assert(!(rootNode2 instanceof rootNode1.constructor)); | ||
/** | ||
* The following sounds a bit weird, but can best be thought of as | ||
* a test for multiple contexts, similar to how different frames on | ||
* the client side use different Array constructors. | ||
*/ | ||
done(err); | ||
}); | ||
it('should create a new object model', function (done) { | ||
new Retext().parse(null, function (err, tree1) { | ||
/* istanbul ignore if: should not error. */ | ||
if (err) { | ||
throw err; | ||
} | ||
new Retext().parse(null, function (err, tree2) { | ||
assert(tree1 instanceof tree1.constructor); | ||
assert(!(tree1 instanceof tree2.constructor)); | ||
assert(tree2 instanceof tree2.constructor); | ||
assert(!(tree2 instanceof tree1.constructor)); | ||
done(err); | ||
}); | ||
} | ||
); | ||
}); | ||
}); | ||
}); | ||
describe('Retext#use', function () { | ||
it('should be of type `function`', function () { | ||
/** | ||
* Test `Retext` when given a parser. | ||
*/ | ||
describe('new Retext(parser)', function () { | ||
it('should set `parser` to the given parser', function () { | ||
var retext; | ||
retext = new Retext(noop); | ||
assert('parser' in retext); | ||
assert(retext.parser === noop); | ||
}); | ||
}); | ||
/** | ||
* Test `Retext#use(plugin)`. | ||
*/ | ||
describe('Retext#use(plugin)', function () { | ||
it('should be a `function`', function () { | ||
assert(typeof Retext.prototype.use === 'function'); | ||
@@ -64,44 +94,179 @@ assert(typeof (new Retext()).use === 'function'); | ||
it('should return self', function () { | ||
var retext = new Retext(); | ||
var retext; | ||
retext = new Retext(); | ||
assert(retext.use(noop) === retext); | ||
}); | ||
it('should throw, when something other than a function was given', | ||
function () { | ||
var retext = new Retext(); | ||
it('should throw when not given a function', function () { | ||
var retext; | ||
assert.throws(function () { | ||
retext.use(); | ||
}, 'undefined'); | ||
retext = new Retext(); | ||
assert.throws(function () { | ||
retext.use(null); | ||
}, 'null'); | ||
assert.throws(function () { | ||
retext.use(); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.use(undefined); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.use(null); | ||
}, 'null'); | ||
assert.throws(function () { | ||
retext.use(true); | ||
}, 'true'); | ||
assert.throws(function () { | ||
retext.use(undefined); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.use({}); | ||
}, 'object Object'); | ||
} | ||
); | ||
assert.throws(function () { | ||
retext.use(true); | ||
}, 'true'); | ||
it('should attach `use`d plugins', function () { | ||
var retext = new Retext(); | ||
assert.throws(function () { | ||
retext.use({}); | ||
}, 'object Object'); | ||
}); | ||
it('should attach a plugin', function () { | ||
var retext; | ||
retext = new Retext(); | ||
assert(retext.ware.fns.length === 0); | ||
retext.use(noop); | ||
assert(retext.ware.fns.length === 1); | ||
}); | ||
it('should not attach `use`d plugins multiple times', function () { | ||
var retext = new Retext(); | ||
it('should invoke `attach` on an attached plugin', function () { | ||
var retext, | ||
isInvoked; | ||
/* istanbul ignore next: noop */ | ||
function plugin() {} | ||
retext = new Retext(); | ||
plugin.attach = function () { | ||
isInvoked = true; | ||
}; | ||
retext.use(plugin); | ||
assert(isInvoked === true); | ||
}); | ||
it('should invoke `attach` with `retext`', function (done) { | ||
var retext, | ||
root, | ||
args; | ||
function plugin() {} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
plugin.attach = function () { | ||
args = arguments; | ||
}; | ||
retext.use(plugin); | ||
retext.run(root, function (err) { | ||
assert(args[0] === retext); | ||
assert(args.length === 1); | ||
done(err); | ||
}); | ||
}); | ||
it('should invoke `attach` on plugins in order', function (done) { | ||
var retext, | ||
isInvoked; | ||
function firstPlugin() {} | ||
function secondPlugin() {} | ||
retext = new Retext(); | ||
isInvoked = false; | ||
firstPlugin.attach = function () { | ||
assert(isInvoked === false); | ||
isInvoked = true; | ||
}; | ||
secondPlugin.attach = function () { | ||
assert(isInvoked === true); | ||
isInvoked = true; | ||
}; | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin) | ||
.parse(null, done); | ||
}); | ||
it('should invoke `attach` on dependencies in order', function (done) { | ||
var retext, | ||
invokeCount; | ||
function firstPlugin() {} | ||
function secondPlugin() {} | ||
function thirdPlugin() {} | ||
retext = new Retext(); | ||
invokeCount = 0; | ||
thirdPlugin.attach = function () { | ||
assert(invokeCount === 2); | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin); | ||
invokeCount++; | ||
}; | ||
secondPlugin.attach = function () { | ||
assert(invokeCount === 1); | ||
invokeCount++; | ||
retext | ||
.use(firstPlugin) | ||
.use(thirdPlugin); | ||
}; | ||
firstPlugin.attach = function () { | ||
assert(invokeCount === 0); | ||
invokeCount++; | ||
retext | ||
.use(secondPlugin) | ||
.use(thirdPlugin); | ||
}; | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin) | ||
.use(thirdPlugin) | ||
.parse(null, done); | ||
}); | ||
it('should not re-attach an attached plugin', function () { | ||
var retext; | ||
retext = new Retext(); | ||
retext.use(noop); | ||
assert(retext.ware.fns.length === 1); | ||
retext.use(noop); | ||
assert(retext.ware.fns.length === 1); | ||
@@ -111,4 +276,8 @@ }); | ||
describe('Retext#parse', function () { | ||
it('should be of type `function`', function () { | ||
/** | ||
* Test Retext#parse(value, done). | ||
*/ | ||
describe('Retext#parse(value, done)', function () { | ||
it('should be a `function`', function () { | ||
assert(typeof Retext.prototype.parse === 'function'); | ||
@@ -118,8 +287,44 @@ assert(typeof (new Retext()).parse === 'function'); | ||
it('should return an instance of RootNode', function (done) { | ||
var retext = new Retext(); | ||
it('should return self', function (done) { | ||
var retext; | ||
retext.parse(null, function (err, rootNode) { | ||
assert(rootNode instanceof retext.parser.TextOM.RootNode); | ||
retext = new Retext(); | ||
assert(retext.parse(null, done) === retext); | ||
}); | ||
it('should throw when `done` is not a `function`', function () { | ||
var retext; | ||
retext = new Retext(); | ||
assert.throws(function () { | ||
retext.parse(null); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.parse(null, null); | ||
}, 'null'); | ||
assert.throws(function () { | ||
retext.parse(null, undefined); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.parse(null, true); | ||
}, 'true'); | ||
assert.throws(function () { | ||
retext.parse(null, {}); | ||
}, 'object Object'); | ||
}); | ||
it('should invoke `done` with a `RootNode`', function (done) { | ||
var retext; | ||
retext = new Retext(); | ||
retext.parse(null, function (err, tree) { | ||
assert(tree instanceof retext.parser.TextOM.RootNode); | ||
done(err); | ||
@@ -129,57 +334,55 @@ }); | ||
it('should immediately call the `attach` method on a plugin, when ' + | ||
'`use` is called', function () { | ||
var retext = new Retext(), | ||
isCalled = false; | ||
it('should transform `value` into a `TextOM` object', function (done) { | ||
new Retext().parse('Something something', function (err, root) { | ||
assert('head' in root); | ||
assert('tail' in root); | ||
assert(root.head.parent === root); | ||
assert('TextOM' in root); | ||
assert(root.toString() === 'Something something'); | ||
/* istanbul ignore next: noop */ | ||
function plugin () {} | ||
done(err); | ||
}); | ||
}); | ||
plugin.attach = function () { | ||
isCalled = true; | ||
}; | ||
it('should not invoke `attach` on an attached plugin', function (done) { | ||
var retext, | ||
isInvoked; | ||
retext.use(plugin); | ||
function plugin() {} | ||
assert(isCalled === true); | ||
} | ||
); | ||
retext = new Retext(); | ||
it('should not call the `attach` method, when `parse` is called', | ||
function (done) { | ||
var retext = new Retext(), | ||
isCalled = false; | ||
plugin.attach = function () { | ||
isInvoked = true; | ||
}; | ||
function plugin () {} | ||
retext.use(plugin); | ||
plugin.attach = function () { | ||
isCalled = true; | ||
}; | ||
assert(isInvoked === true); | ||
retext.use(plugin); | ||
isInvoked = false; | ||
isCalled = false; | ||
retext.parse(null, function (err) { | ||
assert(isInvoked !== true); | ||
retext.parse(null, function (err) { | ||
assert(isCalled === false); | ||
done(err); | ||
}); | ||
}); | ||
done(err); | ||
}); | ||
it('should invoke an attached plugin', function (done) { | ||
var retext, | ||
isInvoked; | ||
function plugin() { | ||
isInvoked = true; | ||
} | ||
); | ||
it('should call `use`d plugins, when `parse` is called', function (done) { | ||
var retext = new Retext(), | ||
isCalled = false; | ||
retext = new Retext(); | ||
function plugin () { | ||
isCalled = true; | ||
} | ||
retext.use(plugin); | ||
assert(isCalled === false); | ||
assert(isInvoked !== true); | ||
retext.parse(null, function (err) { | ||
assert(isCalled === true); | ||
assert(isInvoked === true); | ||
@@ -190,11 +393,13 @@ done(err); | ||
it('should call `use`d plugins with an instance of RootNode and ' + | ||
'Retext, when `parse` is called', function (done) { | ||
var retext = new Retext(), | ||
it('should invoke an attached plugin with a `RootNode` and retext', | ||
function (done) { | ||
var retext, | ||
args; | ||
function plugin () { | ||
function plugin() { | ||
args = arguments; | ||
} | ||
retext = new Retext(); | ||
retext.use(plugin); | ||
@@ -212,35 +417,381 @@ | ||
it('should not call (during parsing) `use`d plugins, when already used', | ||
function (done) { | ||
var retext = new Retext(); | ||
it('should invoke attached plugins in order', function (done) { | ||
var retext, | ||
isInvoked; | ||
function nestedPlugin () {} | ||
function firstPlugin() { | ||
assert(isInvoked === false); | ||
function plugin (tree, retext) { | ||
var length = retext.ware.fns.length; | ||
retext.use(nestedPlugin); | ||
assert(length === retext.ware.fns.length); | ||
} | ||
isInvoked = true; | ||
} | ||
retext.use(nestedPlugin).use(plugin).parse(null, done); | ||
function secondPlugin() { | ||
assert(isInvoked === true); | ||
isInvoked = true; | ||
} | ||
); | ||
it('should parse something into a Text Object Model', function (done) { | ||
new Retext().parse('Something something', function (err, root) { | ||
assert('head' in root); | ||
assert('tail' in root); | ||
assert(root.head.parent === root); | ||
assert('TextOM' in root); | ||
retext = new Retext(); | ||
isInvoked = false; | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin) | ||
.parse(null, done); | ||
}); | ||
it('should invoke dependencies in order', function (done) { | ||
var retext, | ||
invokeCount; | ||
/* eslint-disable no-use-before-define */ | ||
function thirdPlugin() { | ||
assert(invokeCount === 2); | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin); | ||
invokeCount++; | ||
} | ||
function secondPlugin() { | ||
assert(invokeCount === 1); | ||
invokeCount++; | ||
retext | ||
.use(firstPlugin) | ||
.use(thirdPlugin); | ||
} | ||
function firstPlugin() { | ||
assert(invokeCount === 0); | ||
invokeCount++; | ||
retext | ||
.use(secondPlugin) | ||
.use(thirdPlugin); | ||
} | ||
/* eslint-enable no-use-before-define */ | ||
retext = new Retext(); | ||
invokeCount = 0; | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin) | ||
.use(thirdPlugin) | ||
.parse(null, done); | ||
}); | ||
it('should not re-invoke an attached plugin', function (done) { | ||
var retext, | ||
isInvoked; | ||
function nestedPlugin() { | ||
assert(isInvoked !== true); | ||
isInvoked = true; | ||
} | ||
function plugin(tree, retext) { | ||
retext.use(nestedPlugin); | ||
} | ||
retext = new Retext(); | ||
retext | ||
.use(nestedPlugin) | ||
.use(plugin) | ||
.parse(null, done); | ||
}); | ||
it('should not re-attach an attached plugin', function (done) { | ||
var retext; | ||
function nestedPlugin() {} | ||
function plugin(tree, retext) { | ||
var length; | ||
length = retext.ware.fns.length; | ||
retext.use(nestedPlugin); | ||
assert(length === retext.ware.fns.length); | ||
} | ||
retext = new Retext(); | ||
retext | ||
.use(nestedPlugin) | ||
.use(plugin) | ||
.parse(null, done); | ||
}); | ||
}); | ||
/** | ||
* Test Retext#run(tree, done). | ||
*/ | ||
describe('Retext#run(tree, done)', function () { | ||
it('should be a `function`', function () { | ||
assert(typeof Retext.prototype.run === 'function'); | ||
assert(typeof (new Retext()).run === 'function'); | ||
}); | ||
it('should return self', function (done) { | ||
var retext, | ||
root; | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
assert(retext.run(root, done) === retext); | ||
}); | ||
it('should throw when `done` is not a `function`', function () { | ||
var retext, | ||
root; | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
assert.throws(function () { | ||
retext.run(root); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.run(root, null); | ||
}, 'null'); | ||
assert.throws(function () { | ||
retext.run(root, undefined); | ||
}, 'undefined'); | ||
assert.throws(function () { | ||
retext.run(root, true); | ||
}, 'true'); | ||
assert.throws(function () { | ||
retext.run(root, {}); | ||
}, 'object Object'); | ||
}); | ||
it('should invoke `done` with `tree`', function (done) { | ||
var retext, | ||
root; | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
retext.run(root, function (err, tree) { | ||
assert(root === tree); | ||
done(err); | ||
}); | ||
}); | ||
}); | ||
describe('Retext#applyPlugins', function () { | ||
it('should be of type `function`', function () { | ||
assert(typeof Retext.prototype.applyPlugins === 'function'); | ||
assert(typeof (new Retext()).applyPlugins === 'function'); | ||
it('should not invoke `attach` on an attached plugin', function (done) { | ||
var retext, | ||
root, | ||
isInvoked; | ||
function plugin() {} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
plugin.attach = function () { | ||
isInvoked = true; | ||
}; | ||
retext.use(plugin); | ||
assert(isInvoked === true); | ||
isInvoked = false; | ||
retext.run(root, function (err) { | ||
assert(isInvoked !== true); | ||
done(err); | ||
}); | ||
}); | ||
it('should invoke an attached plugin', function (done) { | ||
var retext, | ||
root, | ||
isInvoked; | ||
function plugin() { | ||
isInvoked = true; | ||
} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
retext.use(plugin); | ||
assert(isInvoked !== true); | ||
retext.run(root, function (err) { | ||
assert(isInvoked === true); | ||
done(err); | ||
}); | ||
}); | ||
it('should invoke an attached plugin with a `RootNode`', function (done) { | ||
var retext, | ||
root, | ||
args; | ||
function plugin() { | ||
args = arguments; | ||
} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
retext.use(plugin); | ||
retext.run(root, function (err) { | ||
assert(args[0] === root); | ||
assert(args[1] === retext); | ||
assert(args.length === 2); | ||
done(err); | ||
}); | ||
}); | ||
it('should invoke attached plugins in order', function (done) { | ||
var retext, | ||
root, | ||
isInvoked; | ||
function firstPlugin() { | ||
assert(isInvoked === false); | ||
isInvoked = true; | ||
} | ||
function secondPlugin() { | ||
assert(isInvoked === true); | ||
isInvoked = true; | ||
} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
isInvoked = false; | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin) | ||
.run(root, done); | ||
}); | ||
it('should invoke dependencies in order', function (done) { | ||
var retext, | ||
root, | ||
invokeCount; | ||
/* eslint-disable no-use-before-define */ | ||
function thirdPlugin() { | ||
assert(invokeCount === 2); | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin); | ||
invokeCount++; | ||
} | ||
function secondPlugin() { | ||
assert(invokeCount === 1); | ||
invokeCount++; | ||
retext | ||
.use(firstPlugin) | ||
.use(thirdPlugin); | ||
} | ||
function firstPlugin() { | ||
assert(invokeCount === 0); | ||
invokeCount++; | ||
retext | ||
.use(secondPlugin) | ||
.use(thirdPlugin); | ||
} | ||
/* eslint-enable no-use-before-define */ | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
invokeCount = 0; | ||
retext | ||
.use(firstPlugin) | ||
.use(secondPlugin) | ||
.use(thirdPlugin) | ||
.run(root, done); | ||
}); | ||
it('should not re-invoke an attached plugin', function (done) { | ||
var retext, | ||
root, | ||
isInvoked; | ||
function nestedPlugin() { | ||
assert(isInvoked !== true); | ||
isInvoked = true; | ||
} | ||
function plugin(tree, retext) { | ||
retext.use(nestedPlugin); | ||
} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
retext | ||
.use(nestedPlugin) | ||
.use(plugin) | ||
.run(root, done); | ||
}); | ||
it('should not re-attach an attached plugin', function (done) { | ||
var retext, | ||
root; | ||
function nestedPlugin() {} | ||
function plugin(tree, retext) { | ||
var length; | ||
length = retext.ware.fns.length; | ||
retext.use(nestedPlugin); | ||
assert(length === retext.ware.fns.length); | ||
} | ||
retext = new Retext(); | ||
root = new retext.TextOM.RootNode(); | ||
retext | ||
.use(nestedPlugin) | ||
.use(plugin) | ||
.run(root, done); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50714
1015
219
1