diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..519d231 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +coverage/ +nlcst-search.js +nlcst-search.min.js diff --git a/index.js b/index.js index e386a58..2bcc9b4 100644 --- a/index.js +++ b/index.js @@ -1,81 +1,81 @@ -'use strict'; +'use strict' -var visit = require('unist-util-visit'); -var normalize = require('nlcst-normalize'); -var isLiteral = require('nlcst-is-literal'); +var visit = require('unist-util-visit') +var normalize = require('nlcst-normalize') +var isLiteral = require('nlcst-is-literal') -var own = {}.hasOwnProperty; +var own = {}.hasOwnProperty -module.exports = search; +module.exports = search -var C_SPACE = ' '; -var T_WORD = 'WordNode'; -var T_WHITE_SPACE = 'WhiteSpaceNode'; +var C_SPACE = ' ' +var T_WORD = 'WordNode' +var T_WHITE_SPACE = 'WhiteSpaceNode' function search(tree, phrases, handler, options) { - var settings = options || {}; - var apos = settings.allowApostrophes || options; - var dashes = settings.allowDashes || false; - var literals = settings.allowLiterals; - var config = {allowApostrophes: apos, allowDashes: dashes}; - var byWord = {}; - var length; - var index; - var key; - var firstWord; + var settings = options || {} + var apos = settings.allowApostrophes || options + var dashes = settings.allowDashes || false + var literals = settings.allowLiterals + var config = {allowApostrophes: apos, allowDashes: dashes} + var byWord = {} + var length + var index + var key + var firstWord if (!tree || !tree.type) { - throw new Error('Expected node'); + throw new Error('Expected node') } if (typeof phrases !== 'object') { - throw new Error('Expected object for phrases'); + throw new Error('Expected object for phrases') } - length = phrases.length; - index = -1; + length = phrases.length + index = -1 if ('length' in phrases) { while (++index < length) { - handlePhrase(phrases[index]); + handlePhrase(phrases[index]) } } else { for (key in phrases) { - handlePhrase(key); + handlePhrase(key) } } /* Search the tree. */ - visit(tree, T_WORD, visitor); + visit(tree, T_WORD, visitor) /* Test a phrase. */ function test(phrase, position, parent) { - var siblings = parent.children; - var node = siblings[position]; - var count = siblings.length; - var queue = [node]; - var expression = phrase.split(C_SPACE).slice(1); - var length = expression.length; - var index = -1; + var siblings = parent.children + var node = siblings[position] + var count = siblings.length + var queue = [node] + var expression = phrase.split(C_SPACE).slice(1) + var length = expression.length + var index = -1 /* Move one position forward. */ - position++; + position++ /* Iterate over `expression`. */ while (++index < length) { /* Allow joining white-space. */ while (position < count) { - node = siblings[position]; + node = siblings[position] if (node.type !== T_WHITE_SPACE) { - break; + break } - queue.push(node); - position++; + queue.push(node) + position++ } - node = siblings[position]; + node = siblings[position] /* Exit if there are no nodes left, if the * current node is not a word, or if the @@ -86,50 +86,50 @@ function search(tree, phrases, handler, options) { node.type !== T_WORD || normalize(expression[index], config) !== normalize(node, config) ) { - return null; + return null } - queue.push(node); - position++; + queue.push(node) + position++ } - return queue; + return queue } /* Visitor for `WordNode`s. */ function visitor(node, position, parent) { - var word; - var phrases; - var length; - var index; - var result; + var word + var phrases + var length + var index + var result if (!literals && isLiteral(parent, position)) { - return; + return } - word = normalize(node, config); - phrases = own.call(byWord, word) ? byWord[word] : []; - length = phrases.length; - index = -1; + word = normalize(node, config) + phrases = own.call(byWord, word) ? byWord[word] : [] + length = phrases.length + index = -1 while (++index < length) { - result = test(phrases[index], position, parent); + result = test(phrases[index], position, parent) if (result) { - handler(result, position, parent, phrases[index]); + handler(result, position, parent, phrases[index]) } } } /* Handle a phrase. */ function handlePhrase(phrase) { - firstWord = normalize(phrase.split(C_SPACE, 1)[0], config); + firstWord = normalize(phrase.split(C_SPACE, 1)[0], config) if (own.call(byWord, firstWord)) { - byWord[firstWord].push(phrase); + byWord[firstWord].push(phrase) } else { - byWord[firstWord] = [phrase]; + byWord[firstWord] = [phrase] } } } diff --git a/package.json b/package.json index 22d2994..c8a3536 100644 --- a/package.json +++ b/package.json @@ -30,20 +30,20 @@ "browserify": "^16.0.0", "esmangle": "^1.0.0", "nyc": "^11.0.2", + "prettier": "^1.12.1", "remark-cli": "^5.0.0", "remark-preset-wooorm": "^4.0.0", "tape": "^4.0.0", "xo": "^0.20.0" }, "scripts": { - "build-md": "remark . --quiet --frail --output", + "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", "build-bundle": "browserify index.js --bare -s nlcstSearch > nlcst-search.js", "build-mangle": "esmangle nlcst-search.js > nlcst-search.min.js", - "build": "npm run build-md && npm run build-bundle && npm run build-mangle", - "lint": "xo", - "test-api": "node test.js", + "build": "npm run build-bundle && npm run build-mangle", + "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run build && npm run lint && npm run test-coverage" + "test": "npm run format && npm run build && npm run test-coverage" }, "nyc": { "check-coverage": true, @@ -51,14 +51,21 @@ "functions": 100, "branches": 100 }, + "prettier": { + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "bracketSpacing": false, + "semi": false, + "trailingComma": "none" + }, "xo": { - "space": true, + "prettier": true, "esnext": false, "rules": { "guard-for-in": "off", "no-var": "off", "prefer-arrow-callback": "off", - "guard-for-in": "off", "unicorn/prefer-type-error": "off" }, "ignore": [ diff --git a/readme.md b/readme.md index cd05608..6a0227a 100644 --- a/readme.md +++ b/readme.md @@ -13,8 +13,8 @@ npm install nlcst-search ## Usage ```js -var search = require('nlcst-search'); -var toString = require('nlcst-to-string'); +var search = require('nlcst-search') +var toString = require('nlcst-to-string') var tree = { type: 'SentenceNode', @@ -30,9 +30,7 @@ var tree = { {type: 'WhiteSpaceNode', value: ' '}, { type: 'WordNode', - children: [ - {type: 'TextNode', value: 'do'} - ] + children: [{type: 'TextNode', value: 'do'}] }, {type: 'WhiteSpaceNode', value: ' '}, { @@ -44,17 +42,16 @@ var tree = { ] } ] -}; - +} -search(tree, ['dont'], function (nodes) { - console.log(toString(nodes)); -}); +search(tree, ['dont'], function(nodes) { + console.log(toString(nodes)) +}) // Don’t -search(tree, ['do blocklevel'], function (nodes) { - console.log(toString(nodes)); -}); +search(tree, ['do blocklevel'], function(nodes) { + console.log(toString(nodes)) +}) // do Block-level ``` diff --git a/test.js b/test.js index 20b55bd..2e7a132 100644 --- a/test.js +++ b/test.js @@ -1,7 +1,7 @@ -'use strict'; +'use strict' -var test = require('tape'); -var search = require('.'); +var test = require('tape') +var search = require('.') var tree = { type: 'SentenceNode', @@ -77,306 +77,248 @@ var tree = { children: [{type: 'TextNode', value: 'selfservice'}] } ] -}; +} -test('search(tree, patterns, handle)', function (t) { - t.plan(68); +test('search(tree, patterns, handle)', function(t) { + t.plan(68) t.throws( - function () { - search(); + function() { + search() }, /Error: Expected node/, 'should throw when not given a tree' - ); + ) t.throws( - function () { - search(tree); + function() { + search(tree) }, /Error: Expected object for phrases/, 'should throw when not given phrases' - ); - - search(tree, ['Don’t'], function (nodes, index, parent, phrase) { - t.deepEqual(nodes, [tree.children[0]], 'should pass nodes'); - t.equal(index, 0, 'should pass the correct index'); - t.equal(parent, tree, 'should pass the parent'); - t.equal(phrase, 'Don’t', 'should pass the phrase'); - }); - - search(tree, ['Dont'], function (nodes, index, parent, phrase) { - var match = [tree.children[0]]; - t.deepEqual(nodes, match, 'should pass nodes (normalized)'); - t.equal(index, 0, 'should pass the correct index (normalized)'); - t.equal(parent, tree, 'should pass the parent (normalized)'); - t.equal(phrase, 'Dont', 'should pass the phrase'); - }); - - search(tree, {do: true}, function (nodes, index, parent, phrase) { - var match = [tree.children[2]]; - t.deepEqual(nodes, match, 'should pass nodes (object)'); - t.equal(index, 2, 'should pass the correct index (object)'); - t.equal(parent, tree, 'should pass the parent (object)'); - t.equal(phrase, 'do', 'should pass the phrase (object)'); - }); - - search(tree, ['blocklevel'], function (nodes, index, parent, phrase) { - var match = [tree.children[4]]; - t.deepEqual(nodes, match, 'should pass nodes (normalized 2)'); - t.equal(index, 4, 'should pass the correct index (normalized 2)'); - t.equal(parent, tree, 'should pass the parent (normalized 2)'); - t.equal(phrase, 'blocklevel', 'should pass the phrase'); - }); - - var position = -1; + ) + + search(tree, ['Don’t'], function(nodes, index, parent, phrase) { + t.deepEqual(nodes, [tree.children[0]], 'should pass nodes') + t.equal(index, 0, 'should pass the correct index') + t.equal(parent, tree, 'should pass the parent') + t.equal(phrase, 'Don’t', 'should pass the phrase') + }) + + search(tree, ['Dont'], function(nodes, index, parent, phrase) { + var match = [tree.children[0]] + t.deepEqual(nodes, match, 'should pass nodes (normalized)') + t.equal(index, 0, 'should pass the correct index (normalized)') + t.equal(parent, tree, 'should pass the parent (normalized)') + t.equal(phrase, 'Dont', 'should pass the phrase') + }) + + search(tree, {do: true}, function(nodes, index, parent, phrase) { + var match = [tree.children[2]] + t.deepEqual(nodes, match, 'should pass nodes (object)') + t.equal(index, 2, 'should pass the correct index (object)') + t.equal(parent, tree, 'should pass the parent (object)') + t.equal(phrase, 'do', 'should pass the phrase (object)') + }) + + search(tree, ['blocklevel'], function(nodes, index, parent, phrase) { + var match = [tree.children[4]] + t.deepEqual(nodes, match, 'should pass nodes (normalized 2)') + t.equal(index, 4, 'should pass the correct index (normalized 2)') + t.equal(parent, tree, 'should pass the parent (normalized 2)') + t.equal(phrase, 'blocklevel', 'should pass the phrase') + }) + + var position = -1 var results = [ [[tree.children[0]], 0, tree, 'dont'], [[tree.children[2]], 2, tree, 'do'] - ]; - - search(tree, ['dont', 'do'], function (nodes, index, parent, phrase) { - var match = results[++position]; - t.deepEqual(nodes, match[0], 'should pass nodes (phrases)'); - t.equal(index, match[1], 'should pass the correct index (phrases)'); - t.equal(parent, match[2], 'should pass the parent (phrases)'); - t.equal(phrase, match[3], 'should pass the phrase (phrases)'); - }); - - search(tree, ['dont do'], function (nodes, index, parent, phrase) { - var match = tree.children.slice(0, 3); - t.deepEqual(nodes, match, 'should pass nodes (phrase)'); - t.equal(index, 0, 'should pass the correct index (phrase)'); - t.equal(parent, tree, 'should pass the parent (phrase)'); - t.equal(phrase, 'dont do', 'should pass the phrase (phrase)'); - }); - - t.doesNotThrow( - function () { - search(tree, ['or that']); - }, - 'shouldn’t include non-word and non-white-space nodes' - ); - - var phrases = ['that or this', 'that']; + ] - position = -1; + search(tree, ['dont', 'do'], function(nodes, index, parent, phrase) { + var match = results[++position] + t.deepEqual(nodes, match[0], 'should pass nodes (phrases)') + t.equal(index, match[1], 'should pass the correct index (phrases)') + t.equal(parent, match[2], 'should pass the parent (phrases)') + t.equal(phrase, match[3], 'should pass the phrase (phrases)') + }) + + search(tree, ['dont do'], function(nodes, index, parent, phrase) { + var match = tree.children.slice(0, 3) + t.deepEqual(nodes, match, 'should pass nodes (phrase)') + t.equal(index, 0, 'should pass the correct index (phrase)') + t.equal(parent, tree, 'should pass the parent (phrase)') + t.equal(phrase, 'dont do', 'should pass the phrase (phrase)') + }) + + t.doesNotThrow(function() { + search(tree, ['or that']) + }, 'shouldn’t include non-word and non-white-space nodes') + + var phrases = ['that or this', 'that'] + + position = -1 results = [ [tree.children.slice(17, 22), 17, tree, phrases[0]], [[tree.children[17]], 17, tree, phrases[1]] - ]; + ] - search(tree, phrases, function (nodes, index, parent, phrase) { - var match = results[++position]; - t.deepEqual(nodes, match[0], 'should pass nodes (phrases)'); - t.equal(index, match[1], 'should pass the correct index (phrases)'); - t.equal(parent, match[2], 'should pass the parent (phrases)'); - t.equal(phrase, match[3], 'should pass the phrase (phrases)'); - }); + search(tree, phrases, function(nodes, index, parent, phrase) { + var match = results[++position] + t.deepEqual(nodes, match[0], 'should pass nodes (phrases)') + t.equal(index, match[1], 'should pass the correct index (phrases)') + t.equal(parent, match[2], 'should pass the parent (phrases)') + t.equal(phrase, match[3], 'should pass the phrase (phrases)') + }) /* Handler function is only invoked if match is found * search will throw if a match is found and no handler * is provided the tree contains “hell” but not “he’ll” * or “he'll”. */ - t.throws( - function () { - search(tree, ['hell'], null); - }, - 'should find non-apostrophe words when `allowApostrophes` is absent' - ); + t.throws(function() { + search(tree, ['hell'], null) + }, 'should find non-apostrophe words when `allowApostrophes` is absent') - t.throws( - function () { - search(tree, ['he’ll'], null); - }, - 'should find smart apostrophe words when `allowApostrophes` is absent' - ); + t.throws(function() { + search(tree, ['he’ll'], null) + }, 'should find smart apostrophe words when `allowApostrophes` is absent') - t.throws( - function () { - search(tree, ['he\'ll'], null); - }, - 'should find dumb apostrophe words when `allowApostrophes` is absent' - ); + t.throws(function() { + search(tree, ["he'll"], null) + }, 'should find dumb apostrophe words when `allowApostrophes` is absent') - t.throws( - function () { - search(tree, ['hell'], null, true); - }, - 'should find non-apostrophe words when `allowApostrophes` is true' - ); + t.throws(function() { + search(tree, ['hell'], null, true) + }, 'should find non-apostrophe words when `allowApostrophes` is true') - t.doesNotThrow( - function () { - search(tree, ['he’ll'], null, true); - }, - 'shouldn’t find smart apostrophe words when `allowApostrophes` is true' - ); + t.doesNotThrow(function() { + search(tree, ['he’ll'], null, true) + }, 'shouldn’t find smart apostrophe words when `allowApostrophes` is true') - t.doesNotThrow( - function () { - search(tree, ['he\'ll'], null, true); - }, - 'shouldn’t find dumb apostrophe words when `allowApostrophes` is true' - ); + t.doesNotThrow(function() { + search(tree, ["he'll"], null, true) + }, 'shouldn’t find dumb apostrophe words when `allowApostrophes` is true') - t.throws( - function () { - search(tree, ['hell'], null, false); - }, - 'should find non-apostrophe words when `allowApostrophes` is false' - ); + t.throws(function() { + search(tree, ['hell'], null, false) + }, 'should find non-apostrophe words when `allowApostrophes` is false') - t.throws( - function () { - search(tree, ['he’ll'], null, false); - }, - 'should find smart apostrophe words when `allowApostrophes` is false' - ); + t.throws(function() { + search(tree, ['he’ll'], null, false) + }, 'should find smart apostrophe words when `allowApostrophes` is false') - t.throws( - function () { - search(tree, ['he\'ll'], null, false); - }, - 'should find dumb apostrophe words when `allowApostrophes` is false' - ); + t.throws(function() { + search(tree, ["he'll"], null, false) + }, 'should find dumb apostrophe words when `allowApostrophes` is false') /* The tree contains “selfservice” but not “self-service” */ - t.throws( - function () { - search(tree, ['selfservice'], null); - }, - 'should find non-dash words when `allowDashes` is absent and `allowApostrophes` is absent' - ); - - t.throws( - function () { - search(tree, ['self-service'], null); - }, - 'should find dash words when `allowDashes` is absent and `allowApostrophes` is absent' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, false); - }, - 'should find non-dash words when `allowDashes` is absent and `allowApostrophes` is false' - ); - - t.throws( - function () { - search(tree, ['self-service'], null, false); - }, - 'should find dash words when `allowDashes` is absent and `allowApostrophes` is false' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, true); - }, - 'should find non-dash words when `allowDashes` is absent and `allowApostrophes` is true' - ); - - t.throws( - function () { - search(tree, ['self-service'], null, true); - }, - 'should find dash words when `allowDashes` is absent and `allowApostrophes` is true' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, {allowDashes: true}); - }, - 'should find non-dash words when `allowDashes` is true' - ); - - t.doesNotThrow( - function () { - search(tree, ['self-service'], null, {allowDashes: true}); - }, - 'shouldn’t find dash words when `allowDashes` is true' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, {allowDashes: false}); - }, - 'should find non-dash words when `allowDashes` is false' - ); - - t.throws( - function () { - search(tree, ['self-service'], null, {allowDashes: false}); - }, - 'should find dash words when `allowDashes` is false' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, {allowApostrophes: false, allowDashes: true}); - }, - 'should find non-dash words when `allowDashes` is true and `allowApostrophes` is false' - ); - - t.doesNotThrow( - function () { - search(tree, ['self-service'], null, {allowApostrophes: false, allowDashes: true}); - }, - 'shouldn’t find dash words when `allowDashes` is true and `allowApostrophes` is false' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, {allowApostrophes: false, allowDashes: false}); - }, - 'should find non-dash words when `allowDashes` is false and `allowApostrophes` is false' - ); - - t.throws( - function () { - search(tree, ['self-service'], null, {allowApostrophes: false, allowDashes: false}); - }, - 'should find dash words when `allowDashes` is false and `allowApostrophes` is false' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, {allowApostrophes: true, allowDashes: true}); - }, - 'should find non-dash words when `allowDashes` is true and `allowApostrophes` is true' - ); - - t.doesNotThrow( - function () { - search(tree, ['self-service'], null, {allowApostrophes: true, allowDashes: true}); - }, - 'shouldn’t find dash words when `allowDashes` is true and `allowApostrophes` is true' - ); - - t.throws( - function () { - search(tree, ['selfservice'], null, {allowApostrophes: true, allowDashes: false}); - }, - 'should find non-dash words when `allowDashes` is false and `allowApostrophes` is true' - ); - - t.throws( - function () { - search(tree, ['self-service'], null, {allowApostrophes: true, allowDashes: false}); - }, - 'should find dash words when `allowDashes` is false and `allowApostrophes` is true' - ); - - t.doesNotThrow( - function () { - search(tree, ['mellow']); - }, - 'shouldn’t find literals by default' - ); - - search(tree, ['mellow'], function () { - t.pass('should find literals when given `allowLiterals`'); - }, {allowLiterals: true}); -}); + t.throws(function() { + search(tree, ['selfservice'], null) + }, 'should find non-dash words when `allowDashes` is absent and `allowApostrophes` is absent') + + t.throws(function() { + search(tree, ['self-service'], null) + }, 'should find dash words when `allowDashes` is absent and `allowApostrophes` is absent') + + t.throws(function() { + search(tree, ['selfservice'], null, false) + }, 'should find non-dash words when `allowDashes` is absent and `allowApostrophes` is false') + + t.throws(function() { + search(tree, ['self-service'], null, false) + }, 'should find dash words when `allowDashes` is absent and `allowApostrophes` is false') + + t.throws(function() { + search(tree, ['selfservice'], null, true) + }, 'should find non-dash words when `allowDashes` is absent and `allowApostrophes` is true') + + t.throws(function() { + search(tree, ['self-service'], null, true) + }, 'should find dash words when `allowDashes` is absent and `allowApostrophes` is true') + + t.throws(function() { + search(tree, ['selfservice'], null, {allowDashes: true}) + }, 'should find non-dash words when `allowDashes` is true') + + t.doesNotThrow(function() { + search(tree, ['self-service'], null, {allowDashes: true}) + }, 'shouldn’t find dash words when `allowDashes` is true') + + t.throws(function() { + search(tree, ['selfservice'], null, {allowDashes: false}) + }, 'should find non-dash words when `allowDashes` is false') + + t.throws(function() { + search(tree, ['self-service'], null, {allowDashes: false}) + }, 'should find dash words when `allowDashes` is false') + + t.throws(function() { + search(tree, ['selfservice'], null, { + allowApostrophes: false, + allowDashes: true + }) + }, 'should find non-dash words when `allowDashes` is true and `allowApostrophes` is false') + + t.doesNotThrow(function() { + search(tree, ['self-service'], null, { + allowApostrophes: false, + allowDashes: true + }) + }, 'shouldn’t find dash words when `allowDashes` is true and `allowApostrophes` is false') + + t.throws(function() { + search(tree, ['selfservice'], null, { + allowApostrophes: false, + allowDashes: false + }) + }, 'should find non-dash words when `allowDashes` is false and `allowApostrophes` is false') + + t.throws(function() { + search(tree, ['self-service'], null, { + allowApostrophes: false, + allowDashes: false + }) + }, 'should find dash words when `allowDashes` is false and `allowApostrophes` is false') + + t.throws(function() { + search(tree, ['selfservice'], null, { + allowApostrophes: true, + allowDashes: true + }) + }, 'should find non-dash words when `allowDashes` is true and `allowApostrophes` is true') + + t.doesNotThrow(function() { + search(tree, ['self-service'], null, { + allowApostrophes: true, + allowDashes: true + }) + }, 'shouldn’t find dash words when `allowDashes` is true and `allowApostrophes` is true') + + t.throws(function() { + search(tree, ['selfservice'], null, { + allowApostrophes: true, + allowDashes: false + }) + }, 'should find non-dash words when `allowDashes` is false and `allowApostrophes` is true') + + t.throws(function() { + search(tree, ['self-service'], null, { + allowApostrophes: true, + allowDashes: false + }) + }, 'should find dash words when `allowDashes` is false and `allowApostrophes` is true') + + t.doesNotThrow(function() { + search(tree, ['mellow']) + }, 'shouldn’t find literals by default') + + search( + tree, + ['mellow'], + function() { + t.pass('should find literals when given `allowLiterals`') + }, + {allowLiterals: true} + ) +})